home *** CD-ROM | disk | FTP | other *** search
/ Microsoft Programmer's Library 1.3 / Microsoft_Programmers_Library.7z / MPL / basic / bassam.txt < prev    next >
Encoding:
Text File  |  2013-11-08  |  1.6 MB  |  46,339 lines

Text Truncated. Only the first 1MB is shown below. Download the file for the complete contents.
  1.  Microsoft BASIC (Professional Development System) Sample Code
  2.  
  3.  
  4.  BALLPSET.BAS
  5.  CD-ROM Disc Path:   \SAMPCODE\BASIC\BALLPSET.BAS
  6.  
  7.  DECLARE FUNCTION GetArraySize (WLeft, WRight, WTop, WBottom)
  8.  
  9.  SCREEN 2
  10.  
  11.  ' Define a viewport and draw a border around it:
  12.  VIEW (20, 10)-(620, 190),,1
  13.  
  14.  CONST PI = 3.141592653589#
  15.  
  16.  ' Redefine the coordinates of the viewport with view
  17.  ' coordinates:
  18.  WINDOW (-3.15, -.14)-(3.56, 1.01)
  19.  
  20.  ' Arrays in program are now dynamic:
  21.  ' $DYNAMIC
  22.  
  23.  ' Calculate the view coordinates for the top and bottom of a
  24.  ' rectangle large enough to hold the image that will be
  25.  ' drawn with CIRCLE and PAINT:
  26.  WLeft = -.21
  27.  WRight = .21
  28.  WTop = .07
  29.  WBottom = -.07
  30.  
  31.  ' Call the GetArraySize function,
  32.  ' passing it the rectangle's view coordinates:
  33.  ArraySize% = GetArraySize(WLeft, WRight, WTop, WBottom)
  34.  
  35.  DIM Array (1 TO ArraySize%) AS INTEGER
  36.  
  37.  ' Draw and paint the circle:
  38.  CIRCLE (0, 0), .18
  39.  PAINT (0, 0)
  40.  
  41.  ' Store the rectangle in Array:
  42.  GET (WLeft, WTop)-(WRight, WBottom), Array
  43.  CLS
  44.  ' Draw a box and fill it with a pattern:
  45.  LINE (-3, .8)-(3.4, .2), , B
  46.  Pattern$ = CHR$(126) + CHR$(0) + CHR$(126) + CHR$(126)
  47.  PAINT (0, .5), Pattern$
  48.  
  49.  LOCATE 21, 29
  50.  PRINT "Press any key to end."
  51.  
  52.  ' Initialize loop variables:
  53.  StepSize = .02
  54.  StartLoop = -PI
  55.  Decay = 1
  56.  
  57.  DO
  58.     EndLoop = -StartLoop
  59.     FOR X = StartLoop TO EndLoop STEP StepSize
  60.  
  61.        ' Each time the ball "bounces" (hits the bottom of the
  62.        ' viewport), the Decay variable gets smaller, making
  63.        ' the height of the next bounce smaller:
  64.        Y = ABS(COS(X)) * Decay - .14
  65.        IF Y < -.13 THEN Decay = Decay * .9
  66.  
  67.        ' Stop if key pressed or Decay less than .01:
  68.        Esc$ = INKEY$
  69.        IF Esc$ <> "" OR Decay < .01 THEN EXIT FOR
  70.  
  71.        ' Put the image on the screen. The StepSize offset is
  72.        ' smaller than the border around the circle. Thus,
  73.        ' each time the image moves, it erases any traces
  74.        ' left from the previous PUT (and also erases anything
  75.        ' else on the screen):
  76.        PUT (X, Y), Array, PSET
  77.     NEXT X
  78.  
  79.     ' Reverse direction:
  80.     StepSize = -StepSize
  81.     StartLoop = -StartLoop
  82.  LOOP UNTIL Esc$ <> "" OR Decay < .01
  83.  
  84.  END
  85.  
  86.  FUNCTION GetArraySize (WLeft, WRight, WTop, WBottom) STATIC
  87.  
  88.     ' Map the view coordinates passed to this function to
  89.     ' their physical-coordinate equivalents:
  90.     VLeft = PMAP(WLeft, 0)
  91.     VRight = PMAP(WRight, 0)
  92.     VTop = PMAP(WTop, 1)
  93.     VBottom = PMAP(WBottom, 1)
  94.  ' Calculate the height and width in pixels
  95.     ' of the enclosing rectangle:
  96.     RectHeight = ABS(VBottom - VTop) + 1
  97.     RectWidth = ABS(VRight - VLeft) + 1
  98.  
  99.     ' Calculate size in bytes of array:
  100.     ByteSize = 4 + RectHeight * INT((RectWidth + 7) / 8)
  101.  
  102.     ' Array is integer, so divide bytes by two:
  103.     GetArraySize = ByteSize \ 2 + 1
  104.  END FUNCTION
  105.  
  106.  
  107.  
  108.  BALLXOR.BAS
  109.  CD-ROM Disc Path:   \SAMPCODE\BASIC\BALLXOR.BAS
  110.  
  111.  DECLARE FUNCTION GetArraySize (WLeft, WRight, WTop, WBottom)
  112.  
  113.  SCREEN 2
  114.  
  115.  ' Define a viewport and draw a border around it:
  116.  VIEW (20, 10)-(620, 190), , 1
  117.  
  118.  CONST PI = 3.141592653589#
  119.  
  120.  ' Redefine the coordinates of the viewport with view
  121.  ' coordinates:
  122.  WINDOW (-3.15, -.14)-(3.56, 1.01)
  123.  
  124.  ' Arrays in program are now dynamic:
  125.  ' $DYNAMIC
  126.  
  127.  ' Calculate the view coordinates for the top and bottom of a
  128.  ' rectangle large enough to hold the image that will be
  129.  ' drawn with CIRCLE and PAINT:
  130.  WLeft = -.18
  131.  WRight = .18
  132.  WTop = .05
  133.  WBottom = -.05
  134.  
  135.  ' Call the GetArraySize function,
  136.  ' passing it the rectangle's view coordinates:
  137.  ArraySize% = GetArraySize(WLeft, WRight, WTop, WBottom)
  138.  
  139.  DIM Array(1 TO ArraySize%)  AS INTEGER
  140.  
  141.  ' Draw and paint the circle:
  142.  CIRCLE (0, 0), .18
  143.  PAINT (0, 0)
  144.  
  145.  ' Store the rectangle in Array:
  146.  GET (WLeft, WTop)-(WRight, WBottom), Array
  147.  CLS
  148.  ' Draw a box and fill it with a pattern:
  149.  LINE (-3, .8)-(3.4, .2), , B
  150.  Pattern$ = CHR$(126) + CHR$(0) + CHR$(126) + CHR$(126)
  151.  PAINT (0, .5), Pattern$
  152.  
  153.  LOCATE 21, 29
  154.  PRINT "Press any key to end."
  155.  
  156.  ' Initialize loop variables:
  157.  StepSize = .02
  158.  StartLoop = -PI
  159.  Decay = 1
  160.  
  161.  DO
  162.     EndLoop = -StartLoop
  163.     FOR X = StartLoop TO EndLoop STEP StepSize
  164.        Y = ABS(COS(X)) * Decay - .14
  165.  
  166.        ' The first PUT statement places the image
  167.        ' on the screen:
  168.        PUT (X, Y), Array, XOR
  169.  
  170.        ' Use an empty FOR...NEXT loop to delay
  171.        ' the program and reduce image flicker:
  172.        FOR I = 1 TO 5: NEXT I
  173.  
  174.        IF Y < -.13 THEN Decay = Decay * .9
  175.        Esc$ = INKEY$
  176.        IF Esc$ <> "" OR Decay < .01 THEN EXIT FOR
  177.  
  178.        ' The second PUT statement erases the image and
  179.        ' restores the background:
  180.        PUT (X, Y), Array, XOR
  181.     NEXT X
  182.  
  183.     StepSize = -StepSize
  184.     StartLoop = -StartLoop
  185.  LOOP UNTIL Esc$ <> "" OR Decay < .01
  186.  
  187.  END
  188.  '  .
  189.  '  .
  190.  '  .
  191.  
  192.  FUNCTION GetArraySize (WLeft, WRight, WTop, WBottom) STATIC
  193.  
  194.     ' Map the view coordinates passed to this function to
  195.     ' their physical-coordinate equivalents:
  196.     VLeft = PMAP(WLeft, 0)
  197.     VRight = PMAP(WRight, 0)
  198.     VTop = PMAP(WTop, 1)
  199.     VBottom = PMAP(WBottom, 1)
  200.  ' Calculate the height and width in pixels
  201.     ' of the enclosing rectangle:
  202.     RectHeight = ABS(VBottom - VTop) + 1
  203.     RectWidth = ABS(VRight - VLeft) + 1
  204.  
  205.     ' Calculate size in bytes of array:
  206.     ByteSize = 4 + RectHeight * INT((RectWidth + 7) / 8)
  207.  
  208.     ' Array is integer, so divide bytes by two:
  209.     GetArraySize = ByteSize \ 2 + 1
  210.  END FUNCTION
  211.  
  212.  
  213.  
  214.  BAR.BAS
  215.  CD-ROM Disc Path:   \SAMPCODE\BASIC\BAR.BAS
  216.  
  217.  ' Define type for the titles:
  218.  TYPE TitleType
  219.     MainTitle AS STRING * 40
  220.     XTitle AS STRING * 40
  221.     YTitle AS STRING * 18
  222.  END TYPE
  223.  
  224.  DECLARE SUB InputTitles (T AS TitleType)
  225.  DECLARE FUNCTION DrawGraph$ (T AS TitleType, Label$(), Value!(), N%)
  226.  DECLARE FUNCTION InputData% (Label$(), Value!())
  227.  
  228.  ' Variable declarations for titles and bar data:
  229.  DIM Titles AS TitleType, Label$(1 TO 5), Value(1 TO 5)
  230.  
  231.  CONST FALSE = 0, TRUE = NOT FALSE
  232.  
  233.  DO
  234.     InputTitles Titles
  235.     N% = InputData%(Label$(), Value())
  236.     IF N% <> FALSE THEN
  237.        NewGraph$ = DrawGraph$(Titles, Label$(), Value(), N%)
  238.     END IF
  239.  LOOP WHILE NewGraph$ = "Y"
  240.  
  241.  END
  242.  
  243.  ' ======================== DRAWGRAPH ======================
  244.  '   Draws a bar graph from the data entered in the
  245.  '   INPUTTITLES and INPUTDATA procedures.
  246.  ' =========================================================
  247.  
  248.  FUNCTION DrawGraph$ (T AS TitleType, Label$(), Value(), N%) STATIC
  249.  
  250.     ' Set size of graph:
  251.     CONST GRAPHTOP = 24, GRAPHBOTTOM = 171
  252.     CONST GRAPHLEFT = 48, GRAPHRIGHT = 624
  253.     CONST YLENGTH = GRAPHBOTTOM - GRAPHTOP
  254.  
  255.     ' Calculate maximum and minimum values:
  256.     YMax = 0
  257.     YMin = 0
  258.     FOR I% = 1 TO N%
  259.        IF Value(I%) < YMin THEN YMin = Value(I%)
  260.        IF Value(I%) > YMax THEN YMax = Value(I%)
  261.     NEXT I%
  262.  
  263.     ' Calculate width of bars and space between them:
  264.     BarWidth = (GRAPHRIGHT - GRAPHLEFT) / N%
  265.     BarSpace = .2 * BarWidth
  266.     BarWidth = BarWidth - BarSpace
  267.  
  268.     SCREEN 2
  269.     CLS
  270.  
  271.     ' Draw y-axis:
  272.     LINE (GRAPHLEFT, GRAPHTOP)-(GRAPHLEFT, GRAPHBOTTOM), 1
  273.  
  274.     ' Draw main graph title:
  275.     Start% = 44 - (LEN(RTRIM$(T.MainTitle)) / 2)
  276.     LOCATE 2, Start%
  277.     PRINT RTRIM$(T.MainTitle);
  278.  
  279.     ' Annotate y-axis:
  280.     Start% = CINT(13 - LEN(RTRIM$(T.YTitle)) / 2)
  281.     FOR I% = 1 TO LEN(RTRIM$(T.YTitle))
  282.        LOCATE Start% + I% - 1, 1
  283.        PRINT MID$(T.YTitle, I%, 1);
  284.     NEXT I%
  285.  
  286.     ' Calculate scale factor so labels aren't bigger than four digits:
  287.     IF ABS(YMax) > ABS(YMin) THEN
  288.        Power = YMax
  289.     ELSE
  290.        Power = YMin
  291.     END IF
  292.     Power = CINT(LOG(ABS(Power) / 100) / LOG(10))
  293.     IF Power < 0 THEN Power = 0
  294.  
  295.     ' Scale minimum and maximum values down:
  296.     ScaleFactor = 10 ^ Power
  297.     YMax = CINT(YMax / ScaleFactor)
  298.     YMin = CINT(YMin / ScaleFactor)
  299.     ' If power isn't zero then put scale factor on chart:
  300.     IF Power <> 0 THEN
  301.        LOCATE 3, 2
  302.        PRINT "x 10^"; LTRIM$(STR$(Power))
  303.     END IF
  304.  
  305.     ' Put tic mark and number for Max point on y-axis:
  306.     LINE (GRAPHLEFT - 3, GRAPHTOP) -STEP(3, 0)
  307.     LOCATE 4, 2
  308.     PRINT USING "####"; YMax
  309.  
  310.     ' Put tic mark and number for Min point on y-axis:
  311.     LINE (GRAPHLEFT - 3, GRAPHBOTTOM) -STEP(3, 0)
  312.     LOCATE 22, 2
  313.     PRINT USING "####"; YMin
  314.  
  315.     YMax = YMax * ScaleFactor ' Scale minimum and maximum back
  316.     YMin = YMin * ScaleFactor ' up for charting calculations.
  317.  
  318.     ' Annotate x-axis:
  319.     Start% = 44 - (LEN(RTRIM$(T.XTitle)) / 2)
  320.     LOCATE 25, Start%
  321.     PRINT RTRIM$(T.XTitle);
  322.  
  323.     ' Calculate the pixel range for the y-axis:
  324.     YRange = YMax - YMin
  325.  
  326.     ' Define a diagonally striped pattern:
  327.     Tile$ = CHR$(1)+CHR$(2)+CHR$(4)+CHR$(8)+CHR$(16)+CHR$(32)+CHR$(64)+CHR$(12
  328.  
  329.     ' Draw a zero line if appropriate:
  330.     IF YMin < 0 THEN
  331.        Bottom = GRAPHBOTTOM - ((-YMin) / YRange * YLENGTH)
  332.        LOCATE INT((Bottom - 1) / 8) + 1, 5
  333.        PRINT "0";
  334.     ELSE
  335.        Bottom = GRAPHBOTTOM
  336.     END IF
  337.  
  338.     ' Draw x-axis:
  339.     LINE (GRAPHLEFT - 3, Bottom)-(GRAPHRIGHT, Bottom)
  340.     ' Draw bars and labels:
  341.     Start% = GRAPHLEFT + (BarSpace / 2)
  342.     FOR I% = 1 TO N%
  343.  
  344.        ' Draw a bar label:
  345.        BarMid = Start% + (BarWidth / 2)
  346.        CharMid = INT((BarMid - 1) / 8) + 1
  347.        LOCATE 23, CharMid - INT(LEN(RTRIM$(Label$(I%))) / 2)
  348.        PRINT Label$(I%);
  349.  
  350.        ' Draw the bar and fill it with the striped pattern:
  351.        BarHeight = (Value(I%) / YRange) * YLENGTH
  352.        LINE (Start%, Bottom) -STEP(BarWidth, -BarHeight), , B
  353.        PAINT (BarMid, Bottom - (BarHeight / 2)), Tile$, 1
  354.  
  355.        Start% = Start% + BarWidth + BarSpace
  356.     NEXT I%
  357.     LOCATE 1, 1
  358.     PRINT "New graph? ";
  359.     DrawGraph$ = UCASE$(INPUT$(1))
  360.  
  361.  END FUNCTION
  362.  ' ======================== INPUTDATA ======================
  363.  '     Gets input for the bar labels and their values
  364.  ' =========================================================
  365.  
  366.  FUNCTION InputData% (Label$(), Value()) STATIC
  367.  
  368.     ' Initialize the number of data values:
  369.     NumData% = 0
  370.  
  371.     ' Print data-entry instructions:
  372.     CLS
  373.     PRINT "Enter data for up to 5 bars:"
  374.     PRINT "   * Enter the label and value for each bar."
  375.     PRINT "   * Values can be negative."
  376.     PRINT "   * Enter a blank label to stop."
  377.     PRINT
  378.     PRINT "After viewing the graph, press any key ";
  379.     PRINT "to end the program."
  380.  
  381.     ' Accept data until blank label or 5 entries:
  382.     Done% = FALSE
  383.     DO
  384.        NumData% = NumData% + 1
  385.        PRINT
  386.        PRINT "Bar("; LTRIM$(STR$(NumData%)); "):"
  387.        INPUT ; "        Label? ", Label$(NumData%)
  388.  
  389.        ' Only input value if label isn't blank:
  390.        IF Label$(NumData%) <> "" THEN
  391.           LOCATE , 35
  392.           INPUT "Value? ", Value(NumData%)
  393.  
  394.        ' If label is blank, decrement data counter
  395.        ' and set Done flag equal to TRUE:
  396.        ELSE
  397.           NumData% = NumData% - 1
  398.           Done% = TRUE
  399.        END IF
  400.     LOOP UNTIL (NumData% = 5) OR Done%
  401.  
  402.     ' Return the number of data values input:
  403.     InputData% = NumData%
  404.  
  405.  END FUNCTION
  406.  ' ====================== INPUTTITLES ======================
  407.  '     Accepts input for the three different graph titles
  408.  ' =========================================================
  409.  
  410.  SUB InputTitles (T AS TitleType) STATIC
  411.     SCREEN 0, 0                ' Set text screen.
  412.     DO                        ' Input titles.
  413.        CLS
  414.        INPUT "Enter main graph title: ", T.MainTitle
  415.        INPUT "Enter x-axis title    : ", T.XTitle
  416.        INPUT "Enter y-axis title    : ", T.YTitle
  417.  
  418.        ' Check to see if titles are OK:
  419.        LOCATE 7, 1
  420.        PRINT "OK (Y to continue, N to change)? ";
  421.        LOCATE , , 1
  422.        OK$ = UCASE$(INPUT$(1))
  423.     LOOP UNTIL OK$ = "Y"
  424.  END SUB
  425.  
  426.  
  427.  
  428.  BIGSTRIN.BAS
  429.  CD-ROM Disc Path:   \SAMPCODE\BASIC\BIGSTRIN.BAS
  430.  
  431.  'Define arrays which will be passed to each new level
  432.  '       of recursion.
  433.  DECLARE SUB BigStrings (n%, s1$(), s2$(), s3$(), s4$())
  434.  DEFINT A-Z
  435.  DIM s1$(1 TO 2), s2$(1 TO 2), s3$(1 TO 2), s4$(1 TO 2)
  436.  ' Compute the # of 64K blocks available in far memory.
  437.  n = FRE(-1) \ 65536
  438.  CLS
  439.  'Quit if not enough memory.
  440.  IF n < 1 THEN
  441.               PRINT "Not enough memory for operation."
  442.               END
  443.  END IF
  444.  
  445.  ' Start the recursion.
  446.  CALL BigStrings(n, s1$(), s2$(), s3$(), s4$())
  447.  
  448.  SUB BigStrings (n, s1$(), s2$(), s3$(), s4$())
  449.  ' Create a new array (up to 64K) for each level of recursion.
  450.  DIM a$(1 TO 2)
  451.  ' Have n keep track of recursion level.
  452.  SELECT CASE n
  453.  ' When at highest recusion level, process the strings.
  454.          CASE 0
  455.                  PRINT s1$(1); s1$(2); s2$(1); s2$(2); s3$(1); s3$(2); s4$(1);
  456.          CASE 1
  457.                  a$(1) = "Each "
  458.                  a$(2) = "word "
  459.                  s1$(1) = a$(1)
  460.                  s1$(2) = a$(2)
  461.          CASE 2
  462.                  a$(1) = "pair "
  463.                  a$(2) = "comes "
  464.                  s2$(1) = a$(1)
  465.                  s2$(2) = a$(2)
  466.          CASE 3
  467.                  a$(1) = "from "
  468.                  a$(2) = "separate "
  469.                  s3$(1) = a$(1)
  470.                  s3$(2) = a$(2)
  471.          CASE 4
  472.                  a$(1) = "recursive "
  473.                  a$(2) = "procedures."
  474.                  s4$(1) = a$(1)
  475.                  s4$(2) = a$(2)
  476.  END SELECT
  477.  
  478.  ' Keep going until we're out of memory.
  479.  IF n > 0 THEN
  480.                  n = n - 1
  481.  ' For each recursion, pass in previously created arrays.
  482.                  CALL BigStrings(n, s1$(), s2$(), s3$(), s4$())
  483.  END IF
  484.  
  485.  END SUB
  486.  
  487.  
  488.  
  489.  
  490.  BOOKLOOK.BAS
  491.  CD-ROM Disc Path:   \SAMPCODE\BASIC\BOOKLOOK.BAS
  492.  
  493.  '****************************** Main  Module *******************************
  494.  '* This window contains the module-level code of BOOKLOOK.BAS, a program   *
  495.  '* used to manage the database of a hypothethical library (BOOKS.MDB). The *
  496.  '* program requires the following additional modules: BOOKMOD1.BAS,        *
  497.  '* BOOKMOD2.BAS, and BOOKMOD3.BAS, all named in the file BOOKLOOK.MAK. The *
  498.  '* include file BOOKLOOK.BI and the database file BOOKS.MDB must also be   *
  499.  '* accessible. The program is discussed in Chapter 10, Database Programming*
  500.  '* with ISAM in the BASIC 7.0 Programmer's Guide.                          *
  501.  '*                                                                         *
  502.  '* If you do NOT have expanded memory available, you should have invoked   *
  503.  '* the PROISAM.EXE TSR as PROISAM /Ib:n, where n can be between 10-20.     *
  504.  '* The /Ib: option specifies the number of buffers ISAM needs. Higher n    *
  505.  '* values improve performance. Too few buffers, and the program will fail  *
  506.  '* with an "Out of Memory" error. However if /Ib: is set too high, there   *
  507.  '* may not be enough memory to load and run the program. If you do HAVE    *
  508.  '* expanded memory, ISAM automatically uses up to 1.2 megabytes, even if   *
  509.  '* you set Ib: to a low value. With a program the size of BOOKLOOK, use the*
  510.  '* /Ie: option to reserve some expanded memory for QBX. This indirectly    *
  511.  '* limits the amount of expanded memory ISAM uses, but make sure ISAM gets *
  512.  '* enough EMS for at least 15 buffers (each buffer = 2K). As a last resort,*
  513.  '* you can start QBX with the /NOF switch to make more memory available.   *
  514.  '*                                                                         *
  515.  '* BOOKLOOK manages 3 tables, BookStock, CardHolders, and BooksOut. The    *
  516.  '* data in the BookStock and CardHolders tables is displayed as forms on   *
  517.  '* screen. The user can switch between table displays by pressing "V" (for *
  518.  '* View Other Table). Each table is defined as a separate structure. The   *
  519.  '* structure for BookStock is Books, for CardHolders it is Borrowers, and  *
  520.  '* for BooksOut it is BookStatus. Each of these is incorporated as an      *
  521.  '* element of the structure RecStruct. RecStruct also has an element of    *
  522.  '* INTEGER type called TableNum (to keep track of which table is being     *
  523.  '* displayed), and a STRING element called WhichIndex that holds the name  *
  524.  '* of the index by which the user chooses to order presentation of records.*
  525.  '* Press F2 to see a list of procedures called by the program.             *
  526.  '***************************************************************************
  527.  
  528.  DEFINT A-Z
  529.  '$INCLUDE: 'BOOKLOOK.BI'
  530.  SCREEN 0
  531.  CLS                         ' TempRec is for editing and adding records
  532.  DIM TempRec AS RecStruct    ' Used only to blank out a TempRec
  533.  DIM EmptyRec AS RecStruct   ' See BOOKLOOK.BI for declaration of
  534.  DIM BigRec AS RecStruct     ' this structure and its elements
  535.  DIM Marker(25) AS INTEGER   ' Array to hold SAVEPOINT returns
  536.  
  537.  ' Open the database and the BookStock, CardHolders, and BooksOut tables
  538.  
  539.  ON ERROR GOTO MainHandler
  540.  OPEN "BOOKS.MDB" FOR ISAM Books "BookStock" AS cBookStockTableNum
  541.  OPEN "BOOKS.MDB" FOR ISAM Borrowers "CardHolders" AS cCardHoldersTableNum
  542.  OPEN "BOOKS.MDB" FOR ISAM BookStatus "BooksOut" AS cBooksOutTableNum
  543.  ON ERROR GOTO 0
  544.  
  545.  BigRec.TableNum = cBookStockTableNum   ' Decide which table to show first
  546.  
  547.     ' Since the database has multiple tables, this outer DO loop is used to
  548.     ' reset the number associated with the table the user wants to
  549.     ' to access, then draw the screen appropriate to that table, etc.
  550.  DO
  551.    EraseMessage                          ' Show the interface
  552.    CALL DrawScreen(BigRec.TableNum)
  553.    Checked = CheckIndex%(BigRec, TRUE)   ' Show current index
  554.    CALL Retriever(BigRec, DimN, DimP, Answer) ' Retrieve and show a record
  555.    CALL ShowMessage(" Press V to View other table", 0)
  556.    CALL ShowStatus(" Total records in table: ", CDBL(LOF(BigRec.TableNum)))
  557.  
  558.    ' This loop lets the user traverse BigRec.TableNum and insert, delete,
  559.    ' or modify records.
  560.    DO                                           ' At start of each loop, show
  561.                                                 ' the user valid operations
  562.      CALL Retriever(BigRec, DimN, DimP, Answer) ' and display current record
  563.  
  564.      STACK 4000                          ' Set large stack for recursions-it
  565.                                          ' also resets FRE(-2) to stack 4000.
  566.  
  567.      Answer% = GetInput%(BigRec)         ' Find out what the user wants to do
  568.  
  569.      IF Answer < UNDO THEN               ' Excludes UNDOALL & INVALIDKEY too
  570.        CALL EditCheck(PendingFlag, Answer, BigRec)
  571.      END IF
  572.  
  573.      SELECT CASE Answer         ' Process valid user requests
  574.        CASE QUIT
  575.          CALL ShowMessage(" You chose Quit. So long! ", 0)
  576.          END
  577.  
  578.                                 ' If user picks "N" (Next Record), MOVENEXT.
  579.                                 ' CheckPosition handles end-of-file (i.e. the
  580.        CASE GOAHEAD, ENDK       ' position just past the last record). If EOF
  581.                                 ' or BOF = TRUE, CheckPosition holds position
  582.          MOVENEXT BigRec.TableNum
  583.          CALL CheckPosition(BigRec, Answer, DimN, DimP)
  584.  
  585.                                 ' Same logic as GOAHEAD, but reversed
  586.        CASE GOBACK, HOME
  587.  
  588.          MOVEPREVIOUS BigRec.TableNum
  589.          CALL CheckPosition(BigRec, Answer, DimN, DimP)
  590.  
  591.                                 ' If user chooses "E", let him edit a field.
  592.                                 ' Assign the value returned by SAVEPOINT to
  593.                                 ' an array element, then update the table and
  594.                                 ' show the changed field. Trap any "duplicate
  595.        CASE EDITRECORD          ' value for unique index" (error 86) and
  596.                                 ' handle it. The value returned by SAVEPOINT
  597.                                 ' allows rollbacks so the user can undo edits
  598.  
  599.          IF LOF(BigRec.TableNum) THEN
  600.            IF EditField(Argument%, BigRec, Letter$, EDITRECORD, Answer%) THEN
  601.  
  602.                ' You save a sequence of savepoint identifiers in an array so
  603.                ' you can let the user roll the state of the file back to a
  604.                ' specific point. The returns from SAVEPOINT aren't guaranteed
  605.                ' to be sequential.
  606.              n = n + 1              ' Increment counter first so savepoint
  607.              Marker(n) = SAVEPOINT  ' is synced with array-element subscript
  608.  
  609.              Alert$ = "Setting Savepoint number " + STR$(Marker(n))
  610.              CALL ShowMessage(Alert$, 0)
  611.              ON ERROR GOTO MainHandler
  612.              SELECT CASE BigRec.TableNum   ' Update the table being displayed
  613.                CASE cBookStockTableNum
  614.                  UPDATE BigRec.TableNum, BigRec.Inventory
  615.                CASE cCardHoldersTableNum
  616.                  UPDATE BigRec.TableNum, BigRec.Lendee
  617.              END SELECT
  618.              ON ERROR GOTO 0
  619.            ELSE
  620.              COMMITTRANS               ' Use COMMITTRANS abort transaction if
  621.              PendingFlag = FALSE       ' the user presses ESC
  622.              n = 0                     ' Reset array counter
  623.            END IF
  624.          ELSE
  625.            CALL ShowMessage("Sorry, no records in this table to edit", 0): SLE
  626.          END IF
  627.                            ' If choice is "A", get the values the user wants
  628.                            ' in each of the fields (with AddOne). If there
  629.                            ' is no ESCAPE from the edit, INSERT the record.
  630.                            ' Trap "Duplicate value for unique index" errors
  631.                            ' and handle them in MainHandler (error 86).
  632.        CASE ADDRECORD
  633.          added = AddOne(BigRec, EmptyRec, TempRec, Answer%)
  634.          IF added THEN
  635.            Alert$ = "A new record assumes proper place in current index"
  636.            CALL ShowMessage(Alert$, 0)
  637.            ON ERROR GOTO MainHandler
  638.            SELECT CASE BigRec.TableNum     ' Insert into table being shown
  639.              CASE cBookStockTableNum
  640.                INSERT BigRec.TableNum, TempRec.Inventory
  641.              CASE cCardHoldersTableNum
  642.                INSERT BigRec.TableNum, TempRec.Lendee
  643.            END SELECT
  644.            ON ERROR GOTO 0
  645.          END IF
  646.          TempRec = EmptyRec
  647.  
  648.                                ' If choice is "D" --- prompt for confirmation.
  649.                                ' If so, delete it and show new current record.
  650.        CASE TOSSRECORD
  651.          AnyRecords = LOF(BigRec.TableNum)
  652.          IF BigRec.TableNum = cBookStockTableNum THEN CheckedOut = GetStatus(B
  653.          IF BigRec.TableNum = cCardHoldersTableNum THEN
  654.            SETINDEX cBooksOutTableNum, "CardNumIndexBO"
  655.            SEEKEQ cBooksOutTableNum, BigRec.Lendee.CardNum
  656.            IF NOT EOF(cBooksOutTableNum) THEN CheckedOut = TRUE
  657.          END IF
  658.          IF AnyRecords AND CheckedOut = FALSE THEN
  659.            Alert$ = "Press D again to Delete this record, ESC to escape"
  660.            CALL ShowMessage(Alert$, 0)
  661.            DeleteIt% = GetInput%(BigRec)
  662.            IF DeleteIt% = TOSSRECORD THEN   ' Delete currently-displayed recor
  663.              DELETE BigRec.TableNum
  664.              CALL ShowMessage("Record deleted...Press a key to continue", 0)
  665.            ELSE
  666.              CALL ShowMessage("Record not deleted. Press a key to continue", 0
  667.              CALL ShowRecord(BigRec)
  668.            END IF
  669.            ' The following code checks whether the record deleted was the last
  670.            ' record in the index, then makes the new last record current
  671.            IF EOF(BigRec.TableNum) THEN
  672.              MOVELAST BigRec.TableNum
  673.            END IF
  674.          ELSE
  675.            IF BigRec.TableNum = cBookStockTableNum THEN
  676.              IF AnyRecords = 0 THEN Alert$ = "Sorry, no records in this table
  677.              IF CheckedOut THEN Alert$ = "Can't delete --- this book currently
  678.            ELSEIF BigRec.TableNum = cCardHoldersTableNum THEN
  679.              IF AnyRecords = 0 THEN Alert$ = "Sorry, no records in this table
  680.              IF CheckedOut THEN Alert$ = "Can't delete --- this cardholder sti
  681.            END IF
  682.            CALL ShowMessage(Alert$, 0): SLEEP
  683.          END IF
  684.          CheckedOut = FALSE
  685.  
  686.                                 ' If user chooses "R", walk the fields so he
  687.                                 ' can choose new index to order presentation
  688.        CASE REORDER
  689.          Letter$ = CHR$(TABKEY)
  690.          GotOne = ChooseOrder(BigRec, EmptyRec, TempRec, Letter$, REORDER)
  691.  
  692.                                 ' If a choice of indexes was made, retrieve
  693.                                 ' the index name, set an error trap, and try
  694.                                 ' to set the index, then display new index.
  695.          IF GotOne THEN
  696.            IndexName$ = LTRIM$(RTRIM$(TempRec.WhichIndex))
  697.            ON ERROR GOTO MainHandler
  698.            IF IndexName$ <> "NULL" THEN            ' This string is placed in
  699.              SETINDEX BigRec.TableNum, IndexName$  ' TempRec.WhichIndex if
  700.            ELSE                                    ' user chooses "Default."
  701.              SETINDEX BigRec.TableNum, ""          ' "" is valid index name
  702.            END IF                                  'representing NULL index
  703.            ON ERROR GOTO 0                         '(i.e. the default order)
  704.            CALL AdjustIndex(BigRec)
  705.            LSET TempRec = EmptyRec
  706.          END IF
  707.  
  708.                            ' If choice is "F", first set current index
  709.        CASE SEEKFIELD      ' using same procedure as REORDER. Then do seek.
  710.  
  711.          Letter$ = CHR$(TABKEY)        ' Pass TABKEY for PlaceCursor
  712.          GotOne = ChooseOrder(BigRec, EmptyRec, TempRec, Letter$, SEEKFIELD)
  713.  
  714.          IF GotOne AND TEXTCOMP(TempRec.WhichIndex, "NULL") THEN
  715.            CALL SeekRecord(BigRec, TempRec, Letter$)
  716.            FirstLetter$ = ""
  717.            DimN = EOF(BigRec.TableNum): DimP = BOF(BigRec.TableNum)
  718.          END IF
  719.  
  720.                            ' STATUS gets the due date of a book & displays it
  721.        CASE STATUS
  722.          IF BigRec.TableNum = cBookStockTableNum THEN
  723.            CALL ShowStatus("", 0#)                  ' Explicitly type the 0
  724.            GotIt = GetStatus(BigRec, DateToShow#)   ' to avoid type mismatch
  725.            IF GotIt THEN
  726.              Alert$ = "Press B for information on Borrower of this book"
  727.              CALL ShowMessage(Alert$, 0)
  728.              CALL ShowStatus("Due Date: ", DateToShow#)
  729.            END IF
  730.          END IF
  731.  
  732.                           ' LendeeProfile displays borrower of displayed book
  733.        CASE BORROWER
  734.          CALL LendeeProfile(BigRec)
  735.  
  736.                           ' BooksBorrowed shows books borrowed by CardHolder
  737.        CASE WHICHBOOKS
  738.          IF Borrowed THEN CALL BooksBorrowed(BigRec)
  739.  
  740.                           ' If user hits "V" cycle through displayable tables
  741.        CASE OTHERTABLE
  742.          IF BigRec.TableNum < cDisplayedTables THEN
  743.            BigRec.TableNum = BigRec.TableNum + 1
  744.          ELSE
  745.            BigRec.TableNum = 1
  746.          END IF
  747.          EXIT DO
  748.                           ' If user picks "I" to check current book back in,
  749.                           ' make sure it is out, then check it back in
  750.        CASE CHECKIN
  751.          IF Borrowed THEN
  752.            GotIt = GetStatus(BigRec, DateToShow#)
  753.            IF DateToShow# THEN
  754.              CALL ReturnBook(BigRec, DateToShow#)
  755.            END IF
  756.          END IF
  757.                           ' If user picks "O" to check current book out,
  758.                           ' make sure it is available, then check it out
  759.        CASE CHECKOUT
  760.          GotIt = GetStatus(BigRec, DateToShow#)
  761.            IF DateToShow# = 0# THEN
  762.               CALL BorrowBook(BigRec)
  763.            ELSE
  764.               CALL ShowMessage("Sorry, this book is already checked out...", 0
  765.            END IF
  766.  
  767.                          ' If user wants to Undo all or some of a series of
  768.                          ' uncommitted edits, make sure there is a pending
  769.                          ' transaction to undo, then restore the state of the
  770.                          ' file one step at a time, or altogether, depending
  771.                          ' on whether U or ^U was entered.
  772.        CASE UNDO, UNDOALL
  773.          IF PendingFlag = TRUE THEN
  774.            IF n < 1 THEN
  775.              CALL ShowMessage("No pending edits left to Undo...", 0)
  776.            ELSE
  777.              IF Answer = UNDO THEN
  778.                Alert$ = "Restoring back to Savepoint # " + STR$(Marker(n))
  779.                CALL ShowMessage(Alert$, 0)
  780.                ROLLBACK Marker(n)
  781.                n = n - 1
  782.              ELSE                    ' If it's not UNDO, it must be UNDOALL
  783.                CALL ShowMessage("Undoing the whole last series of edits", 0)
  784.                ROLLBACK ALL
  785.                n = 0
  786.              END IF
  787.           END IF
  788.         ELSE
  789.           CALL ShowMessage("There are no pending edits left to Undo...", 0)
  790.         END IF
  791.  
  792.        CASE INVALIDKEY              ' Alert user if wrong key is pressed
  793.          CALL ShowMessage(KEYSMESSAGE, 0)
  794.          IF PendingFlag = TRUE THEN CALL DrawIndexBox(BigRec.TableNum, EDITREC
  795.      END SELECT
  796.      CALL DrawHelpKeys(BigRec.TableNum)
  797.      CALL ShowKeys(BigRec, BRIGHT + FOREGROUND, DimN, DimP)
  798.    LOOP
  799.  LOOP
  800.  CLOSE
  801.  END
  802.  
  803.  ' This error handler takes care of the most common ISAM errors
  804.  
  805.  MainHandler:
  806.  
  807.  IF ERR = 73 THEN        ' 73 = Feature unavailable
  808.    CALL ShowMessage("You forgot to load the ISAM TSR program", 0)
  809.    END
  810.  ELSEIF ERR = 88 THEN        ' 88 = Database inconsistent
  811.    ' If you have text files corresponding to each of the tables, then
  812.    ' MakeOver prompts for their names and creates an ISAM file from them.
  813.    CALL MakeOver(BigRec)
  814.    RESUME NEXT
  815.  
  816.  ELSEIF ERR = 83 THEN        ' 83 = Index not found
  817.      CALL DrawScreen(BigRec.TableNum)
  818.      CALL ShowMessage("Unable to set the index. Need more buffers?", 0)
  819.      RESUME NEXT
  820.  ELSEIF ERR = 86 THEN        ' 86 = Duplicate value for unique index
  821.      ' Trap errors when a user tries to enter a value for the Card Number or
  822.      ' ID fields that duplicates a value already in the table
  823.      CALL DupeFixer(BigRec)
  824.      RESUME
  825.  ELSE
  826.    Alert$ = "Sorry, not able to handle this error in BOOKLOOK: " + STR$(ERR)
  827.    CALL ShowMessage(Alert$, 0)
  828.    END
  829.  END IF
  830.  
  831.  '***************************************************************************
  832.  '*  The AddOne FUNCTION is called once for each field when the user wants  *
  833.  '*  to add a record to the displayed table.                                *
  834.  '*                                Parameters                               *
  835.  '*  BigRec    RecStruct variable containing information on all tables      *
  836.  '*  EmptyRec  Empty record of same type as BigRec                          *
  837.  '*  TempRec   Temporary record record of same type as BigRec               *
  838.  '*  Answer    Integer passed through to EditField; tells task to perform   *
  839.  '***************************************************************************
  840.  FUNCTION AddOne (BigRec AS RecStruct, EmptyRec AS RecStruct, TempRec AS RecSt
  841.    CALL ClearEm(BigRec.TableNum, 1, 1, 1, 1, 1, 1)
  842.    LSET TempRec = EmptyRec
  843.    CALL ShowMessage("Enter the first field of the new record", 0)
  844.    TempRec.TableNum = BigRec.TableNum
  845.    Edited = EditField(Argument%, TempRec, FirstLetter$, ADDRECORD, Answer%)
  846.    IF Edited THEN
  847.      AddOne = -1
  848.    ELSE
  849.      AddOne = 0
  850.    END IF
  851.    COLOR FOREGROUND, BACKGROUND
  852.  END FUNCTION
  853.  
  854.  '***************************************************************************
  855.  '* The CheckPosition SUB checks the table position after the requested user*
  856.  '* action is completed. If EOF follows a MOVENEXT or the user has chosen   *
  857.  '* MOVELAST, the Keys for Database Viewing/Editing box is updated to say   *
  858.  '* "No Next Record." If BOF  follows a MOVEPREVIOUS or user has chosen a   *
  859.  '* MOVEFIRST, "No Previous Record" is displayed.                           *
  860.  '* In either case, the position is held by executing MOVELAST or MOVEFIRST.*
  861.  '*                            Parameters:                                  *
  862.  '*   Big Rec      User-defined type containing all table information       *
  863.  '*   Answer       Tells what operation retrieve results from               *
  864.  '*   DimN & DimP  Flags telling which menu items should be dimmed/changed  *
  865.  '***************************************************************************
  866.  SUB CheckPosition (BigRec AS RecStruct, Answer, DimN%, DimP%)
  867.    SELECT CASE Answer
  868.      CASE GOAHEAD, ENDK
  869.        IF EOF(BigRec.TableNum) OR (Answer = ENDK) THEN
  870.          CALL ShowMessage("This is the last record in this index", 0)
  871.          DimN = TRUE: DimP = FALSE
  872.          MOVELAST BigRec.TableNum
  873.        ELSE                    ' If not EOF, turn on N
  874.          DimN = FALSE: DimP = FALSE
  875.          CALL EraseMessage
  876.        END IF
  877.      CASE GOBACK, HOME
  878.        IF BOF(BigRec.TableNum) OR (Answer = HOME) THEN
  879.          CALL ShowMessage("This is the first record in this index", 0)
  880.          DimP = TRUE: DimN = FALSE
  881.          MOVEFIRST BigRec.TableNum
  882.        ELSE
  883.          DimP = FALSE: DimN = FALSE
  884.          CALL EraseMessage
  885.        END IF
  886.    END SELECT
  887.  END SUB
  888.  
  889.  '***************************************************************************
  890.  '* The ChooseOrder FUNCTION calls PlaceCursor so the user can move around  *
  891.  '* the form to pick the index to set.                                      *
  892.  '*                                  Parameters                             *
  893.  '*  BigRec       BigRec has all the table information in updated form      *
  894.  '*  EmptyRec     EmptyRec is same template as BigRec, but fields are empty *
  895.  '*  TempRec      Holds intermediate and temporary data                     *
  896.  '*  FirstLetter  Catches letter if user starts typing during SEEKFIELD     *
  897.  '*  Task         Either REORDER or SEEKFIELD - passed on to PlaceCursor    *
  898.  '***************************************************************************
  899.  FUNCTION ChooseOrder (BigRec AS RecStruct, EmptyRec AS RecStruct, TempRec AS
  900.    CALL DrawTable(BigRec.TableNum)
  901.    CALL DrawIndexBox(BigRec.TableNum, Task)
  902.    Argument = TITLEFIELD                    ' Always start with first field
  903.    TempRec = EmptyRec: TempRec.TableNum = BigRec.TableNum
  904.  
  905.    ' Pass temporary RecStruct variable so user can't trash BigRec
  906.    value = PlaceCursor(Argument, TempRec, FirstLetter$, 1, Task)
  907.  
  908.    ' If the user chooses ESC, redraw everything, then exit to module level
  909.    IF ASC(TempRec.WhichIndex) = 0 THEN
  910.      CALL DrawIndexBox(BigRec.TableNum, Task)
  911.      CALL ShowRecord(BigRec)
  912.      CALL ShowMessage(KEYSMESSAGE, 0)
  913.      ChooseOrder = 0
  914.      EXIT FUNCTION
  915.    ELSE                                ' Otherwise, if user makes a choice
  916.      ChooseOrder = -1                  ' of Indexes, signal success to the
  917.    END IF                              ' module-level code
  918.  END FUNCTION
  919.  
  920.  '***************************************************************************
  921.  '*                                                                         *
  922.  '*  The DupeFixer SUB is called when the tries to enter a duplicate value  *
  923.  '*  for the BookStock table's IDnum column or the the CardHolders table's  *
  924.  '*  CardNum column, because their indexes are Unique. The procedure prompts*
  925.  '*  the user to enter a new value.                                         *
  926.  '***************************************************************************
  927.  SUB DupeFixer (BigRec AS RecStruct)
  928.      IF BigRec.TableNum = cBookStockTableNum THEN
  929.        DO
  930.          Alert$ = STR$(BigRec.Inventory.IDnum) + " is not unique. "
  931.          CALL ShowMessage(Alert$, 1)
  932.          COLOR YELLOW + BRIGHT, BACKGROUND
  933.          INPUT "Try another number: ", TempString$
  934.          BigRec.Inventory.IDnum = VAL(TempString$)
  935.        LOOP UNTIL BigRec.Inventory.IDnum
  936.      ELSEIF BigRec.TableNum = cCardHoldersTableNum THEN
  937.        DO
  938.          Alert$ = STR$(BigRec.Lendee.CardNum) + " is not unique. "
  939.          CALL ShowMessage(Alert$, 1)
  940.          COLOR YELLOW + BRIGHT, BACKGROUND
  941.          INPUT "Try another number: ", TempString$
  942.          BigRec.Lendee.CardNum = VAL(TempString$)
  943.        LOOP UNTIL BigRec.Lendee.CardNum
  944.      END IF
  945.      COLOR FOREGROUND, BACKGROUND
  946.  END SUB
  947.  
  948.  '********************************* EditCheck SUB ***************************
  949.  '*                                                                         *
  950.  '* The EditCheck procedure monitors what the user wants to do, and if the  *
  951.  '* choice is EDITRECORD, makes sure that a transaction is begun, or if it  *
  952.  '* already has begun, continues it. If a transaction has been pending, and *
  953.  '* the user chooses anything except EDITRECORD, then the transaction is    *
  954.  '* committed.                                                              *
  955.  '*                                                                         *
  956.  '*                            Parameters:                                  *
  957.  '*   Pending      A flag that indicates whether transaction is pending     *
  958.  '*   Task         Tells what operation the user wants to perform now       *
  959.  '*   TablesRec    Structure containing information about the tables        *
  960.  '*                                                                         *
  961.  '***************************************************************************
  962.  SUB EditCheck (Pending, Task, TablesRec AS RecStruct)
  963.    ' First, decide if this is a new or pending transaction, or not one at all
  964.    ' The only transaction in this program keeps edits to the current record
  965.    ' pending until the user moves on to a new record or a new operation
  966.    ' (for example a Reorder).
  967.  SHARED n                          ' n is index to array of savepoint ids
  968.  
  969.        IF Task = EDITRECORD THEN
  970.          IF Pending = FALSE THEN
  971.            BEGINTRANS
  972.            Pending = TRUE
  973.          END IF
  974.        ELSEIF Pending = TRUE THEN  ' Equivalent to Task<>EDITRECORD AND
  975.          COMMITTRANS               ' Pending=TRUE
  976.          Pending = FALSE
  977.          n = 0                     ' Reset array index for savepoint ids
  978.          CALL DrawIndexBox(TablesRec.TableNum, 0)
  979.        END IF
  980.  END SUB
  981.  
  982.  '***************************************************************************
  983.  '*  The GetInput FUNCTION takes the keystroke input by the user and returns*
  984.  '*  a constant indicating what the user wants to do. If the keystroke rep- *
  985.  '*  resents a valid operation, the choice is echoed to the screen.         *
  986.  '***************************************************************************
  987.  FUNCTION GetInput% (BigRec AS RecStruct)
  988.  DO
  989.    Answer$ = INKEY$
  990.  LOOP WHILE Answer$ = EMPTYSTRING
  991.    IF LEN(Answer$) > 1 THEN
  992.      RightSide = HighKeys%(Answer$)
  993.      GetInput = RightSide
  994.    ELSE
  995.      SELECT CASE Answer$
  996.        CASE "A", "a"
  997.          CALL UserChoice(BigRec, ALINE, 7, "Add Record")
  998.          GetInput% = ADDRECORD
  999.        CASE "B", "b"
  1000.          IF BigRec.TableNum = cBookStockTableNum THEN
  1001.            CALL UserChoice(BigRec, WLINE, 28, "Borrower")
  1002.            GetInput% = BORROWER
  1003.          ELSE
  1004.            CALL UserChoice(BigRec, WLINE, 13, "Books Outstanding")
  1005.            GetInput% = WHICHBOOKS
  1006.          END IF
  1007.        CASE "O", "o"
  1008.          CALL UserChoice(BigRec, CLINE, 7, "Check Book Out")
  1009.          GetInput% = CHECKOUT
  1010.        CASE "I", "i"
  1011.          CALL UserChoice(BigRec, CLINE, 28, "Check In")
  1012.          GetInput% = CHECKIN
  1013.        CASE "D", "d"
  1014.          CALL UserChoice(BigRec, ALINE, 28, "Drop Record")
  1015.          GetInput% = TOSSRECORD
  1016.        CASE "N", "n"
  1017.          GetInput% = GOAHEAD
  1018.        CASE "P", "p"
  1019.          GetInput% = GOBACK
  1020.        CASE "Q", "q"
  1021.          CALL UserChoice(BigRec, ELINE, 28, "Quit")
  1022.          GetInput% = QUIT
  1023.        CASE "E", "e"
  1024.          CALL UserChoice(BigRec, ELINE, 7, "Edit Record")
  1025.          GetInput% = EDITRECORD
  1026.        CASE "F", "f"
  1027.          CALL UserChoice(BigRec, RLINE, 28, "Find Record")
  1028.          GetInput% = SEEKFIELD
  1029.        CASE "R", "r"
  1030.          CALL UserChoice(BigRec, RLINE, 7, "Reorder Records")
  1031.          GetInput% = REORDER
  1032.        CASE "V", "v"
  1033.          GetInput% = OTHERTABLE
  1034.        CASE "W", "w"
  1035.          CALL UserChoice(BigRec, WLINE, 7, "When Due Back")
  1036.          GetInput% = STATUS
  1037.        CASE CHR$(ESCAPE)
  1038.          GetInput% = ESCAPE
  1039.        CASE "U", "u"
  1040.          GetInput = UNDO       ' U signals rollback request after editing
  1041.        CASE CHR$(CTRLU)        ' ^U = rollback a whole series of edits
  1042.          GetInput = UNDOALL
  1043.        CASE ELSE
  1044.          GetInput% = INVALIDKEY
  1045.          BEEP
  1046.      END SELECT
  1047.    END IF
  1048.  END FUNCTION
  1049.  
  1050.  '**************************************************************************
  1051.  '*  The HighKeys FUNCTION handles common two-byte keys input by the user. *
  1052.  '*  The Answer parameter is the keystroke entered by the user.            *
  1053.  '**************************************************************************
  1054.  FUNCTION HighKeys (Answer AS STRING)
  1055.    SELECT CASE ASC(RIGHT$(Answer$, 1))     ' Look at code for right byte
  1056.      CASE UP
  1057.        HighKeys = GOBACK                   ' UP is the up-arrow key
  1058.      CASE DOWN
  1059.        HighKeys = GOAHEAD                  ' DOWN is the down-arrow key
  1060.      CASE HOME
  1061.        HighKeys = HOME                     ' etc.
  1062.      CASE ENDK
  1063.        HighKeys = ENDK
  1064.        CASE LEFT
  1065.        HighKeys = OTHERTABLE
  1066.      CASE RIGHT
  1067.        HighKeys = OTHERTABLE
  1068.      CASE PGUP
  1069.        CALL ShowMessage("You could program so PGUP moves back n records", 0):
  1070.        HighKeys = INVALIDKEY
  1071.      CASE PGDN
  1072.        CALL ShowMessage("You could program so PGDN moves forward n records", 0
  1073.        HighKeys = INVALIDKEY
  1074.      CASE ELSE
  1075.        CALL ShowMessage("Sorry, that key isn't handled yet.", 0): SLEEP
  1076.        HighKeys = INVALIDKEY
  1077.    END SELECT
  1078.  END FUNCTION
  1079.  
  1080.  '****************************** Retriever SUB ******************************
  1081.  '* The Retriever SUB retrieves records from the database file and puts     *
  1082.  '* them into the appropriate recordvariable for the table being displayed. *
  1083.  '* An error trap is set in case the retrieve fails, in which case a message*
  1084.  '* is displayed. Note that if a preceding SEEKoperand fails, EOF is TRUE.  *
  1085.  '* In that case, position is set to the last record, which is retrieved.   *
  1086.  '*                            Parameters:                                  *
  1087.  '*   Big Rec      User-defined type containing all table information       *
  1088.  '*   DimN & DimP  Flags telling which menu items should be dimmed/changed  *
  1089.  '*   Task         Tells what operation retrieve results from               *
  1090.  '***************************************************************************
  1091.  SUB Retriever (BigRec AS RecStruct, DimN, DimP, Task)
  1092.    STATIC PeekFlag         ' Set this if user is just peeking at other table
  1093.    LOCATE , , 0            ' Turn off the cursor
  1094.    ' Show the user which choice was made, and whether EOF or BOF
  1095.    CALL ShowKeys(BigRec, FOREGROUND + BRIGHT, DimN, DimP)
  1096.    ' If table is empty, don't try to retrieve anything
  1097.    IF LOF(BigRec.TableNum) = 0 THEN
  1098.      DrawTable (BigRec.TableNum)
  1099.      CALL ShowMessage("There are no records in this table", 0): EXIT SUB
  1100.    END IF
  1101.  
  1102.    IF Task <> ENDK AND Task <> HOME THEN
  1103.      IF Task < EDITRECORD THEN                         ' Edit needs its
  1104.        CALL Indexbox(BigRec, CheckIndex%(BigRec, 0))   ' own prompts. Show
  1105.      ELSEIF Task > INVALIDKEY THEN                     ' indexbox otherwise
  1106.        IF Task <> ESC THEN CALL DrawIndexBox(BigRec.TableNum, 0)
  1107.        CALL Indexbox(BigRec, CheckIndex%(BigRec, 0))
  1108.      END IF
  1109.    END IF
  1110.    IF BOF(BigRec.TableNum) THEN MOVEFIRST (BigRec.TableNum)
  1111.    ON LOCAL ERROR GOTO LocalHandler           ' Trap errors on the retrieve.
  1112.    IF NOT EOF(BigRec.TableNum) THEN           ' Retrieve current record
  1113.      SELECT CASE BigRec.TableNum              ' from table being displayed
  1114.        CASE cBookStockTableNum                ' if EOF is not true
  1115.          RETRIEVE BigRec.TableNum, BigRec.Inventory
  1116.        CASE cCardHoldersTableNum
  1117.          RETRIEVE BigRec.TableNum, BigRec.Lendee
  1118.      END SELECT
  1119.    ELSE                                       ' If EOF is true, set position
  1120.      MOVELAST BigRec.TableNum                 ' to the last record in table,
  1121.      SELECT CASE BigRec.TableNum              ' then retrieve the record
  1122.        CASE cBookStockTableNum
  1123.          RETRIEVE BigRec.TableNum, BigRec.Inventory
  1124.        CASE cCardHoldersTableNum
  1125.          RETRIEVE BigRec.TableNum, BigRec.Lendee
  1126.      END SELECT
  1127.      DimN = TRUE
  1128.    END IF
  1129.    ON LOCAL ERROR GOTO 0                             ' Turn off error trap
  1130.    CALL ClearEm(BigRec.TableNum, 1, 1, 1, 1, 1, 1)
  1131.    CALL ShowRecord(BigRec)
  1132.    IF Task = OTHERTABLE THEN   ' If user is just peeking at the other table
  1133.      IF PeekFlag = 0 THEN      ' remind him how to get back to first table
  1134.        CALL ShowMessage("Press V to return to the other table", 0)
  1135.        PeekFlag = 1
  1136.      END IF
  1137.    ELSE
  1138.      PeekFlag = 0
  1139.    END IF
  1140.  EXIT SUB
  1141.  
  1142.  LocalHandler:
  1143.    IF ERR = 85 THEN
  1144.      CALL ShowMessage("Unable to retrieve your record...", 0)
  1145.    END IF
  1146.    RESUME NEXT
  1147.  END SUB
  1148.  
  1149.  '********************************* SeekRecord SUB *************************
  1150.  '*  SeekRecord takes the name of the user's chosen index, sets it as the  *
  1151.  '*  current index, then prompts the user to enter the value to seek. A    *
  1152.  '*  minimal editor, MakeString, gets user input. If the SEEK is on a com- *
  1153.  '*  bined index, GetKeyVals is called to get the input. Input is checked  *
  1154.  '*  for minimal acceptability by ValuesOK. If it is OK, GetOperand is     *
  1155.  '*  called to let the user specify how to conduct the SEEK.               *
  1156.  '*                              Parameters:                               *
  1157.  '*      TablesRec  Contains current record information for all tables     *
  1158.  '*      TempRec    Contains the name of the index on which to seek (in    *
  1159.  '*                 TempRec.WhichIndex element)                            *
  1160.  '*      Letter$    If the user starts typing instead of pressing ENTER    *
  1161.  '*                 Letter$ catches the keystroke, passes it to MakeString *
  1162.  '**************************************************************************
  1163.  SUB SeekRecord (TablesRec AS RecStruct, TempRec AS RecStruct, Letter$)
  1164.    DIM EmptyRec AS RecStruct             ' Make an empty record.
  1165.    IF LEFT$(Letter$, 1) < " " THEN       ' Exit if value is not a valid
  1166.                                          ' character, then redraw
  1167.      CALL DrawIndexBox(TablesRec.TableNum, SEEKFIELD)
  1168.      CALL Indexbox(TablesRec, CheckIndex%(TablesRec, TRUE))
  1169.      CALL ShowMessage("You must enter a valid string or numeric value", 0)
  1170.      EXIT SUB
  1171.    END IF
  1172.    TheTable = TablesRec.TableNum
  1173.    IndexName$ = RTRIM$(TempRec.WhichIndex)
  1174.    IF GETINDEX$(TheTable) <> IndexName$ THEN  ' If index to seek on is not
  1175.      ON LOCAL ERROR GOTO SeekHandler          ' current, set it now. Trap
  1176.      SETINDEX TheTable, IndexName$            ' possible failure of SETINDEX
  1177.      ON LOCAL ERROR GOTO 0                    ' then turn off error trap.
  1178.    END IF
  1179.    CALL AdjustIndex(TablesRec)                ' Show the current index
  1180.    TablesRec.WhichIndex = TempRec.WhichIndex
  1181.    TempRec = EmptyRec                         ' Clear TempRec for data
  1182.    TempRec.TableNum = TablesRec.TableNum
  1183.    ' Get the value to SEEK for from the user. The data type you assign the
  1184.    ' input to must be the same as the data in the database, so get it as a
  1185.    ' string with MakeString, then convert it to proper type for index. If
  1186.    ' the index is the combined index BigIndex, use GetKeyVals for input...
  1187.  
  1188.   SELECT CASE RTRIM$(LTRIM$(IndexName$))
  1189.     CASE "TitleIndexBS", "AuthorIndexBS", "PubIndexBS", "NameIndexCH", "StateI
  1190.      Prompt$ = "Value To Seek: "
  1191.      Key1$ = MakeString$(ASC(Letter$), Prompt$): IF Key1$ = "" THEN EXIT SUB
  1192.     CASE "IDIndex", "CardNumIndexCH", "ZipIndexCH"
  1193.      ValueToSeek$ = MakeString$(ASC(Letter$), Prompt$)
  1194.      IF ValueToSeek$ = "" THEN EXIT SUB
  1195.      IF IndexName$ = "IDIndex" THEN
  1196.        NumberToSeek# = VAL(ValueToSeek$)
  1197.        Key1$ = ValueToSeek$
  1198.      ELSE
  1199.        NumberToSeek& = VAL(ValueToSeek$)
  1200.        Key1$ = ValueToSeek$
  1201.      END IF
  1202.     CASE "BigIndex"
  1203.      CALL GetKeyVals(TempRec, Key1$, Key2$, Key3#, Letter$)
  1204.      ValueToSeek$ = STR$(Key3#)
  1205.     CASE ""
  1206.       Alert$ = "Sorry, can't search for field values on the default index"
  1207.       CALL ShowMessage(Alert$, 0)
  1208.     CASE ELSE
  1209.    END SELECT
  1210.  
  1211.    ' Make sure the input values are minimally acceptable
  1212.  
  1213.    IF NOT ValuesOK(TablesRec, Key1$, Key2$, ValueToSeek$) THEN
  1214.      CALL ShowMessage("Sorry, problem with your entry. Try again!", 0)
  1215.      EXIT SUB
  1216.    END IF
  1217.  
  1218.    ' Show the user the values he entered in their appropriate fields
  1219.    CALL ClearEm(TablesRec.TableNum, 1, 1, 1, 1, 1, 1)
  1220.    CALL ShowIt(TempRec, IndexName$, TheTable, Key1$)
  1221.  
  1222.    ' GetOperand lets user specify the way the SEEK is to be conducted ---
  1223.    ' either  =, >, >=, <, or <= the value that was entered above
  1224.  
  1225.    DidIt = GetOperand%(Operand$)
  1226.  
  1227.    ' The actual SEEK has to be done according to two factors, the Index on
  1228.    ' which it is conducted, and the condition chosen in GetOperand. In the
  1229.    ' next section, case on the Operand returned, then IF and ELSEIF on the
  1230.    ' basis of the index on which the search is being conducted
  1231.  
  1232.    IF Operand$ <> "<>" THEN                ' "<>" represents user ESC choice
  1233.  
  1234.     SELECT CASE Operand$
  1235.      CASE "", "="                        ' If operand ="" or "=", use =
  1236.        IF IndexName$ = "BigIndex" THEN
  1237.          IF INSTR(Key2$, ",") = 0 THEN Key2$ = TransposeName(Key2$) ' a name
  1238.          SEEKEQ TheTable, Key1$, Key2$, Key3#
  1239.        ELSEIF IndexName$ = "NameIndexCH" OR IndexName$ = "AuthorIndexBS" THEN
  1240.          IF INSTR(Key1$, ",") = 0 THEN Key1$ = TransposeName(Key1$) ' a name
  1241.          SEEKEQ TheTable, LTRIM$(RTRIM$(Key1$))
  1242.        ELSEIF IndexName$ = "IDIndex" THEN
  1243.          SEEKEQ TheTable, NumberToSeek#
  1244.        ELSEIF IndexName$ = "CardNumIndexCH" OR IndexName$ = "ZipIndexCH" THEN
  1245.          SEEKEQ TheTable, NumberToSeek&
  1246.        ELSE
  1247.          SEEKEQ TheTable, Key1$
  1248.        END IF
  1249.      CASE ">="                      ' at least gets them close
  1250.        IF IndexName$ = "BigIndex" THEN
  1251.          IF INSTR(Key2$, ",") = 0 THEN Key2$ = TransposeName(Key2$) ' a name
  1252.          SEEKGE TheTable, Key1$, Key2$, Key3#
  1253.        ELSEIF IndexName$ = "NameIndexCH" OR IndexName$ = "AuthorIndexBS" THEN
  1254.          IF INSTR(Key1$, ",") = 0 THEN Key1$ = TransposeName(Key1$)
  1255.          SEEKGE TheTable, Key1$
  1256.        ELSEIF IndexName$ = "IDIndex" THEN
  1257.          SEEKGE TheTable, NumberToSeek#
  1258.        ELSEIF IndexName$ = "CardNumIndexCH" OR IndexName$ = "ZipIndexCH" THEN
  1259.          SEEKGE TheTable, NumberToSeek&
  1260.        ELSE
  1261.          SEEKGE TheTable, Key1$
  1262.        END IF
  1263.      CASE ">"
  1264.        IF IndexName$ = "BigIndex" THEN
  1265.          IF INSTR(Key2$, ",") = 0 THEN Key2$ = TransposeName(Key2$)
  1266.          SEEKGT TheTable, Key1$, Key2$, Key3#
  1267.        ELSEIF IndexName$ = "NameIndexCH" OR IndexName$ = "AuthorIndexBS" THEN
  1268.          IF INSTR(Key1$, ",") = 0 THEN Key1$ = TransposeName(Key1$)
  1269.          SEEKGT TheTable, Key1$
  1270.        ELSEIF IndexName$ = "IDIndex" THEN
  1271.          SEEKGT TheTable, NumberToSeek#
  1272.        ELSEIF IndexName$ = "CardNumIndexCH" OR IndexName$ = "ZipIndexCH" THEN
  1273.          SEEKGT TheTable, NumberToSeek&
  1274.        ELSE
  1275.          SEEKGT TheTable, Key1$
  1276.        END IF
  1277.      CASE "<="
  1278.        IF IndexName$ = "BigIndex" THEN
  1279.          IF INSTR(Key2$, ",") = 0 THEN Key2$ = TransposeName(Key2$)
  1280.          SEEKGT TheTable, Key1$, Key2$, Key3#
  1281.          MOVEPREVIOUS TheTable
  1282.        ELSEIF IndexName$ = "NameIndexCH" OR IndexName$ = "AuthorIndexBS" THEN
  1283.          IF INSTR(Key1$, ",") = 0 THEN Key1$ = TransposeName(Key1$)
  1284.          SEEKGT TheTable, Key1$
  1285.          MOVEPREVIOUS TheTable
  1286.        ELSEIF IndexName$ = "IDIndex" THEN
  1287.          SEEKGT TheTable, NumberToSeek#
  1288.          MOVEPREVIOUS TheTable
  1289.        ELSEIF IndexName$ = "CardNumIndexCH" OR IndexName$ = "ZipIndexCH" THEN
  1290.          SEEKGT TheTable, NumberToSeek&
  1291.          MOVEPREVIOUS TheTable
  1292.        ELSE
  1293.          SEEKGT TheTable, Key1$
  1294.          MOVEPREVIOUS TheTable
  1295.        END IF
  1296.      CASE "<"
  1297.        IF IndexName$ = "BigIndex" THEN
  1298.          IF INSTR(Key2$, ",") = 0 THEN Key2$ = TransposeName(Key2$)
  1299.          SEEKGE TheTable, Key1$, Key2$, Key3#
  1300.          MOVEPREVIOUS TheTable
  1301.        ELSEIF IndexName$ = "NameIndexCH" OR IndexName$ = "AuthorIndexBS" THEN
  1302.          IF INSTR(Key1$, ",") = 0 THEN Key1$ = TransposeName(Key1$)
  1303.          SEEKGE TheTable, Key1$
  1304.          MOVEPREVIOUS TheTable
  1305.        ELSEIF IndexName$ = "IDIndex" THEN
  1306.          SEEKGE TheTable, NumberToSeek#
  1307.          MOVEPREVIOUS TheTable
  1308.        ELSEIF IndexName$ = "CardNumIndexCH" OR IndexName$ = "ZipIndexCH" THEN
  1309.          SEEKGE TheTable, NumberToSeek&
  1310.          MOVEPREVIOUS TheTable
  1311.        ELSE
  1312.          SEEKGE TheTable, Key1$
  1313.          MOVEPREVIOUS TheTable
  1314.        END IF
  1315.      CASE ELSE
  1316.        Alert$ = "The returned operand was " + Operand$
  1317.        CALL ShowMessage(Alert$, 0)
  1318.        SLEEP
  1319.    END SELECT
  1320.   ELSE                        ' If they choose ESC, go back to module level
  1321.     CALL DrawScreen(TheTable)
  1322.     CALL ShowRecord(TablesRec)
  1323.     Alert$ = "You've escaped. " + KEYSMESSAGE
  1324.     CALL ShowMessage(Alert$, 0)
  1325.     SLEEP
  1326.     Operand$ = ""
  1327.   END IF
  1328.    CALL EraseMessage
  1329.    CALL DrawScreen(TheTable)
  1330.    CALL Indexbox(TablesRec, CheckIndex%(TablesRec, FALSE))
  1331.   IF EOF(TablesRec.TableNum) THEN
  1332.    Alert$ = "Sorry,  unable to match value you entered with any field value"
  1333.    CALL ShowMessage(Alert$, 0):  ' SLEEP: EraseMessage
  1334.   END IF
  1335.  
  1336.  EXIT SUB
  1337.  
  1338.  SeekHandler:
  1339.   IF ERR = 83 THEN                          ' 83 = Index not found
  1340.      CALL DrawScreen(TablesRec.TableNum)
  1341.      Alert$ = "SETINDEX for " + IndexName$ + " failed. Need more buffers?"
  1342.      CALL ShowMessage(Alert$, 0)
  1343.      EXIT SUB
  1344.   END IF
  1345.  
  1346.  END SUB   ' End of SeekRecord procedure
  1347.  
  1348.  
  1349.  
  1350.  BOOKMOD1.BAS
  1351.  CD-ROM Disc Path:   \SAMPCODE\BASIC\BOOKMOD1.BAS
  1352.  
  1353.  '***********************************************************************
  1354.  '*      This is module level code for BOOKMOD2.BAS, and contains screen*
  1355.  '*      drawing and user interface maintenance routines. This module   *
  1356.  '*      doesn't contain ISAM statements.                               *
  1357.  '***********************************************************************
  1358.  
  1359.  DEFINT A-Z
  1360.  '$INCLUDE: 'booklook.bi'
  1361.  KeysBox:
  1362.    DATA "╔══════════════════════════════════════╗"
  1363.    DATA "║                                      ║"
  1364.    DATA "║                                      ║"
  1365.    DATA "║                                      ║"
  1366.    DATA "║                                      ║"
  1367.    DATA "║                                      ║"
  1368.    DATA "║                                      ║"
  1369.    DATA "║                                      ║"
  1370.    DATA "╚═╡ Keys for Database Viewing/Editing ╞╝"
  1371.  
  1372.  HelpKeys1:
  1373.    DATA ""
  1374.    DATA "N = Next Record      P = Previous   "
  1375.    DATA "R = Reorder Records  F = Find Record"
  1376.    DATA "W = When Due Back    B = Borrower   "
  1377.    DATA "      V = View Other Table          "
  1378.    DATA "A = Add Record       D = Drop Record"
  1379.    DATA "E = Edit Record      Q = Quit       "
  1380.    DATA "O = Check Book Out   I = Check In   "
  1381.    DATA ""
  1382.  
  1383.  HelpKeys2:
  1384.    DATA ""
  1385.    DATA "N = Next Record      P = Previous   "
  1386.    DATA "R = Reorder Records  F = Find Record"
  1387.    DATA "      B = Books Outstanding         "
  1388.    DATA "      V = View Other Table          "
  1389.    DATA "A = Add Record       D = Drop Record"
  1390.    DATA "E = Edit Record      Q = Quit       "
  1391.    DATA "                                    "
  1392.    DATA ""
  1393.  
  1394.  Indexbox1:
  1395.    DATA "╔═══════════════════════════╗"
  1396.    DATA "║ By Titles                 ║"
  1397.    DATA "║ By Authors                ║"
  1398.    DATA "║ By Publishers             ║"
  1399.    DATA "║ By ID numbers             ║"
  1400.    DATA "║ By Title + Author + ID    ║"
  1401.    DATA "║ Default = Insertion order ║"
  1402.    DATA "║                           ║"
  1403.    DATA "╚═╡ Current Sorting Order ╞═╝"
  1404.  Indexbox2:
  1405.    DATA "╔═══════════════════════════╗"
  1406.    DATA "║ By Name                   ║"
  1407.    DATA "║ By State                  ║"
  1408.    DATA "║ By Zip code               ║"
  1409.    DATA "║ By Card number            ║"
  1410.    DATA "║                           ║"
  1411.    DATA "║ Default = Insertion order ║"
  1412.    DATA "║                           ║"
  1413.    DATA "╚═╡ Current Sorting Order ╞═╝"
  1414.  
  1415.  
  1416.  BooksTable:
  1417.  DATA "╔════════════════════════════════════════════════════════════════════╗"
  1418.  DATA "║                                                                    ║"
  1419.  DATA "║    Title:                                                          ║"
  1420.  DATA "║                                                                    ║"
  1421.  DATA "║    Author:                                                         ║"
  1422.  DATA "║                                                                    ║"
  1423.  DATA "║    Publisher:                                                      ║"
  1424.  DATA "║                                                                    ║"
  1425.  DATA "║    Edition:                                                        ║"
  1426.  DATA "║                                                                    ║"
  1427.  DATA "║    Price:                                                          ║"
  1428.  DATA "║                                                                    ║"
  1429.  DATA "║    ID number:                                                      ║"
  1430.  DATA "╚════════════════════════════════════════════════════════════════════╝"
  1431.  
  1432.  
  1433.  LendeesTable:
  1434.  DATA "╔════════════════════════════════════════════════════════════════════╗"
  1435.  DATA "║                                                                    ║"
  1436.  DATA "║   Name:                                                            ║"
  1437.  DATA "║                                                                    ║"
  1438.  DATA "║   Street:                                                          ║"
  1439.  DATA "║                                                                    ║"
  1440.  DATA "║   City:                                                            ║"
  1441.  DATA "║                                                                    ║"
  1442.  DATA "║   State:                                                           ║"
  1443.  DATA "║                                                                    ║"
  1444.  DATA "║   Zipcode:                                                         ║"
  1445.  DATA "║                                                                    ║"
  1446.  DATA "║   Card number:                                                     ║"
  1447.  DATA "╚════════════════════════════════════════════════════════════════════╝"
  1448.  
  1449.  OperandBox:
  1450.  DATA "╔═══════════════════════════╗"
  1451.  DATA "║                           ║"
  1452.  DATA "║ Greater Than              ║"
  1453.  DATA "║ or                        ║"
  1454.  DATA "║ Equal To     Value Entered║"
  1455.  DATA "║ or                        ║"
  1456.  DATA "║ Less Than                 ║"
  1457.  DATA "║                           ║"
  1458.  DATA "╚══╡ Relationship to Key ╞══╝"
  1459.  
  1460.  EditMessage:
  1461.  DATA "╔═══════════════════════════╗"
  1462.  DATA "║ A log is being kept while ║"
  1463.  DATA "║ you edit fields in this   ║"
  1464.  DATA "║ record. Press U to undo   ║"
  1465.  DATA "║ each preceding edit, or   ║"
  1466.  DATA "║ CTRL+U to undo all of the ║"
  1467.  DATA "║ pending edits as a group. ║"
  1468.  DATA "║                           ║"
  1469.  DATA "╚═════╡ To Undo Edits ╞═════╝"
  1470.  
  1471.  '***************************************************************************
  1472.  '*  The ClearEm SUB erases the parts of the screen where table record col- *
  1473.  '*  umn information is displayed, depending on which fields are specified. *
  1474.  '*                                Parameters                               *
  1475.  '*  TableNum    Integer specifying the table being displayed               *
  1476.  '*  Field?      Boolean values specifying which fields to erase            *
  1477.  '***************************************************************************
  1478.  SUB ClearEm (TableNum%, Field1%, Field2%, Field3%, Field4%, Field5%, Field6%)
  1479.  
  1480.    DIM ToClear(10) AS INTEGER
  1481.  
  1482.    ToClear(0) = Field1: ToClear(1) = Field2: ToClear(2) = Field3
  1483.    ToClear(3) = Field4: ToClear(4) = Field5: ToClear(5) = Field6
  1484.  
  1485.    COLOR FOREGROUND, BACKGROUND
  1486.  
  1487.        FOR Index = 0 TO 5
  1488.          IF ToClear(Index) THEN
  1489.            SELECT CASE Index
  1490.              CASE 0
  1491.                LOCATE TITLEFIELD, 18
  1492.                PRINT "                                                    "
  1493.              CASE 1
  1494.                LOCATE AUTHORFIELD, 18
  1495.                PRINT "                                                    "
  1496.              CASE 2
  1497.                LOCATE PUBFIELD, 18
  1498.                PRINT "                                                    "
  1499.              CASE 3
  1500.                LOCATE EDFIELD, 18
  1501.                PRINT "                                                    "
  1502.              CASE 4
  1503.                IF TableNum% = cCardHoldersTableNum THEN
  1504.                  LOCATE PRICEFIELD, 18
  1505.                  PRINT "                                                    "
  1506.                ELSE
  1507.                  LOCATE PRICEFIELD, 19
  1508.                  PRINT "                                                   "
  1509.                END IF
  1510.              CASE 5
  1511.                LOCATE IDFIELD, 18
  1512.                PRINT "                                                    "
  1513.            END SELECT
  1514.          END IF
  1515.        NEXT Index
  1516.  END SUB
  1517.  
  1518.  '**************************************************************************
  1519.  '*  The ConfirmEntry FUNCTION echoes the user's input and processes his   *
  1520.  '*  response to make sure the proper action is taken.                     *
  1521.  '*                                 Parameters                             *
  1522.  '*  Letter$   Contains the input that the user has just entered.          *
  1523.  '**************************************************************************
  1524.  FUNCTION ConfirmEntry% (Letter$)
  1525.    Alert$ = "Press ENTER to confirm choice, type value, or TAB to move on"
  1526.    CALL ShowMessage(Alert$, 1)
  1527.    DO
  1528.    Answer$ = INKEY$
  1529.    LOOP WHILE Answer$ = EMPTYSTRING
  1530.    Reply% = ASC(Answer$)
  1531.  
  1532.    SELECT CASE Reply%
  1533.      CASE ENTER
  1534.        ConfirmEntry% = -1
  1535.        Letter$ = ""
  1536.      CASE TABKEY
  1537.        ConfirmEntry% = 0
  1538.        Letter$ = Answer$
  1539.      CASE ASC(" ") TO ASC("~")
  1540.        Letter$ = Answer$
  1541.        ConfirmEntry = -1
  1542.      CASE ELSE
  1543.        ConfirmEntry% = 0
  1544.        Letter$ = "eScApE"
  1545.        CALL ShowMessage("Invalid key --- Try again", 0)
  1546.     END SELECT
  1547.  END FUNCTION
  1548.  
  1549.  '***************************************************************************
  1550.  '*    The DrawHelpBoox SUB draws the menu box that links a key to a task.  *
  1551.  '***************************************************************************
  1552.  SUB DrawHelpBox
  1553.    COLOR FOREGROUND, BACKGROUND
  1554.    RESTORE KeysBox
  1555.      FOR Row = BOXTOP TO BOXEND
  1556.        LOCATE Row, 1
  1557.        READ Temp$
  1558.        PRINT Temp$
  1559.        IF Row = BOXEND THEN
  1560.          COLOR BACKGROUND, FOREGROUND + BRIGHT
  1561.          LOCATE Row, HELPCOL + 3
  1562.          PRINT " Keys for Database Viewing/Editing "
  1563.          COLOR FOREGROUND, BACKGROUND
  1564.        END IF
  1565.      NEXT Row
  1566.    COLOR FOREGROUND, BACKGROUND
  1567.  END SUB
  1568.  
  1569.  '***************************************************************************
  1570.  '*    The DrawHelpKeys SUB refills the menu box that links a key to a task.*
  1571.  '*                                Parameters                               *
  1572.  '*    TableNum    Integer identifying the table being displayed            *
  1573.  '***************************************************************************
  1574.  SUB DrawHelpKeys (TableNum AS INTEGER)
  1575.  
  1576.  COLOR FOREGROUND, BACKGROUND
  1577.  IF TableNum = cBookStockTableNum THEN RESTORE HelpKeys1 ELSE RESTORE HelpKeys
  1578.  FOR Row = BOXTOP TO BOXEND
  1579.    LOCATE Row, HELPCOL + 2
  1580.    READ Temp$
  1581.    PRINT Temp$
  1582.    IF Row = BOXEND THEN
  1583.      COLOR BACKGROUND, FOREGROUND + BRIGHT
  1584.      LOCATE Row, HELPCOL + 3
  1585.      PRINT " Keys for Database Viewing/Editing "
  1586.      COLOR FOREGROUND, BACKGROUND
  1587.      END IF
  1588.  NEXT Row
  1589.  COLOR FOREGROUND, BACKGROUND
  1590.  
  1591.  END SUB
  1592.  
  1593.  '***************************************************************************
  1594.  '*  The DrawIndexBox procedure draws the appropriate index box, depending  *
  1595.  '*  the table being displayed. If the task is EDITRECORD, the index box    *
  1596.  '*  information is replaced with information about Undo and Undo All       *
  1597.  '*                               Parameters                                *
  1598.  '*  TableNum    Integer identifying the table being displayed              *
  1599.  '*  Task        Integer identifying the task the user is involved in       *
  1600.  '***************************************************************************
  1601.  SUB DrawIndexBox (TableNum AS INTEGER, Task%)
  1602.  
  1603.  COLOR FOREGROUND, BACKGROUND
  1604.  
  1605.  IF Task = EDITRECORD THEN
  1606.    RESTORE EditMessage
  1607.  ELSE
  1608.    IF TableNum = 1 THEN RESTORE Indexbox1 ELSE RESTORE Indexbox2
  1609.  END IF
  1610.  
  1611.  FOR Row = BOXTOP TO BOXEND
  1612.    LOCATE Row, 42
  1613.    READ Temp$
  1614.    PRINT Temp$
  1615.    IF Row = BOXEND THEN
  1616.      IF Task = EDITRECORD THEN
  1617.        COLOR FOREGROUND + BRIGHT, BACKGROUND
  1618.        LOCATE 19, INDBOX + 16
  1619.        PRINT "U"
  1620.        LOCATE 21, INDBOX + 2
  1621.        PRINT "CTRL+U"
  1622.        LOCATE Row, INDBOX + 7
  1623.        PRINT " To Undo Edits "
  1624.        COLOR FOREGROUND, BACKGROUND
  1625.      ELSE
  1626.        COLOR BACKGROUND, FOREGROUND + BRIGHT
  1627.        LOCATE Row, INDBOX + 3
  1628.        PRINT " Current Sorting Order "
  1629.        COLOR FOREGROUND, BACKGROUND
  1630.      END IF
  1631.    END IF
  1632.  NEXT Row
  1633.  COLOR FOREGROUND, BACKGROUND
  1634.  
  1635.  END SUB
  1636.  
  1637.  '***************************************************************************
  1638.  '*  The DrawScreen SUB calls other procedures to draw the appropriate parts*
  1639.  '*  of the screen for the table to be displayed.                           *
  1640.  '*                                Parameters                               *
  1641.  '*  TableNum    Integer telling which table is to be shown                 *
  1642.  '***************************************************************************
  1643.  SUB DrawScreen (TableNum AS INTEGER)
  1644.    CALL DrawTable(TableNum)
  1645.    CALL DrawHelpBox
  1646.    CALL DrawHelpKeys(TableNum)
  1647.    CALL DrawIndexBox(TableNum, Task)
  1648.    CALL ShowMessage("", 0)
  1649.    COLOR FOREGROUND, BACKGROUND
  1650.  END SUB
  1651.  
  1652.  '***************************************************************************
  1653.  '*  The DrawTable SUB draws and lables the table being displayed.          *
  1654.  '*                                Parameters                               *
  1655.  '*  TableNum    The number of the table currently being displayed          *
  1656.  '***************************************************************************
  1657.  SUB DrawTable (TableNum AS INTEGER)
  1658.  CALL ClearEm(TableNum, 1, 1, 1, 1, 1, 1)
  1659.  VIEW PRINT
  1660.  COLOR FOREGROUND, BACKGROUND
  1661.  SELECT CASE TableNum
  1662.    CASE 1
  1663.      TableName$ = " Book Stock Table "
  1664.    CASE 2
  1665.      TableName$ = " Card Holders Table "
  1666.  END SELECT
  1667.  
  1668.  HowLong = LEN(TableName$)
  1669.  NameSpace$ = "╡" + STRING$(HowLong, 32) + "╞"
  1670.  PlaceName = (72 \ 2) - (HowLong \ 2)
  1671.  
  1672.  IF TableNum = 1 THEN RESTORE BooksTable ELSE RESTORE LendeesTable
  1673.  
  1674.  COLOR FOREGROUND, BACKGROUND
  1675.  
  1676.  FOR Row = TABLETOP TO TABLEEND
  1677.    LOCATE Row, 1
  1678.    READ Temp$
  1679.    PRINT Temp$
  1680.    IF Row = TABLETOP THEN
  1681.      LOCATE TABLETOP, PlaceName
  1682.      PRINT NameSpace$
  1683.      COLOR BACKGROUND, BRIGHT + FOREGROUND
  1684.      LOCATE 1, PlaceName + 1
  1685.      PRINT TableName$
  1686.      COLOR FOREGROUND, BACKGROUND
  1687.    END IF
  1688.  NEXT Row
  1689.  COLOR FOREGROUND, BACKGROUND
  1690.  
  1691.  END SUB
  1692.  
  1693.  '***************************************************************************
  1694.  '*  The EraseMessage SUB erases the message in the message box between the *
  1695.  '*  displayed table and the menus at the bottom of the screen. It replaces *
  1696.  '*  the corners of the table and menus that may have been overwritten      *
  1697.  '***************************************************************************
  1698.  SUB EraseMessage
  1699.    COLOR FOREGROUND, BACKGROUND
  1700.         LOCATE MESBOXTOP, 1
  1701.         PRINT "╚"; STRING$(68, CHR$(205)); "╝"
  1702.         LOCATE MESFIELD, 1
  1703.         PRINT SPACE$(70)
  1704.         LOCATE MESBOXEND, 1
  1705.         PRINT "╔"; STRING$(38, CHR$(205)); "╗ ╔"; STRING$(27, CHR$(205)); "╗"
  1706.  
  1707.  END SUB
  1708.  
  1709.  '**************************** MakeString FUNCTION **************************
  1710.  '*                                                                         *
  1711.  '* The MakeString FUNCTION provides a minimal editor to operate in the     *
  1712.  '* BOOKLOOK message box. A prompt is shown. The user can enter numbers,    *
  1713.  '* letters, punctuation, the ENTER, BACKSPACE and ESC keys.                *
  1714.  '*                                                                         *
  1715.  '*                            Parameters:                                  *
  1716.  '*   FilterTrap   Brings in a keystroke or letter by ASCII value           *
  1717.  '*   ThisString   Prompt passed in depends on calling function             *
  1718.  '*                                                                         *
  1719.  '***************************************************************************
  1720.  FUNCTION MakeString$ (FilterTrap AS INTEGER, ThisString$)
  1721.  
  1722.  MessageLen = LEN(ThisString$)                   ' Save length of the prompt
  1723.  IF FilterTrap THEN                              ' then, if a letter was
  1724.    ThisString$ = ThisString$ + CHR$(FilterTrap)  ' passed in, add it to the
  1725.    NewString$ = CHR$(FilterTrap)                 ' prompt and use it to start
  1726.  END IF                                          ' string to be returned.
  1727.  CALL ShowMessage(ThisString$, 1)                ' Show the string and turn
  1728.  DO                                              ' on cursor at end.
  1729.    DO
  1730.    Answer$ = INKEY$
  1731.    LOOP WHILE Answer$ = EMPTYSTRING
  1732.        SELECT CASE Answer$
  1733.          CASE CHR$(ESCAPE)
  1734.            FilterTrap = ESCAPE
  1735.            CALL ShowMessage(KEYSMESSAGE, 0)
  1736.            EXIT FUNCTION
  1737.          CASE " " TO "~"
  1738.            NewString$ = NewString$ + Answer$
  1739.            ThisString$ = ThisString$ + Answer$
  1740.            CALL ShowMessage(ThisString$, 1)
  1741.          CASE CHR$(BACKSPACE)
  1742.            ShortLen = LEN(ThisString$) - 1
  1743.            ThisString$ = MID$(ThisString$, 1, ShortLen)
  1744.            NewString$ = MID$(ThisString$, MessageLen + 1)
  1745.            CALL ShowMessage(ThisString$, 1)
  1746.          CASE CHR$(ENTER)
  1747.            LOCATE , , 0
  1748.            MakeString$ = LTRIM$(RTRIM$(NewString$))
  1749.            EXIT FUNCTION
  1750.          CASE ELSE
  1751.            BEEP
  1752.            CALL ShowMessage("Not a valid key --- press Space bar", 0)
  1753.        END SELECT
  1754.  LOOP
  1755.  END FUNCTION
  1756.  
  1757.  '***************************************************************************
  1758.  '*  The ReturnKey$ FUNCTION gets a key from the user and returns its value *
  1759.  '***************************************************************************
  1760.  FUNCTION ReturnKey$
  1761.    DO
  1762.      Answer$ = INKEY$
  1763.    LOOP WHILE Answer$ = EMPTYSTRING
  1764.    ReturnKey$ = Answer$
  1765.  END FUNCTION
  1766.  
  1767.  '******************************** ShowIt SUB ******************************
  1768.  '*                                                                        *
  1769.  '*    After the user enters a value to search for in a specific index,    *
  1770.  '*    this SUB places the value in the proper element of the temporary    *
  1771.  '*    record variable, then displays the value in the field. Finally,     *
  1772.  '*    the user is prompted to choose the relationship the indexed value   *
  1773.  '*    should have to the key that has been entered.                       *
  1774.  '*                            Parameters:                                 *
  1775.  '*    TabesRec:       A temporary recordvariable - same as BigRec         *
  1776.  '*    WhichIndex:     Tells name of Index on which key should be sought   *
  1777.  '*    WhichTable:     The number of the table currently being displayed   *
  1778.  '*    StringTo Show:  Value user wants to search for in index             *
  1779.  '*                                                                        *
  1780.  '**************************************************************************
  1781.  SUB ShowIt (TablesRec AS RecStruct, WhichIndex$, WhichTable%, StringToShow$)
  1782.    TablesRec.TableNum = WhichTable
  1783.    TablesRec.WhichIndex = WhichIndex$
  1784.    COLOR BRIGHT + FOREGROUND, BACKGROUND
  1785.        SELECT CASE WhichIndex$
  1786.          CASE "TitleIndexBS"
  1787.            TablesRec.Inventory.Title = StringToShow$
  1788.          CASE "AuthorIndexBS"
  1789.            TablesRec.Inventory.Author = StringToShow$
  1790.          CASE "PubIndexBS"
  1791.            TablesRec.Inventory.Publisher = StringToShow$
  1792.          CASE "IDIndex"
  1793.            TablesRec.Inventory.IDnum = VAL(StringToShow$)
  1794.          CASE "NameIndexCH"
  1795.            TablesRec.Lendee.TheName = StringToShow$
  1796.          CASE "StateIndexCH"
  1797.            TablesRec.Lendee.State = StringToShow$
  1798.          CASE "ZipIndexCH"
  1799.            TablesRec.Lendee.Zip = VAL(StringToShow$)
  1800.          CASE "CardNumIndexCH"
  1801.            TablesRec.Lendee.CardNum = VAL(StringToShow$)
  1802.        END SELECT
  1803.      CALL ShowRecord(TablesRec)
  1804.    COLOR FOREGROUND, BACKGROUND
  1805.  END SUB
  1806.  
  1807.  '***************************************************************************
  1808.  '*  The ShowKeys SUB presents the key the user should press for a desired  *
  1809.  '*  operation associated with a description of the task.                   *
  1810.  '*                               Parameters                                *
  1811.  '*  TablesRec   RecStruct type variable containing table information       *
  1812.  '*  ForeGrnd    Integer indicating whether key is highlighted or not       *
  1813.  '*  TableDone   1 for No Next Record, 0 otherwise (usually DimN)           *
  1814.  '*  TableStart  1 for No Previous Record, 0 otherwise (usually DimP)       *
  1815.  '***************************************************************************
  1816.  SUB ShowKeys (TablesRec AS RecStruct, ForeGrnd%, TableDone%, TableStart%)
  1817.    COLOR ForeGrnd, BACKGROUND                    'foreground bright
  1818.    LOCATE NLINE, 3
  1819.    PRINT "N"
  1820.    LOCATE NLINE, 24
  1821.    PRINT "P"
  1822.    LOCATE RLINE, 3
  1823.    PRINT "R"
  1824.    LOCATE RLINE, 24
  1825.    PRINT "F"
  1826.    IF TablesRec.TableNum = cBookStockTableNum THEN
  1827.      LOCATE WLINE, 3
  1828.      PRINT "W"
  1829.      LOCATE WLINE, 24
  1830.      PRINT "B"
  1831.    ELSE
  1832.      LOCATE WLINE, 9
  1833.      PRINT "B"
  1834.    END IF
  1835.    LOCATE VLINE, 9
  1836.    PRINT "V"
  1837.    LOCATE ALINE, 3
  1838.    PRINT "A"
  1839.    LOCATE ALINE, 24
  1840.    PRINT "D"
  1841.    LOCATE ELINE, 3
  1842.    PRINT "E"
  1843.    LOCATE ELINE, 24
  1844.    PRINT "Q"
  1845.    IF TablesRec.TableNum = cBookStockTableNum THEN
  1846.      LOCATE CLINE, 3
  1847.      PRINT "O"
  1848.      LOCATE CLINE, 24
  1849.      PRINT "I"
  1850.    END IF
  1851.    IF TableDone = TRUE THEN
  1852.  
  1853.      LOCATE NLINE, 3
  1854.      PRINT " No Next Record"
  1855.    ELSE
  1856.      LOCATE NLINE, 3
  1857.      PRINT "N "
  1858.      COLOR FOREGROUND, BACKGROUND
  1859.      LOCATE NLINE, 5
  1860.      PRINT "= "
  1861.      LOCATE NLINE, 6
  1862.      PRINT " Next Record"
  1863.    END IF
  1864.    IF TableStart = TRUE THEN
  1865.      COLOR ForeGrnd, BACKGROUND
  1866.      LOCATE NLINE, 20
  1867.      PRINT " No Previous Record"
  1868.    ELSE
  1869.      COLOR ForeGrnd, BACKGROUND
  1870.      LOCATE NLINE, 20
  1871.      PRINT "    P "
  1872.      COLOR FOREGROUND, BACKGROUND
  1873.      LOCATE NLINE, 26
  1874.      PRINT "= "
  1875.      LOCATE NLINE, 27
  1876.      PRINT " Previous   "
  1877.      END IF
  1878.    COLOR FOREGROUND, BACKGROUND
  1879.  END SUB
  1880.  
  1881.  '**************************************************************************
  1882.  '*  The ShowMessage SUB displays the message string passed in the message *
  1883.  '*  box between the displayed table and the menus. If the Cursor parameter*
  1884.  '*  is 0, no cursor appears in the box; if it is 1, a cursor is displaed. *
  1885.  '*                                 Parameters                             *
  1886.  '*  Message$    Prompt or message to display                              *
  1887.  '*  Cursor      Boolean value telling whether or not to show a cursor     *
  1888.  '**************************************************************************
  1889.  SUB ShowMessage (Message$, Cursor)
  1890.    CALL EraseMessage
  1891.    IF (LEN(Message$) MOD 2) THEN
  1892.          Borderlen = 1
  1893.    END IF
  1894.    MesLen = LEN(Message$)
  1895.    SELECT CASE Cursor                          ' No cursor request means to
  1896.    CASE FALSE                                  ' center the message in box
  1897.      HalfMes = (MesLen \ 2) + 1                ' and display without cursor
  1898.      Start = (SCREENWIDTH \ 2) - HalfMes
  1899.    CASE ELSE
  1900.      Start = 4                                 ' Message is part of an edit
  1901.    END SELECT                                  ' so display flush left, and
  1902.      LOCATE MESBOXTOP, 2                       ' keep cursor visible
  1903.      PRINT "╔"; STRING$(66, CHR$(205)); "╗"
  1904.      LOCATE MESFIELD, 2
  1905.      PRINT "║"; SPACE$(66); "║"
  1906.      LOCATE MESBOXEND, 2
  1907.      PRINT "╚"; STRING$(37, CHR$(205)); "╦"; "═╦"; STRING$(26, CHR$(205)); "╝"
  1908.      COLOR BRIGHT + FOREGROUND, BACKGROUND
  1909.      LOCATE MESFIELD, Start, Cursor
  1910.      PRINT Message$;
  1911.      LOCATE MESFIELD, Start + MesLen, Cursor
  1912.      PRINT "";
  1913.      COLOR FOREGROUND, BACKGROUND
  1914.  END SUB
  1915.  
  1916.  '**************************************************************************
  1917.  '*  The ShowRecord SUB displays the columns of the current record of the  *
  1918.  '*  table being displayed. Numerics are only displayed if they are <> 0.  *
  1919.  '*                                Parameters                              *
  1920.  '*  TablesRec   RecStruct type variable containing table information      *
  1921.  '**************************************************************************
  1922.  SUB ShowRecord (TablesRec AS RecStruct)
  1923.  COLOR FOREGROUND, BACKGROUND
  1924.    SELECT CASE TablesRec.TableNum
  1925.      CASE cBookStockTableNum
  1926.        LOCATE TITLEFIELD, 18: PRINT TablesRec.Inventory.Title
  1927.        LOCATE AUTHORFIELD, 18: PRINT TablesRec.Inventory.Author
  1928.        LOCATE PUBFIELD, 18: PRINT TablesRec.Inventory.Publisher
  1929.        IF TablesRec.Inventory.Edition <> 0 THEN LOCATE EDFIELD, 17: PRINT STR$
  1930.        IF TablesRec.Inventory.Price <> 0 THEN LOCATE PRICEFIELD, 17: PRINT " $
  1931.        IF TablesRec.Inventory.IDnum <> 0 THEN LOCATE IDFIELD, 17: PRINT STR$(T
  1932.      CASE cCardHoldersTableNum
  1933.        LOCATE NAMEFIELD, 18: PRINT TablesRec.Lendee.TheName
  1934.        LOCATE STREETFIELD, 18: PRINT TablesRec.Lendee.Street
  1935.        LOCATE CITYFIELD, 18: PRINT TablesRec.Lendee.City
  1936.        LOCATE STATEFIELD, 18: PRINT TablesRec.Lendee.State
  1937.        IF TablesRec.Lendee.Zip <> 0 THEN LOCATE ZIPFIELD, 17: PRINT STR$(Table
  1938.        IF TablesRec.Lendee.CardNum <> 0 THEN LOCATE CARDNUMFIELD, 17: PRINT ST
  1939.      CASE ELSE
  1940.         CALL ShowMessage("There are no other forms defined", 0)
  1941.    END SELECT
  1942.  END SUB
  1943.  
  1944.  '**************************************************************************
  1945.  '*  The UserChoice SUB is used to echo back to the user the most recent   *
  1946.  '*  menu selection he has made. Not all menu choices are echoed back.     *
  1947.  '*                                Parameters                              *
  1948.  '*  BigRec    RecStruct type variable containing table information        *
  1949.  '*  Row       Row on which to put the Feedback$                           *
  1950.  '*  Column    Column at which to start the Feedback$                      *
  1951.  '*  Feedback$ Menu-choice string to highlight                             *
  1952.  '**************************************************************************
  1953.  SUB UserChoice (BigRec AS RecStruct, Row, Column, Feedback$)
  1954.      CALL DrawHelpKeys(BigRec.TableNum)
  1955.      CALL ShowKeys(BigRec, BRIGHT + FOREGROUND, DimN, DimP)
  1956.      COLOR FOREGROUND + BRIGHT, BACKGROUND
  1957.      LOCATE Row, Column
  1958.      PRINT Feedback$
  1959.      COLOR FOREGROUND, BACKGROUND
  1960.  END SUB
  1961.  
  1962.  
  1963.  
  1964.  BOOKMOD2.BAS
  1965.  CD-ROM Disc Path:   \SAMPCODE\BASIC\BOOKMOD2.BAS
  1966.  
  1967.  '***********************************************************************
  1968.  '*  This is module level code for BOOKMOD2.BAS. It contains procedures *
  1969.  '*  that use ISAM statements as well as procedures that support them.  *
  1970.  '*  It is the third module of the BOOKLOOK program.                    *
  1971.  '***********************************************************************
  1972.  DEFINT A-Z
  1973.  '$INCLUDE: 'booklook.bi'
  1974.  
  1975.  EditMessage:
  1976.  DATA "╔═══════════════════════════╗"
  1977.  DATA "║ A log is being kept while ║"
  1978.  DATA "║ you edit fields in this   ║"
  1979.  DATA "║ record. Press U to undo   ║"
  1980.  DATA "║ each preceding edit, or   ║"
  1981.  DATA "║ CTRL+U to undo all of the ║"
  1982.  DATA "║ pending edits as a group. ║"
  1983.  DATA "║                           ║"
  1984.  DATA "╚═════╡ To Undo Edits ╞═════╝"
  1985.  
  1986.  OperandBox:
  1987.  DATA "╔═══════════════════════════╗"
  1988.  DATA "║                           ║"
  1989.  DATA "║ Greater Than              ║"
  1990.  DATA "║ or                        ║"
  1991.  DATA "║ Equal To     Value Entered║"
  1992.  DATA "║ or                        ║"
  1993.  DATA "║ Less Than                 ║"
  1994.  DATA "║                           ║"
  1995.  DATA "╚══╡ Relationship to Key ╞══╝"
  1996.  
  1997.  '************************************************************************
  1998.  '*                                                                      *
  1999.  '*  This SUB checks the real current index after a try to set an index. *
  2000.  '*  If the index was successfully set, it's name is displayed, other-   *
  2001.  '*  wise the current index is displayed. IndexBox is called to update   *
  2002.  '*  Current Sorting Order box on the screen.                            *
  2003.  '*                                                                      *
  2004.  '************************************************************************
  2005.  SUB AdjustIndex (TablesRec AS RecStruct)
  2006.    RealIndexName$ = GETINDEX$(TablesRec.TableNum)
  2007.    CALL Indexbox(TablesRec, CheckIndex%(TablesRec, 0))
  2008.    IF RealIndexName$ <> EMPTYSTRING THEN
  2009.      Alert$ = "Records are now ordered by the index called " + RealIndexName$
  2010.    ELSE
  2011.      Alert$ = "Records now ordered by the default (NULL) index"
  2012.    END IF
  2013.    CALL ShowMessage(Alert$, 0)
  2014.  END SUB
  2015.  
  2016.  '***************************************************************************
  2017.  '*  The ChangeRecord FUNCTION gets the new field value with MakeString. It *
  2018.  '*  then assigns the value (converted if necessary) to its proper element  *
  2019.  '*  in the recordvariable (TablesRec) used to update the table.            *
  2020.  '*                                Parameters                               *
  2021.  '*  FirstLetter   If the user has started typing, this contains a letter   *
  2022.  '*  Argument      Tells what field the cursor is currently in              *
  2023.  '*  TablesRec     RecStruct type variable holding all table information    *
  2024.  '*  Task          Tells which operation is being performed                 *
  2025.  '***************************************************************************
  2026.  FUNCTION ChangeRecord (FirstLetter$, Argument, TablesRec AS RecStruct, Task A
  2027.    STATIC SaveTitle AS STRING
  2028.    Prompt$ = "New Field Value: "
  2029.  
  2030.    IF Task <> SEEKFIELD THEN            ' Adjust the Argument --- It is in-
  2031.      IF Argument = TITLEFIELD THEN      ' cremented as part of PlaceCursor.
  2032.        Argument = IDFIELD               ' But it needs the user's original
  2033.      ELSE                               ' choice in this function.
  2034.         Argument = Argument - 2
  2035.      END IF
  2036.    END IF
  2037.  
  2038.    Filter% = ASC(FirstLetter$)                ' Convert FirstLetter$ to ascii
  2039.    Remainder$ = MakeString$(Filter%, Prompt$) ' number to pass to MakeString.
  2040.    IF Filter% = ESCAPE THEN                   ' This lets the user press ESC
  2041.      ChangeRecord = 0                         ' to abandon function.
  2042.      CALL ShowRecord(TablesRec)
  2043.      EXIT FUNCTION
  2044.    END IF
  2045.                                             ' Select for proper assignment of
  2046.    SELECT CASE Argument                     ' string user makes with MakeStrin
  2047.      CASE TITLEFIELD, NAMEFIELD
  2048.        IF Task = EDITRECORD OR Task = ADDRECORD OR Task = SEEKFIELD THEN
  2049.          IF TablesRec.TableNum = cBookStockTableNum THEN
  2050.            TablesRec.Inventory.Title = Remainder$
  2051.          ELSE
  2052.            TablesRec.Lendee.TheName = Remainder$
  2053.          END IF
  2054.        END IF
  2055.        COLOR FOREGROUND, BACKGROUND
  2056.      CASE AUTHORFIELD, STREETFIELD
  2057.        IF Task = EDITRECORD OR Task = ADDRECORD THEN
  2058.          IF TablesRec.TableNum = cBookStockTableNum THEN
  2059.            TablesRec.Inventory.Author = Remainder$
  2060.          ELSE
  2061.            TablesRec.Lendee.Street = Remainder$
  2062.          END IF
  2063.        END IF
  2064.        COLOR FOREGROUND, BACKGROUND
  2065.      CASE PUBFIELD, CITYFIELD
  2066.        IF Task = EDITRECORD OR Task = ADDRECORD THEN
  2067.          IF TablesRec.TableNum = cBookStockTableNum THEN
  2068.            TablesRec.Inventory.Publisher = Remainder$
  2069.          ELSE
  2070.            TablesRec.Lendee.City = Remainder$
  2071.          END IF
  2072.        END IF
  2073.        COLOR FOREGROUND, BACKGROUND
  2074.      CASE EDFIELD, STATEFIELD
  2075.        IF Task = EDITRECORD OR Task = ADDRECORD THEN
  2076.          IF TablesRec.TableNum = cBookStockTableNum THEN
  2077.            TablesRec.Inventory.Edition = VAL(Remainder$)
  2078.          ELSE
  2079.            TablesRec.Lendee.State = Remainder$
  2080.          END IF
  2081.        END IF
  2082.        COLOR FOREGROUND, BACKGROUND
  2083.      CASE PRICEFIELD, ZIPFIELD
  2084.        IF Task = EDITRECORD OR Task = ADDRECORD THEN
  2085.          IF TablesRec.TableNum = cBookStockTableNum THEN
  2086.            TablesRec.Inventory.Price = VAL(Remainder$)
  2087.          ELSE
  2088.            TablesRec.Lendee.Zip = VAL(Remainder$)
  2089.          END IF
  2090.        END IF
  2091.        COLOR FOREGROUND, BACKGROUND
  2092.      CASE IDFIELD, CARDNUMFIELD
  2093.        IF Task = EDITRECORD OR Task = ADDRECORD THEN
  2094.          IF TablesRec.TableNum = cBookStockTableNum THEN
  2095.            size = LEN(Remainder$)
  2096.            FOR counter = 1 TO size
  2097.              IF ASC(MID$(Remainder$, counter, 1)) = 0 THEN
  2098.                Remainder$ = MID$(Remainder$, (counter + 1), size)
  2099.              END IF
  2100.            NEXT counter
  2101.            TablesRec.Inventory.IDnum = VAL(LTRIM$(RTRIM$(Remainder$)))
  2102.          ELSE
  2103.            TablesRec.Lendee.CardNum = VAL(Remainder$)
  2104.          END IF
  2105.        END IF
  2106.        COLOR FOREGROUND, BACKGROUND
  2107.      CASE ELSE
  2108.          CALL ShowMessage("  Can't change that field ", 0)
  2109.          BEEP
  2110.          SLEEP 1
  2111.  END SELECT
  2112.   ChangeRecord = 1
  2113.  END FUNCTION
  2114.  
  2115.  '***************************************************************************
  2116.  '*  The CheckIndex uses the GETINDEX function to find the current index.   *
  2117.  '*  Since only some displayed fields correspond to indexes, the number     *
  2118.  '*  returned is a code indicating what to do, not the index name           *
  2119.  '*                                Parameters                               *
  2120.  '*  TablesRec   RecStuct type variable holding all table information       *
  2121.  '*  FirstTime   If first time is TRUE, Index is NULL index                 *
  2122.  '***************************************************************************
  2123.  FUNCTION CheckIndex% (TablesRec AS RecStruct, FirstTime)
  2124.    Check$ = GETINDEX$(TablesRec.TableNum)
  2125.    SELECT CASE Check$
  2126.      CASE "TitleIndexBS", "NameIndexCH"
  2127.        CheckIndex% = 0
  2128.      CASE "AuthorIndexBS"
  2129.        CheckIndex% = 1
  2130.      CASE "PubIndexBS"
  2131.        CheckIndex% = 2
  2132.      CASE "StateIndexCH"
  2133.        CheckIndex% = 3
  2134.      CASE "ZipIndexCH"
  2135.        CheckIndex% = 4
  2136.      CASE "IDIndex", "CardNumIndexCH"
  2137.        CheckIndex% = 5
  2138.      CASE "BigIndex"                 ' There's no combined index on
  2139.        CheckIndex% = 6               ' CardHolders table
  2140.      CASE ""
  2141.        CheckIndex% = 7               ' This is a special case for the
  2142.                                      ' Blank line in CardHolders table
  2143.      IF FirstTime% THEN
  2144.        CALL Indexbox(TablesRec, 7)
  2145.      END IF
  2146.    END SELECT
  2147.  END FUNCTION
  2148.  
  2149.  '***************************************************************************
  2150.  '*  The EdAddCursor function is used to place the cursor in the proper     *
  2151.  '*  when the task is to Edit or Add a record.  Note when printing numeric  *
  2152.  '*  fields LOCATE 1 column left to compensate  for the implicit "+" sign.  *
  2153.  '*                                Parameters                               *
  2154.  '*  NextField   Tells which field is to be highlighted next                *
  2155.  '*  Job         Tells operation user wants to engage in                    *
  2156.  '*  TablesRec   RecStruct type variable holding all table information      *
  2157.  '*  FirstShot   Nonzero value indicates this is first time through         *
  2158.  '***************************************************************************
  2159.  FUNCTION EdAddCursor (NextField%, Job%, TablesRec AS RecStruct, FirstShot%)
  2160.    SELECT CASE TablesRec.TableNum
  2161.      CASE cBookStockTableNum                       ' BookStock table is 1
  2162.        SELECT CASE NextField
  2163.          CASE TITLEFIELD, NAMEFIELD
  2164.            LOCATE IDFIELD, 17
  2165.            IF FirstShot THEN COLOR FOREGROUND, BACKGROUND
  2166.            PRINT TablesRec.Inventory.IDnum
  2167.            COLOR BACKGROUND, BRIGHT + FOREGROUND
  2168.            LOCATE TITLEFIELD, 18
  2169.            PRINT TablesRec.Inventory.Title
  2170.            NextField% = AUTHORFIELD
  2171.          CASE AUTHORFIELD, STREETFIELD
  2172.            LOCATE TITLEFIELD, 18
  2173.            PRINT TablesRec.Inventory.Title
  2174.            COLOR BACKGROUND, BRIGHT + FOREGROUND
  2175.            LOCATE AUTHORFIELD, 18
  2176.            PRINT TablesRec.Inventory.Author
  2177.            NextField% = PUBFIELD
  2178.          CASE PUBFIELD, CITYFIELD
  2179.            LOCATE AUTHORFIELD, 18
  2180.            PRINT TablesRec.Inventory.Author
  2181.            COLOR BACKGROUND, BRIGHT + FOREGROUND
  2182.            LOCATE PUBFIELD, 18
  2183.              PRINT TablesRec.Inventory.Publisher
  2184.              NextField% = EDFIELD
  2185.          CASE EDFIELD, STATEFIELD
  2186.            LOCATE PUBFIELD, 18
  2187.            PRINT TablesRec.Inventory.Publisher
  2188.            COLOR BACKGROUND, BRIGHT + FOREGROUND
  2189.            LOCATE EDFIELD, 17
  2190.            PRINT TablesRec.Inventory.Edition
  2191.            NextField% = PRICEFIELD
  2192.          CASE PRICEFIELD, ZIPFIELD
  2193.            LOCATE EDFIELD, 17
  2194.            PRINT TablesRec.Inventory.Edition
  2195.            COLOR BACKGROUND, BRIGHT + FOREGROUND
  2196.            LOCATE PRICEFIELD, 19
  2197.            PRINT ; TablesRec.Inventory.Price
  2198.            NextField% = IDFIELD
  2199.          CASE IDFIELD, CARDNUMFIELD
  2200.            LOCATE PRICEFIELD, 18
  2201.            PRINT "$"; TablesRec.Inventory.Price
  2202.            COLOR BACKGROUND, BRIGHT + FOREGROUND
  2203.            LOCATE IDFIELD, 17
  2204.            PRINT TablesRec.Inventory.IDnum
  2205.            NextField% = TITLEFIELD
  2206.        END SELECT
  2207.      CASE cCardHoldersTableNum                       ' CardHolders table is 2
  2208.        SELECT CASE NextField
  2209.          CASE NAMEFIELD
  2210.            LOCATE CARDNUMFIELD, 17
  2211.            IF FirstShot THEN COLOR FOREGROUND, BACKGROUND
  2212.            PRINT TablesRec.Lendee.CardNum
  2213.            COLOR BACKGROUND, BRIGHT + FOREGROUND
  2214.            LOCATE NAMEFIELD, 18
  2215.            PRINT TablesRec.Lendee.TheName
  2216.            NextField% = STREETFIELD
  2217.          CASE STREETFIELD
  2218.            LOCATE NAMEFIELD, 18
  2219.            PRINT TablesRec.Lendee.TheName
  2220.            COLOR BACKGROUND, BRIGHT + FOREGROUND
  2221.            LOCATE STREETFIELD, 18
  2222.            PRINT TablesRec.Lendee.Street
  2223.            NextField% = CITYFIELD
  2224.          CASE CITYFIELD
  2225.            LOCATE STREETFIELD, 18
  2226.            PRINT TablesRec.Lendee.Street
  2227.            COLOR BACKGROUND, BRIGHT + FOREGROUND
  2228.            LOCATE CITYFIELD, 18
  2229.            PRINT TablesRec.Lendee.City
  2230.            NextField% = STATEFIELD
  2231.          CASE STATEFIELD
  2232.            LOCATE CITYFIELD, 18
  2233.            PRINT TablesRec.Lendee.City
  2234.            COLOR BACKGROUND, BRIGHT + FOREGROUND
  2235.            LOCATE STATEFIELD, 18
  2236.            PRINT TablesRec.Lendee.State
  2237.            NextField% = PRICEFIELD
  2238.          CASE ZIPFIELD
  2239.            LOCATE STATEFIELD, 18
  2240.            PRINT TablesRec.Lendee.State
  2241.            COLOR BACKGROUND, BRIGHT + FOREGROUND
  2242.            LOCATE ZIPFIELD, 17
  2243.            PRINT TablesRec.Lendee.Zip
  2244.            NextField% = IDFIELD
  2245.          CASE CARDNUMFIELD
  2246.            LOCATE ZIPFIELD, 17
  2247.            PRINT TablesRec.Lendee.Zip
  2248.            COLOR BACKGROUND, BRIGHT + FOREGROUND
  2249.            LOCATE CARDNUMFIELD, 17
  2250.            PRINT TablesRec.Lendee.CardNum
  2251.            NextField% = TITLEFIELD
  2252.        END SELECT
  2253.    END SELECT
  2254.    COLOR FOREGROUND, BACKGROUND
  2255.  END FUNCTION
  2256.  
  2257.  '***************************************************************************
  2258.  '*  The EditField function lets the user choose whether or not to actually *
  2259.  '*  change the current field (by calling ChangeRecord) or move on to the   *
  2260.  '*  next field. It also displays a message telling how to Undo edits. If   *
  2261.  '*  EditField returns TRUE, a SAVEPOINT is set at module level. If the task*
  2262.  '*  is ADDRECORD, the user is taken through the fields one at a time until *
  2263.  '*  they have all been entered.                                            *
  2264.  '*                              Parameters                                 *
  2265.  '*  Argument    Tells which field is currently being dealt with            *
  2266.  '*  TablesRec   RecStruct type variable holding current table information  *
  2267.  '*  FirstLetter If the user has started typing, the letter is passed in    *
  2268.  '*  Task        Tells what type of operation the user is performing        *
  2269.  '*  Answer      Same as Task, but passed to ChangeRecord
  2270.  '***************************************************************************
  2271.  FUNCTION EditField (Argument%, TablesRec AS RecStruct, FirstLetter$, Task%, A
  2272.    ' Show the transaction block message dealing with undoing edits:
  2273.    IF Task = EDITRECORD THEN CALL DrawIndexBox(1, Task)
  2274.  
  2275.    STATIC NextField
  2276.    FirstLetter$ = ""
  2277.    IF Task = EDITRECORD THEN CALL ShowMessage("Edit this field or TAB to anoth
  2278.    Argument = TITLEFIELD
  2279.    Value = PlaceCursor(Argument, TablesRec, FirstLetter$, 1, Task%)
  2280.    IF Argument THEN
  2281.      IF Task = EDITRECORD THEN CALL ShowMessage("Edit this field or TAB to ano
  2282.      COLOR FOREGROUND, BACKGROUND
  2283.      WasFieldChanged = ChangeRecord(FirstLetter$, Argument, TablesRec, Answer)
  2284.  
  2285.      IF Task = EDITRECORD AND WasFieldChanged <> 0 THEN
  2286.        CALL ShowMessage("Press E to Edit another field ", 0)
  2287.        EditField = TRUE            ' If True is returned, a SAVEPOINT is set
  2288.      ELSEIF Task = EDITRECORD AND WasFieldChanged = 0 THEN
  2289.        CALL ShowRecord(TablesRec)
  2290.        CALL ShowMessage("Please try again...", 0)
  2291.        EditField = FALSE     'Don't set SAVEPOINT if user escapes from edit
  2292.      ELSEIF Task = SEEKFIELD THEN
  2293.        EditField = FALSE: EXIT FUNCTION
  2294.      END IF
  2295.      IF Task = ADDRECORD THEN
  2296.        NextField = 1
  2297.        DO WHILE NextField <> 0 AND Argument <> 0
  2298.          CALL ShowMessage("Enter value for field or ESC to abandon addition ",
  2299.          SELECT CASE NextField
  2300.            CASE 1
  2301.              Argument = AUTHORFIELD
  2302.              FieldsDone = FieldsDone + 1
  2303.            CASE 2
  2304.              Argument = PUBFIELD
  2305.              FieldsDone = FieldsDone + 1
  2306.            CASE 3
  2307.              Argument = EDFIELD
  2308.              FieldsDone = FieldsDone + 1
  2309.            CASE 4
  2310.              Argument = PRICEFIELD
  2311.              FieldsDone = FieldsDone + 1
  2312.            CASE 5
  2313.              Argument = IDFIELD
  2314.              FieldsDone = FieldsDone + 1
  2315.              NextField = 0
  2316.            CASE ELSE
  2317.              CALL ShowMessage("Problem in the CASE assignments to Argument", 0
  2318.          END SELECT
  2319.          FirstLetter$ = ""
  2320.          Value = PlaceCursor(Argument, TablesRec, FirstLetter$, 1, Task%)
  2321.          IF Argument THEN
  2322.            COLOR FOREGROUND, BACKGROUND
  2323.            WasFieldChanged = ChangeRecord(FirstLetter$, Argument, TablesRec, A
  2324.            NextField = NextField + 1
  2325.            IF FieldsDone = 5 THEN EditField% = 1: EXIT FUNCTION
  2326.          END IF
  2327.        LOOP
  2328.        EditField = FALSE 'No need for SAVEPOINT with ADDRECORD
  2329.      END IF
  2330.    ELSE
  2331.    CALL ShowRecord(TablesRec)
  2332.    CALL ShowMessage("Please try again...", 0)
  2333.    SLEEP: CALL EraseMessage
  2334.    CALL DrawIndexBox(TablesRec.TableNum, 0)' Replace Edit stuff with Index stu
  2335.    EditField = FALSE     'Don't set SAVEPOINT if user escapes from edit
  2336.    END IF
  2337.  
  2338.  END FUNCTION
  2339.  
  2340.  '***************************************************************************
  2341.  '*  The GetKeyVals SUB gathers the Keys for searching on a combined index. *
  2342.  '*  It shows the fields as they are entered.                               *
  2343.  '*                                Parameters                               *
  2344.  '*  TablesRec   Contains all the information for the tables                *
  2345.  '*  Key1        Represents the Title field of BookStock table              *
  2346.  '*  Key2        Represents the Author field of BookStock table             *
  2347.  '*  Key3        Represents the IDnum field of BookStock table              *
  2348.  '*  Letter      Holds the first letter the user tries to enter at prompt   *
  2349.  '***************************************************************************
  2350.  SUB GetKeyVals (TablesRec AS RecStruct, Key1$, Key2$, Key3#, Letter$)
  2351.    WhichTable = TablesRec.TableNum
  2352.    Prompt$ = "Value to Seek: "
  2353.  
  2354.    CALL DrawScreen(WhichTable)
  2355.    DO
  2356.      ' Have the user ENTER the Title value to search for
  2357.      COLOR BACKGROUND, FOREGROUND
  2358.      LOCATE TITLEFIELD, 18
  2359.      PRINT "Please enter the Title to find"
  2360.      Key1$ = MakeString$(ASC(Letter$), Prompt$)
  2361.      CALL ShowIt(TablesRec, "TitleIndexBS", WhichTable, Key1$)
  2362.    LOOP UNTIL Key1$ <> ""
  2363.  
  2364.    Letter$ = " "    ' Set it to a blank space for typing
  2365.  
  2366.      ' Have the user ENTER the Author value to search for
  2367.    DO
  2368.      COLOR BACKGROUND, FOREGROUND
  2369.      LOCATE AUTHORFIELD, 18
  2370.      PRINT "Please enter the Author name to find"
  2371.      Key2$ = MakeString$(ASC(Letter$), Prompt$)
  2372.      ' Show it just shows the input user has entered, not a record from file
  2373.      CALL ShowIt(TablesRec, "AuthorIndexBS", WhichTable, Key2$)
  2374.    LOOP UNTIL Key2$ <> ""
  2375.  
  2376.    Letter$ = " "    ' Set it to a blank space for typing
  2377.      ' Have the user ENTER the ID number value to search for
  2378.    DO
  2379.      COLOR BACKGROUND, FOREGROUND
  2380.      LOCATE IDFIELD, 18
  2381.      PRINT "Please enter the ID number to find"
  2382.      ValueToSeek$ = MakeString$(ASC(Letter$), Prompt$)
  2383.      Key3# = CDBL(VAL(ValueToSeek$))       ' CURRENCY field
  2384.      CALL ShowIt(TablesRec, "IDIndex", WhichTable, ValueToSeek$)
  2385.  LOOP UNTIL Key3# <> 0
  2386.  END SUB
  2387.  
  2388.  '****************************** GetOperand FUNCTION ************************
  2389.  '* The GetOperand FUNCTION displays a choice of operators to allow user a  *
  2390.  '* choice in how a SEEKoperand search will be conducted. If the user makes *
  2391.  '* a valid choice, it is assigned to HoldOperand. An invalid choice or a   *
  2392.  '* choice of ESC results in "<>" being passed back. This permits an exit   *
  2393.  '* from the function (which is recursive). Otherwise, the user's choice is *
  2394.  '* trapped in HoldOperand when ENTER is pressed.                           *
  2395.  '* Note that this function is recursive so use the calls menu to keep      *
  2396.  '* track of the nesting depth when stepping through it. Unlike PlaceCursor *
  2397.  '* GetOperand doesn't keep track of the stack - the stack set should be OK.*
  2398.  '*                              Parameters                                 *
  2399.  '*   HoldOperand    Contains operand to check each time function calls     *
  2400.  '*                  itself; Let's user ESC from function if desired.       *
  2401.  '***************************************************************************
  2402.  FUNCTION GetOperand% (HoldOperand$)
  2403.    STATIC WhichOne     ' Keep track of which case from call to call
  2404.  
  2405.    ' If user has chose ESC then exit back to caller
  2406.    IF HoldOperand$ = "<>" THEN WhichOne = 0: EXIT FUNCTION
  2407.  
  2408.    ' if this is the first time through the function then
  2409.    ' Replace the Sort Order box with box of operand choices
  2410.    IF WhichOne = 0 THEN
  2411.      RESTORE OperandBox
  2412.      FOR Row = BOXTOP TO BOXEND
  2413.        LOCATE Row, 42
  2414.        READ Temp$
  2415.        PRINT Temp$
  2416.        IF Row = BOXEND THEN
  2417.          COLOR FOREGROUND + BRIGHT, BACKGROUND
  2418.          LOCATE Row, INDBOX + 5
  2419.          PRINT "Relationship to Key"
  2420.        END IF
  2421.      NEXT Row
  2422.      LOCATE VLINE, 44
  2423.      PRINT "Equal To     Value Entered"     ' This is default --- if user
  2424.      COLOR FOREGROUND, BACKGROUND           ' presses ENTER without tabbing,
  2425.    END IF                                   ' SeekRecord sets the operand
  2426.                                             ' to =    Note: a more flexible
  2427.                                             ' default choice might be >=
  2428.  
  2429.    Alert$ = "Now press TAB to select how search should be conducted"
  2430.    CALL ShowMessage(Alert$, 0)
  2431.    DO
  2432.    Answer$ = INKEY$
  2433.    LOOP WHILE Answer$ <> CHR$(TABKEY) AND Answer$ <> CHR$(ENTER) AND Answer$ <
  2434.  
  2435.    IF LEN(Answer$) = 1 THEN
  2436.      SELECT CASE ASC(Answer$)
  2437.        CASE TABKEY
  2438.          SELECT CASE WhichOne
  2439.            CASE 0
  2440.              COLOR FOREGROUND, BACKGROUND
  2441.              LOCATE VLINE, 44
  2442.              PRINT "Equal To"
  2443.              COLOR BRIGHT + FOREGROUND, BACKGROUND
  2444.              LOCATE RLINE, 44
  2445.              PRINT "Greater Than"
  2446.              WhichOne = WhichOne + 1
  2447.              HoldOperand$ = ">"
  2448.            CASE 1
  2449.              COLOR BRIGHT + FOREGROUND, BACKGROUND
  2450.              LOCATE VLINE, 44
  2451.              PRINT "Equal To"
  2452.              LOCATE WLINE, 44
  2453.              PRINT "or"
  2454.              WhichOne = WhichOne + 1
  2455.              HoldOperand$ = ">="
  2456.            CASE 2
  2457.              COLOR FOREGROUND, BACKGROUND
  2458.              LOCATE RLINE, 44
  2459.              PRINT "Greater Than"
  2460.              LOCATE WLINE, 44
  2461.              PRINT "or"
  2462.              COLOR BRIGHT + FOREGROUND, BACKGROUND
  2463.              LOCATE ALINE, 44
  2464.              PRINT "or"
  2465.              LOCATE ELINE, 44
  2466.              PRINT "Less Than"
  2467.              WhichOne = WhichOne + 1
  2468.              HoldOperand$ = "<="
  2469.            CASE 3
  2470.              COLOR FOREGROUND, BACKGROUND
  2471.              LOCATE VLINE, 44
  2472.              PRINT "Equal To"
  2473.              LOCATE ALINE, 44
  2474.              PRINT "or"
  2475.              WhichOne = WhichOne + 1
  2476.              HoldOperand$ = "<"
  2477.              SLEEP
  2478.            CASE 4
  2479.              COLOR FOREGROUND, BACKGROUND
  2480.              LOCATE ELINE, 44
  2481.              PRINT "Less Than"
  2482.              COLOR BRIGHT + FOREGROUND, BACKGROUND
  2483.              LOCATE VLINE, 44
  2484.              PRINT "Equal To     Value Entered"
  2485.              WhichOne = WhichOne + 1
  2486.              HoldOperand$ = "="
  2487.            CASE ELSE
  2488.          END SELECT                          ' If no choice was made, call
  2489.          IF WhichOne > 4 THEN WhichOne = 0   ' GetOperand again
  2490.          COLOR FOREGROUND, BACKGROUND
  2491.          OK = GetOperand%(HoldOperand$)
  2492.        CASE ENTER
  2493.          WhichOne = 0
  2494.          EXIT FUNCTION
  2495.      CASE ESCAPE                 ' If user chooses ESC, signal the function
  2496.        HoldOperand$ = "<>"       ' to exit and keep exiting back through
  2497.        GetOperand% = 0           ' all levels of recursion
  2498.        WhichOne = 0
  2499.      CASE ELSE                   ' If user chooses invalid key, try again
  2500.        BEEP
  2501.        CALL ShowMessage("Use TAB to select relationship to search for...", 0)
  2502.        COLOR white, BACKGROUND
  2503.        OK = GetOperand%(HoldOperand$)
  2504.    END SELECT
  2505.  ELSE
  2506.  END IF
  2507.  
  2508.  END FUNCTION
  2509.  
  2510.  '***************************************************************************
  2511.  '*  The IndexBox SUB highlights the proper index name in the Current Index *
  2512.  '*  box at the bottom right section of the screen.                         *
  2513.  '                                                                          *
  2514.  '*  TablesRec   RecStruct type variable containing all table information   *
  2515.  '*  MoveDown    Integer representing line on which index name resides      *
  2516.  '***************************************************************************
  2517.  SUB Indexbox (TablesRec AS RecStruct, MoveDown)
  2518.     Table = TablesRec.TableNum
  2519.     COLOR BRIGHT + FOREGROUND, BACKGROUND
  2520.     LOCATE 17 + MoveDown, 44
  2521.     SELECT CASE MoveDown
  2522.       CASE 0
  2523.        IF Table = cBookStockTableNum THEN PRINT "By Titles   " ELSE PRINT "By
  2524.        COLOR FOREGROUND, BACKGROUND
  2525.        LOCATE ELINE, 44
  2526.        PRINT "Default = Insertion Order"
  2527.       CASE 1
  2528.        IF Table = cBookStockTableNum THEN PRINT "By Authors   "
  2529.        COLOR FOREGROUND, BACKGROUND
  2530.        LOCATE NLINE, 44
  2531.        IF Table = cBookStockTableNum THEN PRINT "By Titles   " ELSE PRINT "By
  2532.       CASE 2
  2533.        IF Table = cBookStockTableNum THEN PRINT "By Publishers   "
  2534.        COLOR FOREGROUND, BACKGROUND
  2535.        LOCATE RLINE, 44
  2536.        IF Table = cBookStockTableNum THEN PRINT "By Authors    "
  2537.       CASE 3
  2538.        IF Table = cCardHoldersTableNum THEN
  2539.          LOCATE RLINE, 44
  2540.          PRINT "By States     "
  2541.          COLOR FOREGROUND, BACKGROUND
  2542.          LOCATE NLINE, 44
  2543.          PRINT "By Names     "
  2544.        ELSE
  2545.          COLOR FOREGROUND, BACKGROUND
  2546.          LOCATE WLINE, 44
  2547.          PRINT "By Publishers"
  2548.        END IF
  2549.       CASE 4
  2550.        IF Table = cCardHoldersTableNum THEN
  2551.          LOCATE WLINE, 44
  2552.          PRINT "By Zipcodes   "
  2553.          COLOR FOREGROUND, BACKGROUND
  2554.          LOCATE RLINE, 44
  2555.          PRINT "By States     "
  2556.        END IF
  2557.       CASE 5
  2558.        LOCATE VLINE, 44
  2559.        IF Table = cBookStockTableNum THEN
  2560.          PRINT "By ID Numbers   "
  2561.          COLOR FOREGROUND, BACKGROUND
  2562.        ELSE
  2563.          PRINT "By Card numbers   "
  2564.          COLOR FOREGROUND, BACKGROUND
  2565.          LOCATE WLINE, 44
  2566.          PRINT "By Zipcodes    "
  2567.        END IF
  2568.       CASE 6
  2569.        IF Table = cBookStockTableNum THEN
  2570.          LOCATE ALINE, 44
  2571.          PRINT "By Title + Author + ID"
  2572.          COLOR FOREGROUND, BACKGROUND
  2573.          LOCATE VLINE, 44
  2574.          PRINT "By ID Numbers"
  2575.        ELSE
  2576.          LOCATE VLINE, 44
  2577.          COLOR FOREGROUND, BACKGROUND
  2578.          PRINT "By Card numbers   "
  2579.        END IF
  2580.       COLOR FOREGROUND, BACKGROUND
  2581.       CASE 7
  2582.        LOCATE ELINE, 44
  2583.        PRINT "Default = Insertion Order"
  2584.        COLOR FOREGROUND, BACKGROUND
  2585.        IF Table = cBookStockTableNum THEN
  2586.          LOCATE ALINE, 44
  2587.          PRINT "By Title + Author + ID"
  2588.        ELSE
  2589.          LOCATE VLINE, 44
  2590.          PRINT "By Card numbers"
  2591.        END IF
  2592.      END SELECT
  2593.     IF MoveDown < 7 THEN
  2594.      MoveDown = MoveDown + 1
  2595.     ELSE
  2596.      MoveDown = 0
  2597.     END IF
  2598.  COLOR FOREGROUND, BACKGROUND
  2599.  END SUB
  2600.  
  2601.  '***************************************************************************
  2602.  '* The OrderCursor FUNCTION returns TRUE or FALSE for user index choice.   *
  2603.  '* Each time the user places the cursor on an Index to sort on, this       *
  2604.  '* function displays an instruction message in the field(s) corresponding  *
  2605.  '* to the Index, It then associates the highlighted index name (in the     *
  2606.  '* Sorting Order box) with the name it is known by in the program, and     *
  2607.  '* places that name in the .WhichIndex element of a structured variable of *
  2608.  '* RecStruct type.                                                         *
  2609.  '*                                   Parameters:                           *
  2610.  '* Index       Integer telling which index user has highlighted            *
  2611.  '* NextField   Manifest Constant telling big cursor field position         *
  2612.  '* Job         Manifest Constant indicating task being performed           *
  2613.  '* TablesRec   Variable of RecStruct type, whose .WhichInded element is    *
  2614.  '*             used to return the index name to be used by SETINDEX.       *
  2615.  '***************************************************************************
  2616.  FUNCTION OrderCursor (Index%, NextField%, Job%, TablesRec AS RecStruct, Lette
  2617.    OrderCursor = FALSE
  2618.    CALL Indexbox(TablesRec, Index)         ' Light up the new index
  2619.    COLOR BACKGROUND, BRIGHT + FOREGROUND   ' in Sorting Order box
  2620.    LOCATE NextField, 18
  2621.    IF Job = REORDER THEN         ' Tell the user what is expected of him
  2622.  
  2623.      IF TablesRec.TableNum = cBookStockTableNum THEN
  2624.        IF NextField <> PRICEFIELD AND NextField <> EDFIELD THEN
  2625.          PRINT "Press enter to resort, or TAB to move on"
  2626.        ELSE
  2627.          LOCATE NextField, 20 '19
  2628.          PRINT "Sorry, cannot sort on an unindexed field"
  2629.        END IF
  2630.      ELSE
  2631.        IF NextField <> STREETFIELD AND NextField <> CITYFIELD THEN
  2632.          PRINT "Press enter to resort, or TAB to move on"
  2633.        ELSE
  2634.          PRINT "Sorry, cannot sort on an unindexed field"
  2635.        END IF
  2636.      END IF
  2637.     END IF
  2638.  
  2639.          ' The following places the name of the index to sort on in the
  2640.          ' WhichIndex element of the structured variable TablesRec --- it
  2641.          ' retrieved at the module-level code
  2642.  
  2643.          LOCATE NextField, 18
  2644.          SELECT CASE NextField
  2645.            CASE TITLEFIELD, NAMEFIELD
  2646.              IF Job = SEEKFIELD THEN
  2647.                IF TablesRec.TableNum = cBookStockTableNum THEN
  2648.                  PRINT "Type Title to search for, or press TAB to move on"
  2649.                ELSE
  2650.                  PRINT "Type Name to search for, or press TAB to move on"
  2651.                END IF
  2652.              END IF
  2653.              IF ConfirmEntry%(Letter$) THEN
  2654.                IF TablesRec.TableNum = cBookStockTableNum THEN
  2655.                  TablesRec.WhichIndex = "TitleIndexBS"
  2656.                ELSE
  2657.                  TablesRec.WhichIndex = "NameIndexCH"
  2658.                END IF
  2659.                OrderCursor = TRUE
  2660.                EXIT FUNCTION
  2661.              ELSE
  2662.                OrderCursor = FALSE
  2663.                NextField% = AUTHORFIELD
  2664.              END IF
  2665.            CASE AUTHORFIELD, STREETFIELD
  2666.              IF Job = SEEKFIELD THEN
  2667.                IF TablesRec.TableNum = cBookStockTableNum THEN
  2668.                  PRINT "Type Author name to search for, or TAB to move on"
  2669.                ELSE
  2670.                  PRINT "Sorry, can't search on an unindexed field"
  2671.                END IF
  2672.              END IF
  2673.              IF ConfirmEntry%(Letter$) THEN
  2674.                IF TablesRec.TableNum = cBookStockTableNum THEN
  2675.                  TablesRec.WhichIndex = "AuthorIndexBS"
  2676.                END IF
  2677.                OrderCursor = TRUE
  2678.                EXIT FUNCTION
  2679.              ELSE
  2680.                OrderCursor = FALSE
  2681.                NextField% = PUBFIELD
  2682.              END IF
  2683.            CASE PUBFIELD, CITYFIELD
  2684.              IF Job = SEEKFIELD THEN
  2685.                IF TablesRec.TableNum = cBookStockTableNum THEN
  2686.                  PRINT "Type Publisher name to search for, or TAB to move on"
  2687.                ELSE
  2688.                  PRINT "Sorry, can't search on an unindexed field"
  2689.                END IF
  2690.              END IF
  2691.              IF ConfirmEntry%(Letter$) THEN
  2692.                IF TablesRec.TableNum = cBookStockTableNum THEN
  2693.                  TablesRec.WhichIndex = "PubIndexBS"
  2694.                END IF
  2695.                OrderCursor = TRUE
  2696.                EXIT FUNCTION
  2697.              ELSE
  2698.                OrderCursor = FALSE
  2699.                NextField% = EDFIELD
  2700.              END IF
  2701.            CASE EDFIELD, STATEFIELD
  2702.              IF Job = SEEKFIELD THEN
  2703.                IF TablesRec.TableNum = cCardHoldersTableNum THEN
  2704.                  PRINT "Type State (2 letters), or TAB to move on"
  2705.                ELSE
  2706.                  PRINT "Sorry, can't search on an unindexed field"
  2707.                END IF
  2708.              END IF
  2709.              IF ConfirmEntry%(Letter$) THEN
  2710.                IF TablesRec.TableNum = cCardHoldersTableNum THEN
  2711.                  TablesRec.WhichIndex = "StateIndexCH"
  2712.                END IF
  2713.                OrderCursor = TRUE
  2714.                EXIT FUNCTION
  2715.              ELSE
  2716.                OrderCursor = FALSE
  2717.                NextField% = PRICEFIELD
  2718.              END IF
  2719.            CASE PRICEFIELD, ZIPFIELD
  2720.              IF Job = SEEKFIELD THEN
  2721.                IF TablesRec.TableNum = cCardHoldersTableNum THEN
  2722.                  PRINT "Type Zipcode to search for, or TAB to move on"
  2723.                ELSE
  2724.                  LOCATE PRICEFIELD, 20
  2725.                  PRINT "Sorry, can't search on an unindexed field"
  2726.                END IF
  2727.              END IF
  2728.              IF ConfirmEntry%(Letter$) THEN
  2729.                IF TablesRec.TableNum = cCardHoldersTableNum THEN
  2730.                  TablesRec.WhichIndex = "ZipIndexCH"
  2731.                END IF
  2732.                OrderCursor = TRUE
  2733.                EXIT FUNCTION
  2734.              ELSE
  2735.                OrderCursor = FALSE
  2736.                NextField% = IDFIELD
  2737.              END IF
  2738.            CASE IDFIELD, CARDNUMFIELD
  2739.              IF Job = SEEKFIELD THEN
  2740.                IF TablesRec.TableNum = cBookStockTableNum THEN
  2741.                  PRINT "Type ID number to search for, or TAB to move on"
  2742.                ELSE
  2743.                  PRINT "Type Card number to seek, or press TAB to move on"
  2744.                END IF
  2745.              END IF
  2746.              ' Setting Letter$ to "" may be unnecessary now
  2747.              Letter$ = ""
  2748.              IF ConfirmEntry%(Letter$) THEN
  2749.                IF TablesRec.TableNum = cBookStockTableNum THEN
  2750.                  TablesRec.WhichIndex = "IDIndex"
  2751.                ELSE
  2752.                  TablesRec.WhichIndex = "CardNumIndexCH"
  2753.                END IF
  2754.                OrderCursor = TRUE
  2755.                EXIT FUNCTION
  2756.              ELSE
  2757.                OrderCursor = FALSE
  2758.                NextField% = BIGINDEX
  2759.              END IF
  2760.          END SELECT
  2761.   IF Letter$ = "eScApE" THEN OrderCursor = 3: FirstLetter$ = ""
  2762.  END FUNCTION
  2763.  
  2764.  '***************************************************************************
  2765.  '*  The PlaceCursor FUNCTION lets the user tab around on the displayed form*
  2766.  '*  when performing field-specific operations on the table. Since this     *
  2767.  '*  function is recursive it keeps track of available stack space. The two *
  2768.  '*  major possibilities are SEEKs/REORDERs (for which OrderCursor is then  *
  2769.  '*  called) and EDIT/ADD records (for which EdAddCursor is called. Note    *
  2770.  '*  the combined index (BigIndex) and the default index are handled as     *
  2771.  '*  special cases, since they don't correspond to a single field.Recursive *
  2772.  '*  construction lets the user cycle through the fields as long as         *
  2773.  '*  sufficient stack remains to keep calling PlaceCursor. Note that since  *
  2774.  '*  it is recursive, it may take while to step out while stepping with F8. *
  2775.  '*                                Parameters                               *
  2776.  '*  WhichField    Integer identifier specifying current field on form      *
  2777.  '*  TablesRec     Variable of type RecStruct holding all table information *
  2778.  '*  FirstLetter$  Carries user response to initial prompt shown            *
  2779.  '*  FirstTime     Boolean telling whether this is first cal or recursion   *
  2780.  '*  Task          Tells operation being performed                          *
  2781.  '***************************************************************************
  2782.  '
  2783.  FUNCTION PlaceCursor% (WhichField, TablesRec AS RecStruct, FirstLetter$, Firs
  2784.  STATIC ReturnValue, InitialLetter$, GetOut, counter, WhichOne
  2785.  WhichTable = TablesRec.TableNum
  2786.  IF ExitFlag THEN EXIT FUNCTION
  2787.  
  2788.  ReturnValue = WhichField
  2789.  ' Keep tabs on the stack and exit and reset it if it gets too low
  2790.  IF FRE(-2) < 400 THEN
  2791.    WhichField = 0
  2792.    PlaceCursor = 0
  2793.    GetOut = -1
  2794.    EXIT FUNCTION
  2795.  END IF
  2796.  
  2797.  ' Set up for each of the possible operations that use PlaceCursor
  2798.  IF Task = REORDER THEN
  2799.     COLOR FOREGROUND, BACKGROUND
  2800.     CALL ShowMessage("Press TAB to choose field to sort on, ESC to escape", 0)
  2801.     IF WhichField = TITLEFIELD THEN WhichOne = 0
  2802.  ELSEIF Task = SEEKFIELD THEN
  2803.     CALL ShowMessage("TAB to a field, then enter a value to search", 0)
  2804.  ELSEIF Task = ADDRECORD THEN
  2805.    IF FirstTime THEN FirstLetter$ = CHR$(TABKEY) ELSE FirstLetter$ = ""
  2806.  END IF
  2807.  
  2808.  ' The following IF... lets function handle either an entered letter or TAB
  2809.  IF FirstLetter$ <> "" THEN
  2810.      Answer$ = FirstLetter$
  2811.  ELSEIF FirstTime THEN
  2812.    IF Task = EDITRECORD THEN
  2813.      Answer$ = CHR$(TABKEY)
  2814.    END IF
  2815.  ELSE
  2816.    DO
  2817.    Answer$ = INKEY$
  2818.    LOOP WHILE Answer$ = EMPTYSTRING
  2819.  END IF
  2820.  
  2821.  IF LEN(Answer$) = 1 THEN
  2822.  
  2823.  ' Clear the fields for the appropriate messages
  2824.  IF Task <> EDITRECORD AND Task <> ADDRECORD THEN
  2825.  CALL ClearEm(TablesRec.TableNum, 1, 1, 1, 1, 1, 1)
  2826.  END IF
  2827.  
  2828.     SELECT CASE ASC(Answer$)
  2829.      CASE IS = TABKEY, ENTER
  2830.             SELECT CASE WhichField
  2831.              CASE TITLEFIELD, AUTHORFIELD, PUBFIELD, EDFIELD, PRICEFIELD, IDFI
  2832.                IF Task = REORDER OR Task = SEEKFIELD THEN
  2833.                  RetVal = OrderCursor(WhichOne, WhichField, Task, TablesRec, F
  2834.                  IF RetVal THEN
  2835.                    ' trap a magic value for an escape here then call the Draw
  2836.                    IF RetVal <> 3 THEN
  2837.                      WhichOne = 0: EXIT FUNCTION
  2838.                    ELSE
  2839.                      WhichOne = 0
  2840.                      WhichField = 0
  2841.                      PlaceCursor = 0
  2842.                      CALL ShowRecord(TablesRec)
  2843.                      CALL ShowMessage("You've escaped! Try again", 0)
  2844.                      CALL DrawTable(WhichTable)
  2845.                      CALL DrawHelpKeys(WhichTable)
  2846.                      CALL ShowKeys(TablesRec, FOREGROUND + BRIGHT, 0, 0)
  2847.                      EXIT FUNCTION
  2848.                    END IF
  2849.                  END IF
  2850.                ELSEIF Task = EDITRECORD OR Task = ADDRECORD THEN
  2851.                  Placed = EdAddCursor(WhichField, Task, TablesRec, FirstTime)
  2852.                END IF
  2853.  
  2854.              CASE BIGINDEX
  2855.                  CALL Indexbox(TablesRec, WhichOne)
  2856.                  IF WhichTable = cBookStockTableNum THEN
  2857.                    COLOR BACKGROUND, BRIGHT + FOREGROUND
  2858.                    IF Task = REORDER THEN
  2859.                      LOCATE TITLEFIELD, 18
  2860.                      PRINT "Press ENTER to sort first by Title..."
  2861.                      LOCATE AUTHORFIELD, 18
  2862.                      PRINT "... then subsort by Author..."
  2863.                      LOCATE IDFIELD, 18
  2864.                      PRINT "... then subsort again by ID "
  2865.                      SLEEP
  2866.                    ELSEIF Task = SEEKFIELD THEN
  2867.                      LOCATE TITLEFIELD, 18
  2868.                      PRINT "First, type in the Title to search for,"
  2869.                      LOCATE AUTHORFIELD, 18
  2870.                      PRINT "... then type in the Author's name"
  2871.                      LOCATE IDFIELD, 18
  2872.                      PRINT "... then type in the ID number "
  2873.                      CALL ShowMessage("Typing in a value for a combined index
  2874.                      SLEEP
  2875.                    END IF
  2876.                    COLOR FOREGROUND, BACKGROUND
  2877.                    IF ConfirmEntry%(FirstLetter$) THEN
  2878.                      TablesRec.WhichIndex = "BigIndex"
  2879.                      IF Task = SEEKFIELD THEN
  2880.                        WhichOne = 0
  2881.                        WhichField = TITLEFIELD
  2882.                      END IF
  2883.                      EXIT FUNCTION
  2884.                    END IF
  2885.                  END IF
  2886.                  CALL ClearEm(TablesRec.TableNum, 1, 1, 0, 0, 1, 0)
  2887.                  WhichField = NULLINDEX   ' TITLEFIELD
  2888.  
  2889.              CASE NULLINDEX
  2890.                  CALL Indexbox(TablesRec, WhichOne)
  2891.                  IF Task = SEEKFIELD THEN
  2892.                    CALL ShowMessage("Can't SEEK on the default index", 0)
  2893.                    DO
  2894.                      KeyIn$ = INKEY$
  2895.                      IF KeyIn$ <> "" THEN
  2896.                        IF ASC(KeyIn$) = ESCAPE THEN EXIT FUNCTION
  2897.                      END IF
  2898.                    LOOP WHILE KeyIn$ = ""
  2899.                    'SLEEP
  2900.                  '  EXIT FUNCTION
  2901.                  'END IF
  2902.                  ELSEIF ConfirmEntry%(FirstLetter$) THEN
  2903.                    TablesRec.WhichIndex = "NULL"
  2904.                    EXIT FUNCTION
  2905.                  END IF
  2906.                  WhichField = TITLEFIELD
  2907.  
  2908.              CASE ELSE
  2909.                  EraseMessage
  2910.                   CALL ShowMessage("Not a valid key --- press Space bar", 0)
  2911.                  EXIT FUNCTION
  2912.            END SELECT
  2913.          ' Placecursor calls itself for next user response
  2914.          Value = PlaceCursor(WhichField, TablesRec, FirstLetter$, 0, Task)
  2915.  
  2916.      CASE ESCAPE
  2917.        WhichOne = 0
  2918.        WhichField = 0
  2919.        PlaceCursor = 0
  2920.        CALL ShowRecord(TablesRec)
  2921.        CALL ShowMessage("You've escaped! Try again", 0)
  2922.        CALL DrawTable(WhichTable)
  2923.        CALL DrawHelpKeys(WhichTable)
  2924.        CALL ShowKeys(TablesRec, FOREGROUND + BRIGHT, 0, 0)
  2925.        EXIT FUNCTION
  2926.      CASE 32 TO 127                        ' Acceptable ASCII characters
  2927.       InitialLetter$ = Answer$
  2928.       FirstLetter$ = InitialLetter$
  2929.       EXIT FUNCTION
  2930.      CASE ELSE
  2931.          BEEP
  2932.          EraseMessage
  2933.           CALL ShowMessage("Not a valid key --- press Space bar", 0)
  2934.          WhichField = 0
  2935.          PlaceCursor = 0
  2936.          EXIT FUNCTION
  2937.      END SELECT
  2938.  ELSEIF Answer$ <> CHR$(9) THEN
  2939.    EraseMessage
  2940.    CALL ShowMessage("Not a valid key --- press Space bar", 0)
  2941.    WhichField = 0
  2942.    EXIT FUNCTION
  2943.  ELSE
  2944.       CALL ShowMessage("  Press TAB key or ENTER  ", 0)
  2945.  END IF
  2946.  
  2947.  IF GetOut THEN
  2948.    counter = counter + 1
  2949.    IF counter < 15 THEN
  2950.      WhichField = 0
  2951.      WhichOne = 0
  2952.      EXIT FUNCTION
  2953.    ELSE
  2954.      GetOut = 0
  2955.      counter = 0
  2956.   END IF
  2957.  END IF
  2958.  
  2959.  END FUNCTION
  2960.  
  2961.  '***************************************************************************
  2962.  '*  The TransposeName FUNCTION takes a  string and decideds whether it is  *
  2963.  '*  a comma-delimited, last-name-first name, a first-name-first name or a  *
  2964.  '*  single word name. In the last case, the string is returned unchanged.  *
  2965.  '*  In either of the other cases, the string is translated to the comple-  *
  2966.  '*  mentary format.                                                        *
  2967.  '*                              Parameters                                 *
  2968.  '*  TheName   A string representing a CardHolders table TheName element,   *
  2969.  '*            or a BookStock table Author Element                          *
  2970.  '***************************************************************************
  2971.  FUNCTION TransposeName$ (TheName AS STRING)
  2972.  SubStrLen = (INSTR(TheName, ","))
  2973.  IF SubStrLen = 0 THEN
  2974.    SubStrLen = INSTR(TheName, " ")
  2975.    IF SubStrLen = 0 THEN TransposeName$ = TheName: EXIT FUNCTION
  2976.  END IF
  2977.  TheName = LTRIM$(RTRIM$(TheName))
  2978.    IF INSTR(TheName, ",") THEN
  2979.      LastNameLen = INSTR(TheName, ",")
  2980.      LastName$ = LTRIM$(RTRIM$(LEFT$(TheName, LastNameLen - 1)))
  2981.      FirstName$ = LTRIM$(RTRIM$(MID$(TheName, LastNameLen + 1)))
  2982.      TransposeName$ = LTRIM$(RTRIM$(FirstName$ + " " + LastName$))
  2983.    ELSE
  2984.      FirstNameLen = INSTR(TheName, " ")
  2985.      IF FirstNameLen THEN
  2986.        FirstName$ = LTRIM$(RTRIM$(LEFT$(TheName, FirstNameLen - 1)))
  2987.        LastName$ = LTRIM$(RTRIM$(MID$(TheName, FirstNameLen + 1)))
  2988.      ELSE
  2989.        LastName$ = LTRIM$(RTRIM$(TheName))
  2990.      END IF
  2991.      TransposeName$ = LTRIM$(RTRIM$(LastName$ + ", " + FirstName$))
  2992.    END IF
  2993.  END FUNCTION
  2994.  
  2995.  '****************************** ValuesOK FUNCTION **************************
  2996.  '* The ValuesOK FUNCTION checks the values input by the user for various   *
  2997.  '* purposes. The checking is very minimal and checks the format of what is *
  2998.  '* entered. For example, the IDnum field needs a double value, but the form*
  2999.  '* (5 digits, followed by a decimal point, followed by 4 digits) is more   *
  3000.  '* important than the data type.                                           *
  3001.  '*                                Parameters:                              *
  3002.  '*   Big Rec      User-defined type containing all table information       *
  3003.  '*   Key1, Key2   Represent strings to check                               *
  3004.  '*   ValueToSeek  Represents the final value of a combined index           *
  3005.  '***************************************************************************
  3006.  FUNCTION ValuesOK (BigRec AS RecStruct, Key1$, Key2$, ValueToSeek$)
  3007.    IndexName$ = BigRec.WhichIndex
  3008.    ValueToSeek$ = LTRIM$(RTRIM$(ValueToSeek$))
  3009.    SELECT CASE RTRIM$(LTRIM$(IndexName$))
  3010.      CASE "TitleIndexBS", "PubIndexBS"       ' LEN <= 50
  3011.        IF LEN(Key1$) > 50 THEN ValuesOK = FALSE: EXIT FUNCTION
  3012.  
  3013.      CASE "AuthorIndexBS", "NameIndexCH"     ' LEN <= 36
  3014.        IF LEN(Key1$) > 36 THEN ValuesOK = FALSE: EXIT FUNCTION
  3015.  
  3016.      CASE "StateIndexCH"                     ' LEN = 2
  3017.        IF LEN(Key1$) > 2 THEN ValuesOK = FALSE: EXIT FUNCTION
  3018.  
  3019.      CASE "IDIndex", "IDIndexBO"             ' 5 digits befor d.p., 4 after
  3020.        IF LEN(ValueToSeek$) <> 10 THEN ValuesOK = FALSE: EXIT FUNCTION
  3021.        IF MID$(ValueToSeek$, 6, 1) <> "." THEN
  3022.          ValuesOK = FALSE: EXIT FUNCTION
  3023.        END IF
  3024.      CASE "CardNumIndexCH", "CardNumIndexBO" ' 5 digits, value <= LONG
  3025.        IF LEN(ValueToSeek$) <> 5 THEN ValuesOK = FALSE: EXIT FUNCTION
  3026.  
  3027.      CASE "ZipIndexCH"                       ' 5 digits, value <= LONG
  3028.        IF LEN(ValueToSeek$) <> 5 THEN ValuesOK = FALSE: EXIT FUNCTION
  3029.  
  3030.      CASE "BigIndex"                         ' Key1$ <= 50, Key2$ <= 36
  3031.        IF LEN(Key1$) > 50 THEN ValuesOK = FALSE: EXIT FUNCTION
  3032.        IF LEN(Key2$) > 36 THEN ValuesOK = FALSE: EXIT FUNCTION
  3033.        IF MID$(ValueToSeek$, 6, 1) <> "." THEN
  3034.          ValuesOK = FALSE: EXIT FUNCTION
  3035.        END IF
  3036.    END SELECT
  3037.    ValuesOK = TRUE
  3038.  END FUNCTION
  3039.  
  3040.  
  3041.  
  3042.  BOOKMOD3.BAS
  3043.  CD-ROM Disc Path:   \SAMPCODE\BASIC\BOOKMOD3.BAS
  3044.  
  3045.  '***************************************************************************
  3046.  '* This is module level code for BOOKMOD3.BAS, the fourth                  *
  3047.  '* module of BOOKLOOK.BAS.                                                 *
  3048.  '*                                                                         *
  3049.  '* The module contains a procedure, MakeOver, you can use to convert text  *
  3050.  '* files containing the right format and type of information for the tables*
  3051.  '* used by the BOOKLOOK program to a .MDB file. However, you need to call  *
  3052.  '* MakeOver from the Immediate Window, and in order for it to work, you    *
  3053.  '* must use the PROISAMD version of the TSR, because MakeOver needs the    *
  3054.  '* data dictionary functionality for creating indexes, etc.                *
  3055.  '* If you use the DTFMTER.QLB library functions you must include the files *
  3056.  '* DATIM.BI and FORMAT.BI at this level, using syntax as shown below.      *
  3057.  '***************************************************************************
  3058.  DEFINT A-Z
  3059.  '$INCLUDE: 'booklook.bi'
  3060.  
  3061.  '***************************************************************************
  3062.  '*  The BooksBorrowed SUB takes the CardNum in BooksOut associated with the*
  3063.  '*  currently displayed CardHolder, then looks up each book in BooksOut    *
  3064.  '*  assigned to that CardNum. Note that you can use SEEKoperand to find the*
  3065.  '*  first matching record, but thereafter you need to MOVENEXT and check   *
  3066.  '*  each succeeding record to see if the CardNum matches. When a match is  *
  3067.  '*  made, look up the IDnum in the BooksOut table and retrieve the title.  *
  3068.  '*  Put all the titles in the Titles array, then display with PeekWindow.  *
  3069.  '*                                   Parameters                            *
  3070.  '*  TablesRec   Structure containing information on all database tables    *
  3071.  '***************************************************************************
  3072.  SUB BooksBorrowed (TablesRec AS RecStruct)
  3073.          DIM Titles(50) AS STRING
  3074.          ' First, get the card number of the current record in Bookstock - the
  3075.          ' at the end of this procedure, restore that book
  3076.          IF LOF(cBooksOutTableNum) = 0 THEN EXIT SUB
  3077.          IF GETINDEX$(cBooksOutTableNum) <> "CardNumIndexBO" THEN
  3078.                  SETINDEX cBooksOutTableNum, "CardNumIndexBO"
  3079.          END IF
  3080.          RevName$ = TransposeName$(TablesRec.Lendee.TheName)
  3081.          SEEKEQ cBooksOutTableNum, TablesRec.Lendee.CardNum
  3082.           IF NOT EOF(cBooksOutTableNum) THEN
  3083.                  DO
  3084.                          RETRIEVE cBooksOutTableNum, TablesRec.OutBooks
  3085.                           IF TablesRec.OutBooks.CardNum = TablesRec.Lendee.Car
  3086.                                          IF GETINDEX$(cBookStockTableNum) <> "
  3087.                                                  SETINDEX cBookStockTableNum,
  3088.                                          END IF
  3089.                                          SEEKEQ cBookStockTableNum, TablesRec.
  3090.                                          IF NOT EOF(cBookStockTableNum) THEN
  3091.                                                  RETRIEVE cBookStockTableNum,
  3092.                                                  Titles(Index) = RTRIM$(Tables
  3093.                                                  ThisSize = LEN(RTRIM$(Titles(
  3094.                                                          IF ThisSize > Biggest
  3095.                                                                  Biggest = Thi
  3096.                                                          END IF
  3097.                                           Index = Index + 1
  3098.                                          END IF
  3099.                                  END IF
  3100.                  MOVENEXT cBooksOutTableNum
  3101.                  LOOP UNTIL EOF(cBooksOutTableNum)
  3102.          ELSE
  3103.                  Alert$ = RevName$ + " currently has no books checked out"
  3104.                  CALL ShowMessage(Alert$, 0)
  3105.          END IF
  3106.          IF Index <> 0 THEN
  3107.                  HeadMessage$ = " Books borrowed by " + RevName$ + " "
  3108.                  FootMessage$ = " Press a key to continue "
  3109.                  CALL PeekWindow(Titles(), HeadMessage$, FootMessage$, Biggest
  3110.                  CALL DrawTable(TablesRec.TableNum)
  3111.                  CALL ShowMessage(KEYSMESSAGE, 0)
  3112.          END IF
  3113.  END SUB
  3114.  
  3115.  '***************************************************************************
  3116.  '*  The BorrowBook SUB prompts the user to enter the name of the Cardholder*
  3117.  '*  who wants to borrow the book, then updates all the other tables accord-*
  3118.  '*  ingly. The name or cardnumber can be entered --- if conversion to a    *
  3119.  '*  number fails, the user entered a name. If the name isn't of the right  *
  3120.  '*  format, it is transposed to last-first, comma delimited. If no exact   *
  3121.  '*  match is found, the next best match is attempted and presented for the *
  3122.  '*  approval of the user.
  3123.  '*                                  Parameter                              *
  3124.  '*  TablesRec   RecStruct type variable holding current table information  *
  3125.  '***************************************************************************
  3126.  SUB BorrowBook (TablesRec AS RecStruct)
  3127.  
  3128.  DIM SaveBook AS RecStruct
  3129.  DIM PeekString(10) AS STRING
  3130.  
  3131.  Prompt$ = "Name or Card Number to Seek: "
  3132.  SaveBook = TablesRec                          ' Save book information
  3133.          ' Prompt user and catch keystroke
  3134.  CALL ShowMessage("Enter borrower cardnumber or name: ", 1)
  3135.  FirstChar = ASC(ReturnKey$)                   ' ReturnKey$ is a function
  3136.  IF FirstChar = ESCAPE THEN CALL ShowMessage("", 0): EXIT SUB
  3137.  Answer$ = MakeString$(FirstChar, Prompt$)
  3138.  IF FirstChar = ESCAPE THEN CALL ShowMessage("", 0): EXIT SUB
  3139.  NumToCheck& = VAL(Answer$)
  3140.  IF NumToCheck& = 0 THEN
  3141.          IF INSTR(Answer$, ",") = 0 THEN
  3142.                  StraightName$ = Answer$
  3143.                  Answer$ = TransposeName$(Answer$)
  3144.          ELSE
  3145.                  StraightName$ = TransposeName$(Answer$)
  3146.          END IF
  3147.  
  3148.          SETINDEX cCardHoldersTableNum, "NameIndexCH"
  3149.          SEEKEQ cCardHoldersTableNum, Answer$
  3150.          IF EOF(cCardHoldersTableNum) THEN
  3151.                  MOVEFIRST cCardHoldersTableNum
  3152.                  SEEKGE cCardHoldersTableNum, Answer$     ' If EQ fails, try G
  3153.                  IF EOF(cCardHoldersTableNum) THEN
  3154.                          Alert$ = "Sorry, couldn't find " + StraightName$ + "
  3155.                          CALL ShowMessage(Alert$, 0)
  3156.                          EXIT SUB
  3157.                  END IF
  3158.          END IF
  3159.          IF NOT EOF(cCardHoldersTableNum) THEN
  3160.                  RETRIEVE cCardHoldersTableNum, SaveBook.Lendee
  3161.                  IF TEXTCOMP(LEFT$(SaveBook.Lendee.TheName, 2), LEFT$(Answer$,
  3162.                          NumToCheck& = SaveBook.Lendee.CardNum
  3163.                  ELSE
  3164.                          Alert$ = "Sorry, couldn't match " + StraightName$ + "
  3165.                          CALL ShowMessage(Alert$, 0): ' SLEEP: EraseMessage
  3166.                          EXIT SUB
  3167.                  END IF
  3168.          END IF
  3169.  ELSE
  3170.          SETINDEX cCardHoldersTableNum, "CardNumIndexCH"
  3171.          SEEKEQ cCardHoldersTableNum, NumToCheck&
  3172.          IF EOF(cCardHoldersTableNum) THEN
  3173.                  Alert$ = "Sorry, could not match " + Answer$
  3174.                  CALL ShowMessage(Alert$, 0):  ' SLEEP: EraseMessage
  3175.                  EXIT SUB
  3176.          ELSE
  3177.                  RETRIEVE cCardHoldersTableNum, SaveBook.Lendee
  3178.                  NumToCheck& = SaveBook.Lendee.CardNum
  3179.          END IF
  3180.  END IF
  3181.  
  3182.  DateDue# = 32950#     ' the Date/Time library as shown on these 2 lines:
  3183.  'DateDue# = Now# + 30#
  3184.  'DateDue$ = STR$(Month&(DateDue#)) + "/" + LTRIM$(STR$(Day&(DateDue#))) + "/"
  3185.  
  3186.  ' Show the information on the Cardholder you found...
  3187.  DO
  3188.          PeekString(0) = " This book will be checked out to: "
  3189.          PeekString(1) = ""
  3190.          PeekString(2) = RTRIM$(SaveBook.Lendee.TheName)
  3191.          PeekString(3) = RTRIM$(SaveBook.Lendee.Street)
  3192.          PeekString(4) = RTRIM$(SaveBook.Lendee.City) + ", " + RTRIM$(SaveBook
  3193.          PeekString(5) = "Card number: " + STR$(SaveBook.Lendee.CardNum)
  3194.          PeekString(6) = ""
  3195.          PeekString(7) = "The Due Date will be " + STR$(DateDue# + 30)
  3196.          IF LEN(DateDue$) THEN PeekString(7) = "The Due Date will be " + DateD
  3197.          FOR Index = 0 TO 8
  3198.                  ThisSize = LEN(RTRIM$(PeekString(Index)))
  3199.                  IF ThisSize > Biggest THEN
  3200.                          Biggest = ThisSize
  3201.                  END IF
  3202.          NEXT Index
  3203.  
  3204.          HeadMessage$ = " Cardholder checking out this book "
  3205.          FootMessage$ = " Press ENTER to confirm this checkout "
  3206.          Alert$ = "Press N seek next similar match, ESC to abort checkout"
  3207.          CALL ShowMessage(Alert$, 0)
  3208.          CALL PeekWindow(PeekString(), HeadMessage$, FootMessage$, Biggest)
  3209.  
  3210.          ' Let the user press "N" to see the next best match, ESC to abort che
  3211.          ' anything else to confirm this as person to whom to check book out t
  3212.  
  3213.          Reply$ = ReturnKey$
  3214.          SELECT CASE Reply$
  3215.                  CASE CHR$(ESCAPE)
  3216.                          DoneFlag = TRUE
  3217.                  CASE "N", "n"
  3218.                          MOVENEXT cCardHoldersTableNum
  3219.                          IF EOF(cCardHoldersTableNum) THEN
  3220.                                  DoneFlag = TRUE
  3221.                          ELSE
  3222.                                  RETRIEVE cCardHoldersTableNum, SaveBook.Lende
  3223.                                  NumToCheck& = SaveBook.Lendee.CardNum
  3224.                                  IF LEFT$(SaveBook.Lendee.TheName, 2) <> LEFT$
  3225.                                          DoneFlag = TRUE
  3226.                                  END IF
  3227.                          END IF
  3228.                  CASE ELSE
  3229.                                  TablesRec.OutBooks.CardNum = NumToCheck&
  3230.                                  TablesRec.OutBooks.IDnum = SaveBook.Inventory
  3231.                                  TablesRec.OutBooks.DueDate = DateDue#
  3232.                                  DoneFlag = TRUE
  3233.                                  MOVEFIRST (cBooksOutTableNum)
  3234.                                  INSERT cBooksOutTableNum, TablesRec.OutBooks
  3235.                                  CALL ShowMessage("", 0)
  3236.          END SELECT
  3237.  LOOP UNTIL DoneFlag
  3238.  
  3239.  CALL DrawTable(TablesRec.TableNum)
  3240.  CALL ShowMessage(KEYSMESSAGE, 0)
  3241.  
  3242.  END SUB
  3243.  
  3244.  '**************************************************************************
  3245.  '*  The Borrowed FUNCTION simply makes sure there are records in the      *
  3246.  '*  BooksOut table. If there are none, a message is displayed             *
  3247.  '**************************************************************************
  3248.  FUNCTION Borrowed
  3249.          IF LOF(cBooksOutTableNum) = 0 THEN
  3250.                  CALL ShowMessage("Sorry, no records in the BooksOut table", 0
  3251.                  Borrowed = FALSE
  3252.          ELSE
  3253.                  Borrowed = TRUE
  3254.          END IF
  3255.  END FUNCTION
  3256.  
  3257.  '***************************************************************************
  3258.  '* The CatchKey function gets a keystroke and returns TRUE if it was ENTER,*
  3259.  '* otherwise it returns FALSE.                                             *
  3260.  '***************************************************************************
  3261.  FUNCTION CatchKey%
  3262.          DO
  3263.          Answer$ = INKEY$
  3264.          LOOP WHILE Answer$ = ""
  3265.          SELECT CASE ASC(Answer$)
  3266.                  CASE ENTER
  3267.                          CatchKey% = -1
  3268.                  CASE ELSE
  3269.                          CatchKey% = 0
  3270.          END SELECT
  3271.  END FUNCTION
  3272.  
  3273.  '***************************************************************************
  3274.  '*  The GetStatus FUNCTION looks up the status of a book in the BooksOut   *
  3275.  '*  table. If the SEEK fails it means the book isn't checked out, and that *
  3276.  '*  message is displayed. Otherwise, it is placed in DateToShow parameter. *
  3277.  '*  The final message about retrieving borrow info relates to LendeeProfile*
  3278.  '*                                   Parameters                            *
  3279.  '*  TablesRec     Structure containing the information about all the tables*
  3280.  '*  DateToShow    The due date to show in the ShowStatus SUB               *
  3281.  '***************************************************************************
  3282.  FUNCTION GetStatus (TablesRec AS RecStruct, DateToShow#)
  3283.                  IF GETINDEX$(cBooksOutTableNum) <> "IDIndexBO" THEN
  3284.                          SETINDEX cBooksOutTableNum, "IDIndexBO"
  3285.                  END IF
  3286.                  SEEKEQ cBooksOutTableNum, TablesRec.Inventory.IDnum
  3287.                  IF NOT EOF(cBooksOutTableNum) THEN
  3288.                          RETRIEVE cBooksOutTableNum, TablesRec.OutBooks
  3289.                  ELSE
  3290.                          Alert$ = "This book is not checked out"   ' the book
  3291.                          CALL ShowMessage(Alert$, 0)               ' table, so
  3292.                          DateToShow# = 0: GetStatus = FALSE
  3293.                          EXIT FUNCTION
  3294.                  END IF
  3295.                  DateToShow# = TablesRec.OutBooks.DueDate#
  3296.                  GetStatus = TRUE
  3297.  END FUNCTION
  3298.  
  3299.  '***************************************************************************
  3300.  '*  The LendeeProfile takes the IDnum of the currently displayed book, then*
  3301.  '*  looks that up in the BooksOut table and fetches the CardHolder record  *
  3302.  '*  that corresponds to the CardNum entry in BooksOut. Then the CardNum is *
  3303.  '*  looked up in the CardHolders table and the borrower information shown. *
  3304.  '*                                Parameters                               *
  3305.  '*  TablesRec   Contains information on all the tables in the database     *
  3306.  '***************************************************************************
  3307.  SUB LendeeProfile (TablesRec AS RecStruct)
  3308.          ' Make sure the CardHolders table actually has records
  3309.          IF LOF(cCardHoldersTableNum) = 0 THEN
  3310.                  CALL ShowMessage("Sorry, there are no cardholder records", 0)
  3311.                  EXIT SUB
  3312.          END IF
  3313.          ' Create an array to hold information from CardHolders table
  3314.          DIM LendeeInfo(10)  AS STRING
  3315.          ' Set the index if it is not the one you want
  3316.          IF GETINDEX$(cBooksOutTableNum) <> "IDIndexBO" THEN
  3317.                  SETINDEX cBooksOutTableNum, "IDIndexBO"
  3318.          END IF
  3319.          SEEKEQ cBooksOutTableNum, TablesRec.Inventory.IDnum    ' Seek the rec
  3320.          IF EOF(cBooksOutTableNum) THEN                         ' If you find
  3321.                  CALL ShowMessage("This book is not checked out", 0)  ' the bo
  3322.                  EXIT SUB                                             ' otherw
  3323.          ELSE                                                   ' If it's ther
  3324.                  RETRIEVE cBooksOutTableNum, TablesRec.OutBooks       ' fetch
  3325.  
  3326.                  ' If the CardNum exists, set an index in CardHolders and SEEK
  3327.                  ' CardNum. If SEEK fails, print a warning; if it succeeds, ge
  3328.                  ' information about the borrower, and display it using PeekWi
  3329.  
  3330.                  IF TablesRec.OutBooks.CardNum <> 0 THEN
  3331.                          IF GETINDEX$(cCardHoldersTableNum) <> "CardNumIndexCH
  3332.                                  SETINDEX cCardHoldersTableNum, "CardNumIndexC
  3333.                          END IF
  3334.                          SEEKEQ cCardHoldersTableNum, TablesRec.OutBooks.CardN
  3335.                          IF EOF(cBooksOutTableNum) THEN
  3336.                                  Alert$ = "Cardholder number associated with b
  3337.                                  CALL ShowMessage(Alert$, 0)
  3338.                                  EXIT SUB
  3339.                          ELSE
  3340.                                  RETRIEVE cCardHoldersTableNum, TablesRec.Lend
  3341.                                  LendeeInfo(0) = RTRIM$(TablesRec.Lendee.TheNa
  3342.                                  LendeeInfo(1) = ""
  3343.                                  LendeeInfo(2) = RTRIM$(TablesRec.Lendee.Stree
  3344.                                  LendeeInfo(3) = RTRIM$(TablesRec.Lendee.City)
  3345.                                  LendeeInfo(4) = RTRIM$(TablesRec.Lendee.State
  3346.                                  LendeeInfo(5) = LTRIM$(STR$(TablesRec.Lendee.
  3347.                                  LendeeInfo(7) = STR$(TablesRec.Lendee.CardNum
  3348.                                  LendeeInfo(6) = ""
  3349.                                  LendeeInfo(7) = "Card number: " + LendeeInfo(
  3350.                                  LendeeInfo(8) = ""
  3351.                                  FOR Index = 1 TO 6
  3352.                                          ThisBig = LEN(LendeeInfo(Index))
  3353.                                          IF ThisBig > BiggestYet THEN
  3354.                                                  BiggestYet = ThisBig
  3355.                                          END IF
  3356.                                  NEXT Index
  3357.                                  Alert$ = "Press V to access the record for th
  3358.                                  CALL ShowMessage(Alert$, 0)
  3359.                                  HeadMessage$ = "Borrower of this Book"
  3360.                                  FootMessage$ = "Press a key to clear box"
  3361.                                  CALL ClearEm(TablesRec.TableNum, 1, 1, 1, 1,
  3362.                                  CALL PeekWindow(LendeeInfo(), HeadMessage$, F
  3363.                                  CALL DrawTable(TablesRec.TableNum)
  3364.                                  CALL ShowMessage(KEYSMESSAGE, 0)
  3365.                          END IF
  3366.                  END IF
  3367.          END IF
  3368.  END SUB
  3369.  
  3370.  '***************************************************************************
  3371.  '* The MakeOver SUB lets the user input the names of properly formatted    *
  3372.  '* text files, then creates a database file of the same type as BOOKS.MDB. *
  3373.  '* There is also a prompt for the new database name. The text files must   *
  3374.  '* contain comma-delimited fields, with strings within double quote marks. *
  3375.  '* The last part of this SUB demonstrates how indexes are created. You need*
  3376.  '* to have loaded PROISAMD.EXE to run this procedure.                      *
  3377.  '*                            Parameters:                                  *
  3378.  '*   Big Rec      User-defined type containing all table information       *
  3379.  '***************************************************************************
  3380.  '
  3381.  SUB MakeOver (BigRec AS RecStruct)
  3382.          CLOSE
  3383.          Alert$ = "Type name of file containing Cardholders table data: "
  3384.          CALL ShowMessage(Alert$, 1)
  3385.          INPUT "", CardFile$
  3386.          Alert$ = "Type name of file containing BooksOut table data: "
  3387.          CALL ShowMessage(Alert$, 1)
  3388.          INPUT "", OutBooks$
  3389.          Alert$ = "Type name of file containing BookStock table data: "
  3390.          CALL ShowMessage(Alert$, 1)
  3391.          INPUT "", BookFile$
  3392.          Alert$ = "Type name of ISAM file to create: "
  3393.          CALL ShowMessage(Alert$, 1)
  3394.          INPUT "", IsamFile$
  3395.          IF UCASE$(IsamFile$) = "BOOKS.MDB" THEN KILL "BOOKS.MDB"
  3396.          CALL ShowMessage("Loading database...", 0)
  3397.  
  3398.          CLOSE
  3399.          ON LOCAL ERROR GOTO FileHandler
  3400.          LenFileNo% = 10
  3401.          OPEN CardFile$ FOR INPUT AS LenFileNo%
  3402.          OutFileNo% = 11
  3403.          OPEN OutBooks$ FOR INPUT AS OutFileNo%
  3404.          RecFileNo% = 12
  3405.          OPEN BookFile$ FOR INPUT AS RecFileNo%
  3406.          ON ERROR GOTO 0
  3407.  
  3408.          ' Open the database and the BookStock table
  3409.          OPEN IsamFile$ FOR ISAM Books "BookStock" AS cBookStockTableNum
  3410.          OPEN IsamFile$ FOR ISAM Borrowers "CardHolders" AS cCardHoldersTableN
  3411.          OPEN IsamFile$ FOR ISAM BookStatus "BooksOut" AS cBooksOutTableNum
  3412.          CALL ShowMessage(" Opened all isam tables", 0)
  3413.  
  3414.          SeqFile% = LenFileNo
  3415.          DO WHILE (Reader%(BigRec, SeqFile%))
  3416.           INSERT cCardHoldersTableNum, BigRec.Lendee
  3417.          LOOP
  3418.          SeqFile% = OutFileNo
  3419.          DO WHILE (Reader%(BigRec, SeqFile))
  3420.           INSERT cBooksOutTableNum, BigRec.OutBooks
  3421.          LOOP
  3422.          SeqFile = RecFileNo
  3423.          DO WHILE (Reader%(BigRec, SeqFile))
  3424.           INSERT cBookStockTableNum, BigRec.Inventory
  3425.          LOOP
  3426.          CALL ShowMessage("Finished reading in records---Indexes next", 0)
  3427.  ' These indexes are already in the BOOKS.MDB database --- the following
  3428.  ' is the syntax that was used to create them
  3429.  
  3430.          ON LOCAL ERROR GOTO FileHandler
  3431.          CREATEINDEX cBookStockTableNum, "TitleIndexBS", 0, "Title"
  3432.          CREATEINDEX cBookStockTableNum, "AuthorIndexBS", 0, "Author"
  3433.          CREATEINDEX cBookStockTableNum, "PubIndexBS", 0, "Publisher"
  3434.          CREATEINDEX cBookStockTableNum, "IDIndex", 1, "IDnum"     ' Note uniq
  3435.          CREATEINDEX cBookStockTableNum, "BigIndex", 0, "Title", "Author", "ID
  3436.  
  3437.          CREATEINDEX cBooksOutTableNum, "IDIndexBO", 0, "IDnum"
  3438.          CREATEINDEX cBooksOutTableNum, "CardNumIndexBO", 0, "CardNum"
  3439.  
  3440.          CREATEINDEX cCardHoldersTableNum, "NameIndexCH", 0, "TheName"
  3441.          CREATEINDEX cCardHoldersTableNum, "StateIndexCH", 0, "State"
  3442.          CREATEINDEX cCardHoldersTableNum, "ZipIndexCH", 0, "Zip"
  3443.          CREATEINDEX cCardHoldersTableNum, "CardNumIndexCH", 1, "CardNum"  ' U
  3444.          ON ERROR GOTO 0
  3445.          CALL ShowMessage(" All done with indexes...", 0)
  3446.          'CLOSE
  3447.  
  3448.          EXIT SUB
  3449.  
  3450.  FileHandler:
  3451.          IF ERR = 73 THEN
  3452.                  CALL ShowMessage("You need to Exit QBX and load PROISAMD /Ib:
  3453.          ELSEIF ERR = 10 THEN
  3454.                  Alert$ = "Finished appending the records to " + IsamFile$
  3455.                  CALL ShowMessage(Alert$, 0)
  3456.                  END
  3457.          ELSEIF ERR = 86 THEN
  3458.                  Alert$ = "Tried to add record with duplicate value on a uniqu
  3459.                  CALL ShowMessage(Alert$, 0)
  3460.                  ELSE
  3461.                  CALL ShowMessage("Can't find textfiles needed to make the dat
  3462.          END IF
  3463.          END
  3464.  END SUB
  3465.  
  3466.  '***************************************************************************
  3467.  '*  The PeekWindow SUB displays the elements of the OutBookNames array in  *
  3468.  '*  a window on top of the currently displayed table.                      *
  3469.  '*                                Parameters                               *
  3470.  '*  OutBookNames    Array of strings containing lines displayed in window  *
  3471.  '*  Header$         String to show at top of window                        *
  3472.  '*  Footer$         String to show at bottom of window                     *
  3473.  '*  BiggestYet      Length of the longest string to be shown               *
  3474.  '***************************************************************************
  3475.  SUB PeekWindow (OutBookNames() AS STRING, Header$, Footer$, BiggestYet%)
  3476.  HeadLen = LEN(Header$)        ' + 4
  3477.  FootLen = LEN(Footer$)        ' + 4
  3478.  IF HeadLen > FootLen THEN Bigger = HeadLen ELSE Bigger = FootLen
  3479.  IF Bigger > BiggestYet THEN BiggestYet = Bigger
  3480.  
  3481.  InnerBox = 9          ' InnerBox is total number of lines allowed inside box
  3482.  first = 0: last = 8
  3483.  DO
  3484.  
  3485.          ' Calculate header and footer placement
  3486.  
  3487.                  IF (HeadLen MOD 2) THEN
  3488.                          HeadStart = ((BiggestYet - HeadLen) \ 2) + 13
  3489.                  ELSE
  3490.                          HeadStart = ((BiggestYet - HeadLen) \ 2) + 12
  3491.                  END IF
  3492.                  IF (FootLen MOD 2) THEN
  3493.                          FootStart = ((BiggestYet - FootLen) \ 2) + 13
  3494.                  ELSE
  3495.                          FootStart = ((BiggestYet - FootLen) \ 2) + 12
  3496.                  END IF
  3497.  
  3498.                  ' Print a box and fill it with titles
  3499.                  Inset = TABLETOP + 2
  3500.  
  3501.                  Lines = Inset + 1
  3502.                  IF MoreBoxes = FALSE THEN
  3503.                          LOCATE Inset, 3
  3504.                          PRINT "       ╔"; STRING$(BiggestYet + 2, CHR$(205));
  3505.                  END IF
  3506.                  FOR PrintEm = first TO last
  3507.                          LOCATE Lines + NextSpace, 3
  3508.                          PRINT "       ║ "; OutBookNames(Total); SPACE$(Bigges
  3509.                          Total = Total + 1: NextSpace = NextSpace + 1
  3510.                  NEXT PrintEm
  3511.                  IF MoreBoxes = FALSE THEN                       ' Means first
  3512.                          LOCATE Lines + NextSpace, 3
  3513.                          PRINT "       ╚"; STRING$(BiggestYet + 2, CHR$(205));
  3514.                          COLOR BACKGROUND, FOREGROUND + BRIGHT
  3515.                          LOCATE Inset, HeadStart
  3516.                          PRINT Header$;                          '"╡ "; Header
  3517.                          LOCATE Lines + NextSpace, FootStart
  3518.                          PRINT Footer$                           '"╡ "; Footer
  3519.                          COLOR FOREGROUND, BACKGROUND
  3520.                  END IF
  3521.                  SLEEP
  3522.          first = first + InnerBox: last = last + InnerBox
  3523.          NextSpace = 0: HowMany = 0
  3524.  
  3525.          MoreBoxes = TRUE
  3526.  
  3527.  LOOP UNTIL LEN(RTRIM$(OutBookNames(Total))) = 0
  3528.  
  3529.  END SUB
  3530.  
  3531.  '***************************************************************************
  3532.  '*  The Reader FUNCTION reads specified text files and returns each line   *
  3533.  '*  as a separate record for the corresponding table.                      *
  3534.  '*                               Parameters                                *
  3535.  '*  BigRec    RecStruct variable containing information on tables          *
  3536.  '*  SeqFile   File number used to open the text file to be read
  3537.  '***************************************************************************
  3538.  FUNCTION Reader% (BigRec AS RecStruct, SeqFile%)
  3539.          SELECT CASE SeqFile
  3540.                  CASE 10
  3541.                          IF NOT EOF(SeqFile) THEN
  3542.                           INPUT #SeqFile, BigRec.Lendee.CardNum, BigRec.Lendee
  3543.                           Reader = -1
  3544.                          ELSE
  3545.                                  Reader = 0
  3546.                          END IF
  3547.                  CASE 11
  3548.                          IF NOT EOF(SeqFile) THEN
  3549.                           INPUT #SeqFile, BigRec.OutBooks.IDnum, BigRec.OutBoo
  3550.                           Reader = -1
  3551.                          ELSE
  3552.                           Reader = 0
  3553.                          END IF
  3554.                  CASE 12
  3555.                          IF NOT EOF(SeqFile) THEN
  3556.                                  INPUT #SeqFile, BigRec.Inventory.IDnum, BigRe
  3557.                                  Reader = -1
  3558.                           ELSE
  3559.                                  Reader = 0
  3560.                           END IF
  3561.          END SELECT
  3562.  END FUNCTION
  3563.  
  3564.  '***************************************************************************
  3565.  '*  The ReturnBook SUB checks the book currently being displayed back into *
  3566.  '*  the library --- that is, it eliminates the appropriate entry from the  *
  3567.  '*  BooksOut table. It checks to see if the book is overdue, and if so, it *
  3568.  '*  displays the amount of the fine to be paid.                            *
  3569.  '*                                Parameters                               *
  3570.  '*  TablesRec   RecStruct type variable holding current table information  *
  3571.  '***************************************************************************
  3572.  SUB ReturnBook (TablesRec AS RecStruct, DueDate#)
  3573.  
  3574.  DIM ReturnLines(10) AS STRING
  3575.  
  3576.  Alert$ = "Press ENTER to check current book in, N to abort checkin..."
  3577.  CALL ShowMessage(Alert$, 0)
  3578.  
  3579.  SETINDEX cBooksOutTableNum, "IDIndexBO"
  3580.  SEEKEQ cBooksOutTableNum, TablesRec.Inventory.IDnum
  3581.  IF NOT EOF(cBooksOutTableNum) THEN
  3582.          RETRIEVE cBooksOutTableNum, TablesRec.OutBooks
  3583.  END IF
  3584.  SETINDEX cCardHoldersTableNum, "CardNumIndexCH"
  3585.  SEEKEQ cCardHoldersTableNum, TablesRec.OutBooks.CardNum
  3586.  
  3587.  IF NOT EOF(cBooksOutTableNum) THEN
  3588.          IF LOF(cCardHoldersTableNum) THEN
  3589.                  RETRIEVE cCardHoldersTableNum, TablesRec.Lendee
  3590.          END IF
  3591.  END IF
  3592.  
  3593.  Today# = 32000    'Replace this with call to DTFMTER.QLB library routine
  3594.                                                                          'as s
  3595.  'Today# = Now#
  3596.  'ShowDate$ = STR$(Month&(Today#)) + "/" + LTRIM$(STR$(Day&(Today#))) + "/" +
  3597.  IF Today# > TablesRec.OutBooks.DueDate THEN
  3598.          Fine = Today# - TablesRec.OutBooks.DueDate
  3599.  END IF
  3600.  
  3601.  DateDue# = (TablesRec.OutBooks.DueDate)
  3602.  ' If you have DTFMTER.QLB loaded, use in to get date to display
  3603.  ' DateDue$ = STR$(Month&(DateDue#)) + "/" + LTRIM$(STR$(Day&(DateDue#))) + "/
  3604.  ReturnLines(0) = ""
  3605.  ReturnLines(1) = RTRIM$(TablesRec.Inventory.Title)
  3606.  ReturnLines(2) = "is checked out to card number: " + STR$(TablesRec.OutBooks.
  3607.  ReturnLines(3) = RTRIM$(TablesRec.Lendee.TheName)
  3608.  ReturnLines(4) = ""
  3609.  ReturnLines(5) = "Today's Date:     " + STR$(Today#) + " - A phoney date"
  3610.  IF LEN(ShowDate$) THEN ReturnLines(5) = "Today's Date:     " + ShowDate$
  3611.  ReturnLines(6) = "Due Date of Book: " + STR$(TablesRec.OutBooks.DueDate)
  3612.  IF LEN(DateDue$) THEN ReturnLines(6) = "Due Date of Book: " + DateDue$
  3613.  ReturnLines(7) = "Fine Payable:     $" + STR$(ABS(Fine / 100))
  3614.  ReturnLines(8) = ""
  3615.  ReturnLines(9) = ""
  3616.  FOR Index = 0 TO 10
  3617.          ThisOne = LEN(ReturnLines(Index))
  3618.          IF ThisOne > BiggestYet THEN BiggestYet = ThisOne
  3619.  NEXT Index
  3620.  Header$ = "Press ENTER to check book in..."
  3621.  Footer$ = "Press N or n to abort checkin..."
  3622.  CALL PeekWindow(ReturnLines(), Header$, Footer$, BiggestYet%)
  3623.  
  3624.  IF CatchKey THEN                              ' If user confirms, delete
  3625.          IF LOF(cBooksOutTableNum) <> 0 THEN         ' the entry to BooksOut t
  3626.                  DELETE cBooksOutTableNum
  3627.          END IF
  3628.  END IF
  3629.  CALL DrawTable(TablesRec.TableNum)
  3630.  CALL EraseMessage
  3631.  
  3632.  END SUB
  3633.  
  3634.  '***************************************************************************
  3635.  '* The ShowStatus SUB uses the due date associated with the book IDnum from*
  3636.  '* of the BooksOut table. This date is in serial form which is not decoded *
  3637.  '* here, but can be decoded with the date/time function library supplied   *
  3638.  '* with BASIC 7.0. The due date is displayed centered on the top line of   *
  3639.  '* the ShowMessage box.                                                    *
  3640.  '*                                Parameters                               *
  3641.  '*  Stat$       Message introducing the due date when displayed in its box *
  3642.  '*  ValueToShow The due date of the book from the BooksOut table           *
  3643.  '***************************************************************************
  3644.  SUB ShowStatus (Stat$, ValueToShow AS DOUBLE)
  3645.  
  3646.  COLOR FOREGROUND, BACKGROUND
  3647.  DataEndLine$ = STRING$(60, 205)       'redraw the bottom line
  3648.  
  3649.  StringToShow$ = Stat$       ' Figure out where to locate the text
  3650.  IF ValueToShow = 0 THEN
  3651.          LOCATE TABLEEND, 4
  3652.          PRINT DataEndLine$
  3653.          EXIT SUB
  3654.  ELSE
  3655.          ' The dates in the file are in serial form. Use the DTFMTER.QLB libra
  3656.          ' to decode serial dates for normal display. In the code below, the
  3657.          ' calls to the library are commented out.
  3658.  
  3659.          'TheDate$ = STR$(Month&(ValueToShow)) + "/" + LTRIM$(STR$(Day&(ValueT
  3660.          IF Stat$ = " Total records in table: " OR LEN(TheDate$) = 0 THEN
  3661.                  StringToShow$ = StringToShow$ + " " + STR$(ValueToShow)
  3662.          ELSE
  3663.                  StringToShow$ = StringToShow$ + " " + TheDate$
  3664.          END IF
  3665.          HowLong = LEN(StringToShow$)
  3666.          PlaceStatus = (73 \ 2) - (HowLong \ 2)
  3667.          StatusSpace$ = CHR$(181) + STRING$(HowLong, 32) + CHR$(198)
  3668.  END IF
  3669.  LOCATE TABLEEND, PlaceStatus
  3670.  PRINT StatusSpace$
  3671.  COLOR BACKGROUND, BRIGHT + FOREGROUND
  3672.  LOCATE TABLEEND, PlaceStatus + 1
  3673.  PRINT StringToShow$
  3674.  COLOR FOREGROUND, BACKGROUND
  3675.  
  3676.  END SUB
  3677.  
  3678.  
  3679.  
  3680.  CAL.BAS
  3681.  CD-ROM Disc Path:   \SAMPCODE\BASIC\CAL.BAS
  3682.  
  3683.  DEFINT A-Z      ' Default variable type is integer.
  3684.  
  3685.  ' Define a data type for the names of the months and the
  3686.  ' number of days in each:
  3687.  TYPE MonthType
  3688.          Number AS INTEGER  ' Number of days in the month
  3689.          MName AS STRING * 9   ' Name  of the month
  3690.  END TYPE
  3691.  
  3692.  ' Declare procedures used:
  3693.  DECLARE FUNCTION IsLeapYear% (N%)
  3694.  DECLARE FUNCTION GetInput% (Prompt$, Row%, LowVal%, HighVal%)
  3695.  
  3696.  DECLARE SUB PrintCalendar (Year%, Month%)
  3697.  DECLARE SUB ComputeMonth (Year%, Month%, StartDay%, TotalDays%)
  3698.  
  3699.  DIM MonthData(1 TO 12)   AS MonthType
  3700.  
  3701.  ' Initialize month definitions from DATA statements below:
  3702.  FOR I = 1 TO 12
  3703.          READ MonthData(I).MName, MonthData(I).Number
  3704.  NEXT
  3705.  
  3706.  ' Main loop, repeat for as many months as desired:
  3707.  DO
  3708.          CLS
  3709.  
  3710.          ' Get year and month as input:
  3711.          Year = GetInput("Year (1899 to 2099): ", 1, 1899, 2099)
  3712.          Month = GetInput("Month (1 to 12): ", 2, 1, 12)
  3713.  
  3714.          ' Print the calendar:
  3715.          PrintCalendar Year, Month
  3716.  ' Another Date?
  3717.          LOCATE 13, 1      ' Locate in 13th row, 1st column.
  3718.          PRINT "New Date? ";  ' Keep cursor on same line.
  3719.          LOCATE , , 1, 0, 13  ' Turn cursor on and make it one
  3720.                          ' character high.
  3721.          Resp$ = INPUT$(1) ' Wait for a key press.
  3722.          PRINT Resp$    ' Print  the key  pressed.
  3723.  
  3724.  LOOP WHILE UCASE$(Resp$) = "Y"
  3725.  END
  3726.  
  3727.  ' Data for the months of a year:
  3728.  DATA January, 31, February, 28,  March, 31
  3729.  DATA April, 30,   May, 31, June, 30, July, 31, August, 31
  3730.  DATA September,   30, October, 31, November, 30, December, 31
  3731.  
  3732.  ' ====================== COMPUTEMONTH =====================
  3733.  '  Computes the first day and the total days in a month
  3734.  ' =========================================================
  3735.  '
  3736.  SUB ComputeMonth (Year, Month, StartDay, TotalDays) STATIC
  3737.          SHARED MonthData() AS MonthType
  3738.  
  3739.          CONST LEAP = 366 MOD 7
  3740.          CONST NORMAL = 365 MOD 7
  3741.  
  3742.          ' Calculate total number of days (NumDays) since 1/1/1899:
  3743.  
  3744.          ' Start with whole years:
  3745.          NumDays = 0
  3746.          FOR I = 1899 TO Year - 1
  3747.                  IF IsLeapYear(I) THEN              ' If leap year,
  3748.                          NumDays = NumDays + LEAP   ' add 366 MOD 7.
  3749.                  ELSE                               ' If normal year,
  3750.                          NumDays = NumDays + NORMAL ' add 365 MOD 7.
  3751.                  END IF
  3752.          NEXT
  3753.  
  3754.          ' Next, add in days from whole months:
  3755.          FOR I = 1 TO Month - 1
  3756.                  NumDays = NumDays + MonthData(I).Number
  3757.          NEXT
  3758.  
  3759.          ' Set the number of days in the requested month:
  3760.          TotalDays = MonthData(Month).Number
  3761.  
  3762.          ' Compensate if requested year is a leap year:
  3763.          IF IsLeapYear(Year) THEN
  3764.  
  3765.                  ' If after February, add one to total days:
  3766.                  IF Month > 2 THEN
  3767.                          NumDays = NumDays + 1
  3768.  
  3769.                  ' If February, add one to the month's days:
  3770.                  ELSEIF Month = 2 THEN
  3771.                          TotalDays = TotalDays + 1
  3772.                  END IF
  3773.          END IF
  3774.  
  3775.          ' 1/1/1899 was a Sunday, so calculating "NumDays MOD 7"
  3776.          ' gives the day of week (Sunday = 0, Monday = 1, Tuesday
  3777.          ' = 2, and so on) for the first day of the input month:
  3778.          StartDay = NumDays MOD 7
  3779.  END SUB
  3780.  
  3781.  ' ======================== GETINPUT =======================
  3782.  '  Prompts for input, then tests for a valid range
  3783.  ' =========================================================
  3784.  '
  3785.  FUNCTION GetInput (Prompt$, Row, LowVal, HighVal) STATIC
  3786.  
  3787.          ' Locate prompt at specified row, turn cursor on and
  3788.          ' make it one character high:
  3789.          LOCATE Row, 1, 1, 0, 13
  3790.          PRINT Prompt$;
  3791.  
  3792.          ' Save column position:
  3793.          Column = POS(0)
  3794.  
  3795.          ' Input value until it's within range:
  3796.          DO
  3797.                  LOCATE Row, Column   ' Locate cursor at end of prompt.
  3798.                  PRINT SPACE$(10)     ' Erase anything already there.
  3799.                  LOCATE Row, Column   ' Relocate cursor at end of prompt.
  3800.                  INPUT "", Value      ' Input value with no prompt.
  3801.          LOOP WHILE (Value < LowVal OR Value > HighVal)
  3802.  
  3803.          ' Return valid input as value of function:
  3804.          GetInput = Value
  3805.  
  3806.  END FUNCTION
  3807.  
  3808.  ' ====================== ISLEAPYEAR =======================
  3809.  '   Determines if a year is a leap year or not
  3810.  ' =========================================================
  3811.  '
  3812.  FUNCTION IsLeapYear (N) STATIC
  3813.  
  3814.          ' If the year is evenly divisible by 4 and not divisible
  3815.          ' by 100, or if the year is evenly divisible by 400,
  3816.          ' then it's a leap year:
  3817.          IsLeapYear = (N MOD 4 = 0 AND N MOD 100 <> 0) OR (N MOD 400 = 0)
  3818.  END FUNCTION
  3819.  
  3820.  ' ===================== PRINTCALENDAR =====================
  3821.  '   Prints a formatted calendar given the year and month
  3822.  ' =========================================================
  3823.  '
  3824.  SUB PrintCalendar (Year, Month) STATIC
  3825.  SHARED MonthData() AS MonthType
  3826.  
  3827.          ' Compute starting day (Su M Tu ...)
  3828.          ' and total days for the month:
  3829.          ComputeMonth Year, Month, StartDay, TotalDays
  3830.          CLS
  3831.          Header$ = RTRIM$(MonthData(Month).MName) + "," + STR$(Year)
  3832.  
  3833.          ' Calculate location for centering month and year:
  3834.          LeftMargin = (35 - LEN(Header$)) \ 2
  3835.  ' Print header:
  3836.          PRINT TAB(LeftMargin); Header$
  3837.          PRINT
  3838.          PRINT "Su    M   Tu    W   Th    F   Sa"
  3839.          PRINT
  3840.  
  3841.          ' Recalculate and print tab
  3842.          ' to the first day of the month (Su M Tu ...):
  3843.          LeftMargin = 5 * StartDay + 1
  3844.          PRINT TAB(LeftMargin);
  3845.  
  3846.          ' Print out the days of the month:
  3847.          FOR I = 1 TO TotalDays
  3848.                  PRINT USING "##_   "; I;
  3849.  
  3850.                  ' Advance to the next line
  3851.                  ' when the cursor is past column 32:
  3852.                  IF POS(0) > 32 THEN PRINT
  3853.          NEXT
  3854.  
  3855.  END SUB
  3856.  
  3857.  
  3858.  
  3859.  CHECK.BAS
  3860.  CD-ROM Disc Path:   \SAMPCODE\BASIC\CHECK.BAS
  3861.  
  3862.  DIM Amount(1 TO 100) AS CURRENCY, Balance AS CURRENCY
  3863.  CONST FALSE = 0, TRUE = NOT FALSE
  3864.  CLS
  3865.  ' Get account's starting balance:
  3866.  INPUT "Type starting balance, then press <ENTER>: ", Balance
  3867.  ' Get transactions. Continue accepting input
  3868.  ' until the input is zero for a transaction,
  3869.  ' or until 100 transactions have been entered:
  3870.  FOR TransacNum% = 1 TO 100
  3871.     PRINT TransacNum%;
  3872.     PRINT ") Enter transaction amount (0 to end): ";
  3873.     INPUT "", Amount(TransacNum%)
  3874.     IF Amount(TransacNum%) = 0 THEN
  3875.        TransacNum% = TransacNum% - 1
  3876.        EXIT FOR
  3877.     END IF
  3878.  NEXT
  3879.  
  3880.  ' Sort transactions in ascending order,
  3881.  ' using a "bubble sort":
  3882.  Limit% = TransacNum%
  3883.  DO
  3884.     Swaps% = FALSE
  3885.     FOR I% = 1 TO (Limit% - 1)
  3886.        ' If two adjacent elements are out of order,
  3887.        ' switch those elements:
  3888.        IF Amount(I%) < Amount(I% + 1) THEN
  3889.           SWAP Amount(I%), Amount(I% + 1)
  3890.           Swaps% = I%
  3891.        END IF
  3892.     NEXT I%
  3893.    ' Sort on next pass only to where last switch was made:
  3894.    Limit% = Swaps%
  3895.  
  3896.  ' Sort until no elements are exchanged:
  3897.  LOOP WHILE Swaps%
  3898.  ' Print the sorted transaction array. If a transaction
  3899.  ' is greater than zero, print it as a "CREDIT"; if a
  3900.  ' transaction is less than zero, print it as a "DEBIT":
  3901.  FOR I% = 1 TO TransacNum%
  3902.     IF Amount(I%) > 0 THEN
  3903.        PRINT USING "CREDIT: $$#####.##"; Amount(I%)
  3904.     ELSEIF Amount(I%) < 0 THEN
  3905.        PRINT USING "DEBIT: $$#####.##"; Amount(I%)
  3906.     END IF
  3907.     ' Update balance:
  3908.     Balance = Balance + Amount(I%)
  3909.  NEXT I%
  3910.  ' Print the final balance:
  3911.  PRINT
  3912.  PRINT "--------------------------"
  3913.  PRINT USING "Final Balance: $$######.##"; Balance
  3914.  END
  3915.  
  3916.  
  3917.  
  3918.  CHRTASM.ASM
  3919.  CD-ROM Disc Path:   \SAMPCODE\BASIC\CHRTASM.ASM
  3920.  
  3921.  .MODEL medium
  3922.  ;********************************************************
  3923.  ;CHRTASM.ASM - assembly routines for the BASIC chart toolbox
  3924.  ;
  3925.  ;  Copyright (C) 1989 Microsoft Corporation, All Rights Reserved
  3926.  ;
  3927.  ;   DefaultFont - provides the segment:offset address for
  3928.  ;                  the default font
  3929.  ;
  3930.  ;********************************************************
  3931.  
  3932.  .FARDATA
  3933.  _IBM8_def label byte
  3934.  
  3935.        db   000h,000h,07Eh,00Ch,000h,000h,000h,000h
  3936.        db   000h,000h,000h,000h,000h,000h,000h,000h
  3937.        db   000h,000h,000h,000h,000h,000h,000h,000h
  3938.        db   000h,000h,000h,000h,000h,000h,000h,000h
  3939.        db   000h,000h,000h,000h,000h,000h,000h,000h
  3940.        db   000h,000h,000h,000h,000h,000h,000h,000h
  3941.        db   000h,000h,000h,000h,000h,000h,000h,000h
  3942.        db   000h,000h,000h,000h,000h,000h,000h,000h
  3943.        db   000h,000h,000h,000h,008h,000h,030h,000h
  3944.        db   060h,000h,007h,000h,000h,000h,000h,000h
  3945.        db   000h,000h,000h,090h,001h,000h,008h,000h
  3946.        db   008h,000h,000h,008h,000h,008h,000h,000h
  3947.        db   0FFh,02Eh,020h,0FFh,000h,000h,000h,000h
  3948.        db   000h,07Ah,004h,000h,000h,000h,000h,000h
  3949.        db   000h,07Eh,004h,000h,000h,000h,008h,000h
  3950.        db   07Eh,004h,008h,000h,086h,004h,008h,000h
  3951.        db   08Eh,004h,008h,000h,096h,004h,008h,000h
  3952.        db   09Eh,004h,008h,000h,0A6h,004h,008h,000h
  3953.        db   0AEh,004h,008h,000h,0B6h,004h,008h,000h
  3954.        db   0BEh,004h,008h,000h,0C6h,004h,008h,000h
  3955.        db   0CEh,004h,008h,000h,0D6h,004h,008h,000h
  3956.        db   0DEh,004h,008h,000h,0E6h,004h,008h,000h
  3957.        db   0EEh,004h,008h,000h,0F6h,004h,008h,000h
  3958.        db   0FEh,004h,008h,000h,006h,005h,008h,000h
  3959.        db   00Eh,005h,008h,000h,016h,005h,008h,000h
  3960.        db   01Eh,005h,008h,000h,026h,005h,008h,000h
  3961.        db   02Eh,005h,008h,000h,036h,005h,008h,000h
  3962.        db   03Eh,005h,008h,000h,046h,005h,008h,000h
  3963.        db   04Eh,005h,008h,000h,056h,005h,008h,000h
  3964.        db   05Eh,005h,008h,000h,066h,005h,008h,000h
  3965.        db   06Eh,005h,008h,000h,076h,005h,008h,000h
  3966.        db   07Eh,005h,008h,000h,086h,005h,008h,000h
  3967.        db   08Eh,005h,008h,000h,096h,005h,008h,000h
  3968.        db   09Eh,005h,008h,000h,0A6h,005h,008h,000h
  3969.        db   0AEh,005h,008h,000h,0B6h,005h,008h,000h
  3970.        db   0BEh,005h,008h,000h,0C6h,005h,008h,000h
  3971.        db   0CEh,005h,008h,000h,0D6h,005h,008h,000h
  3972.        db   0DEh,005h,008h,000h,0E6h,005h,008h,000h
  3973.        db   0EEh,005h,008h,000h,0F6h,005h,008h,000h
  3974.        db   0FEh,005h,008h,000h,006h,006h,008h,000h
  3975.        db   00Eh,006h,008h,000h,016h,006h,008h,000h
  3976.        db   01Eh,006h,008h,000h,026h,006h,008h,000h
  3977.        db   02Eh,006h,008h,000h,036h,006h,008h,000h
  3978.        db   03Eh,006h,008h,000h,046h,006h,008h,000h
  3979.        db   04Eh,006h,008h,000h,056h,006h,008h,000h
  3980.        db   05Eh,006h,008h,000h,066h,006h,008h,000h
  3981.        db   06Eh,006h,008h,000h,076h,006h,008h,000h
  3982.        db   07Eh,006h,008h,000h,086h,006h,008h,000h
  3983.        db   08Eh,006h,008h,000h,096h,006h,008h,000h
  3984.        db   09Eh,006h,008h,000h,0A6h,006h,008h,000h
  3985.        db   0AEh,006h,008h,000h,0B6h,006h,008h,000h
  3986.        db   0BEh,006h,008h,000h,0C6h,006h,008h,000h
  3987.        db   0CEh,006h,008h,000h,0D6h,006h,008h,000h
  3988.        db   0DEh,006h,008h,000h,0E6h,006h,008h,000h
  3989.        db   0EEh,006h,008h,000h,0F6h,006h,008h,000h
  3990.        db   0FEh,006h,008h,000h,006h,007h,008h,000h
  3991.        db   00Eh,007h,008h,000h,016h,007h,008h,000h
  3992.        db   01Eh,007h,008h,000h,026h,007h,008h,000h
  3993.        db   02Eh,007h,008h,000h,036h,007h,008h,000h
  3994.        db   03Eh,007h,008h,000h,046h,007h,008h,000h
  3995.        db   04Eh,007h,008h,000h,056h,007h,008h,000h
  3996.        db   05Eh,007h,008h,000h,066h,007h,008h,000h
  3997.        db   06Eh,007h,008h,000h,076h,007h,008h,000h
  3998.        db   07Eh,007h,008h,000h,086h,007h,008h,000h
  3999.        db   08Eh,007h,008h,000h,096h,007h,008h,000h
  4000.        db   09Eh,007h,008h,000h,0A6h,007h,008h,000h
  4001.        db   0AEh,007h,008h,000h,0B6h,007h,008h,000h
  4002.        db   0BEh,007h,008h,000h,0C6h,007h,008h,000h
  4003.        db   0CEh,007h,008h,000h,0D6h,007h,008h,000h
  4004.        db   0DEh,007h,008h,000h,0E6h,007h,008h,000h
  4005.        db   0EEh,007h,008h,000h,0F6h,007h,008h,000h
  4006.        db   0FEh,007h,008h,000h,006h,008h,008h,000h
  4007.        db   00Eh,008h,008h,000h,016h,008h,008h,000h
  4008.        db   01Eh,008h,008h,000h,026h,008h,008h,000h
  4009.        db   02Eh,008h,008h,000h,036h,008h,008h,000h
  4010.        db   03Eh,008h,008h,000h,046h,008h,008h,000h
  4011.        db   04Eh,008h,008h,000h,056h,008h,008h,000h
  4012.        db   05Eh,008h,008h,000h,066h,008h,008h,000h
  4013.        db   06Eh,008h,008h,000h,076h,008h,008h,000h
  4014.        db   07Eh,008h,008h,000h,086h,008h,008h,000h
  4015.        db   08Eh,008h,008h,000h,096h,008h,008h,000h
  4016.        db   09Eh,008h,008h,000h,0A6h,008h,008h,000h
  4017.        db   0AEh,008h,008h,000h,0B6h,008h,008h,000h
  4018.        db   0BEh,008h,008h,000h,0C6h,008h,008h,000h
  4019.        db   0CEh,008h,008h,000h,0D6h,008h,008h,000h
  4020.        db   0DEh,008h,008h,000h,0E6h,008h,008h,000h
  4021.        db   0EEh,008h,008h,000h,0F6h,008h,008h,000h
  4022.        db   0FEh,008h,008h,000h,006h,009h,008h,000h
  4023.        db   00Eh,009h,008h,000h,016h,009h,008h,000h
  4024.        db   01Eh,009h,008h,000h,026h,009h,008h,000h
  4025.        db   02Eh,009h,008h,000h,036h,009h,008h,000h
  4026.        db   03Eh,009h,008h,000h,046h,009h,008h,000h
  4027.        db   04Eh,009h,008h,000h,056h,009h,008h,000h
  4028.        db   05Eh,009h,008h,000h,066h,009h,008h,000h
  4029.        db   06Eh,009h,008h,000h,076h,009h,008h,000h
  4030.        db   07Eh,009h,008h,000h,086h,009h,008h,000h
  4031.        db   08Eh,009h,008h,000h,096h,009h,008h,000h
  4032.        db   09Eh,009h,008h,000h,0A6h,009h,008h,000h
  4033.        db   0AEh,009h,008h,000h,0B6h,009h,008h,000h
  4034.        db   0BEh,009h,008h,000h,0C6h,009h,008h,000h
  4035.        db   0CEh,009h,008h,000h,0D6h,009h,008h,000h
  4036.        db   0DEh,009h,008h,000h,0E6h,009h,008h,000h
  4037.        db   0EEh,009h,008h,000h,0F6h,009h,008h,000h
  4038.        db   0FEh,009h,008h,000h,006h,00Ah,008h,000h
  4039.        db   00Eh,00Ah,008h,000h,016h,00Ah,008h,000h
  4040.        db   01Eh,00Ah,008h,000h,026h,00Ah,008h,000h
  4041.        db   02Eh,00Ah,008h,000h,036h,00Ah,008h,000h
  4042.        db   03Eh,00Ah,008h,000h,046h,00Ah,008h,000h
  4043.        db   04Eh,00Ah,008h,000h,056h,00Ah,008h,000h
  4044.        db   05Eh,00Ah,008h,000h,066h,00Ah,008h,000h
  4045.        db   06Eh,00Ah,008h,000h,076h,00Ah,008h,000h
  4046.        db   07Eh,00Ah,008h,000h,086h,00Ah,008h,000h
  4047.        db   08Eh,00Ah,008h,000h,096h,00Ah,008h,000h
  4048.        db   09Eh,00Ah,008h,000h,0A6h,00Ah,008h,000h
  4049.        db   0AEh,00Ah,008h,000h,0B6h,00Ah,008h,000h
  4050.        db   0BEh,00Ah,008h,000h,0C6h,00Ah,008h,000h
  4051.        db   0CEh,00Ah,008h,000h,0D6h,00Ah,008h,000h
  4052.        db   0DEh,00Ah,008h,000h,0E6h,00Ah,008h,000h
  4053.        db   0EEh,00Ah,008h,000h,0F6h,00Ah,008h,000h
  4054.        db   0FEh,00Ah,008h,000h,006h,00Bh,008h,000h
  4055.        db   00Eh,00Bh,008h,000h,016h,00Bh,008h,000h
  4056.        db   01Eh,00Bh,008h,000h,026h,00Bh,008h,000h
  4057.        db   02Eh,00Bh,008h,000h,036h,00Bh,008h,000h
  4058.        db   03Eh,00Bh,008h,000h,046h,00Bh,008h,000h
  4059.        db   04Eh,00Bh,008h,000h,056h,00Bh,008h,000h
  4060.        db   05Eh,00Bh,008h,000h,066h,00Bh,008h,000h
  4061.        db   06Eh,00Bh,008h,000h,076h,00Bh,008h,000h
  4062.        db   07Eh,00Bh,008h,000h,086h,00Bh,008h,000h
  4063.        db   08Eh,00Bh,008h,000h,096h,00Bh,008h,000h
  4064.        db   09Eh,00Bh,008h,000h,0A6h,00Bh,008h,000h
  4065.        db   0AEh,00Bh,008h,000h,0B6h,00Bh,008h,000h
  4066.        db   0BEh,00Bh,008h,000h,0C6h,00Bh,008h,000h
  4067.        db   0CEh,00Bh,008h,000h,0D6h,00Bh,008h,000h
  4068.        db   0DEh,00Bh,008h,000h,0E6h,00Bh,008h,000h
  4069.        db   0EEh,00Bh,008h,000h,0F6h,00Bh,008h,000h
  4070.        db   0FEh,00Bh,008h,000h,006h,00Ch,008h,000h
  4071.        db   00Eh,00Ch,008h,000h,016h,00Ch,008h,000h
  4072.        db   01Eh,00Ch,008h,000h,026h,00Ch,008h,000h
  4073.        db   02Eh,00Ch,008h,000h,036h,00Ch,008h,000h
  4074.        db   03Eh,00Ch,008h,000h,046h,00Ch,008h,000h
  4075.        db   04Eh,00Ch,008h,000h,056h,00Ch,008h,000h
  4076.        db   05Eh,00Ch,008h,000h,066h,00Ch,008h,000h
  4077.        db   06Eh,00Ch,008h,000h,076h,00Ch,008h,000h
  4078.        db   07Eh,005h,049h,042h,04Dh,000h,000h,000h
  4079.        db   000h,000h,000h,000h,000h,000h,07Eh,081h
  4080.        db   0A5h,081h,0BDh,099h,081h,07Eh,07Eh,0FFh
  4081.        db   0DBh,0FFh,0C3h,0E7h,0FFh,07Eh,06Ch,0FEh
  4082.        db   0FEh,0FEh,07Ch,038h,010h,000h,010h,038h
  4083.        db   07Ch,0FEh,07Ch,038h,010h,000h,038h,07Ch
  4084.        db   038h,0FEh,0FEh,07Ch,038h,07Ch,010h,010h
  4085.        db   038h,07Ch,0FEh,07Ch,038h,07Ch,010h,010h
  4086.        db   038h,07Ch,0FEh,07Ch,038h,07Ch,0FFh,0FFh
  4087.        db   0E7h,0C3h,0C3h,0E7h,0FFh,0FFh,000h,000h
  4088.        db   000h,000h,000h,000h,000h,000h,000h,000h
  4089.        db   000h,000h,000h,000h,000h,000h,000h,000h
  4090.        db   000h,000h,000h,000h,000h,000h,000h,000h
  4091.        db   000h,000h,000h,000h,000h,000h,000h,000h
  4092.        db   000h,000h,000h,000h,000h,000h,07Fh,063h
  4093.        db   07Fh,063h,063h,067h,0E6h,0C0h,099h,05Ah
  4094.        db   03Ch,0E7h,0E7h,03Ch,05Ah,099h,080h,0E0h
  4095.        db   0F8h,0FEh,0F8h,0E0h,080h,000h,002h,00Eh
  4096.        db   03Eh,0FEh,03Eh,00Eh,002h,000h,018h,03Ch
  4097.        db   07Eh,018h,018h,07Eh,03Ch,018h,066h,066h
  4098.        db   066h,066h,066h,000h,066h,000h,07Fh,0DBh
  4099.        db   0DBh,07Bh,01Bh,01Bh,01Bh,000h,03Eh,063h
  4100.        db   038h,06Ch,06Ch,038h,0CCh,078h,000h,000h
  4101.        db   000h,000h,07Eh,07Eh,07Eh,000h,018h,03Ch
  4102.        db   07Eh,018h,07Eh,03Ch,018h,0FFh,018h,03Ch
  4103.        db   07Eh,018h,018h,018h,018h,000h,018h,018h
  4104.        db   018h,018h,07Eh,03Ch,018h,000h,000h,018h
  4105.        db   00Ch,0FEh,00Ch,018h,000h,000h,000h,030h
  4106.        db   060h,0FEh,060h,030h,000h,000h,000h,030h
  4107.        db   060h,0FEh,060h,030h,000h,000h,000h,030h
  4108.        db   060h,0FEh,060h,030h,000h,000h,000h,030h
  4109.        db   060h,0FEh,060h,030h,000h,000h,000h,030h
  4110.        db   060h,0FEh,060h,030h,000h,000h,000h,000h
  4111.        db   000h,000h,000h,000h,000h,000h,030h,078h
  4112.        db   078h,030h,030h,000h,030h,000h,06Ch,06Ch
  4113.        db   06Ch,000h,000h,000h,000h,000h,06Ch,06Ch
  4114.        db   0FEh,06Ch,0FEh,06Ch,06Ch,000h,030h,07Ch
  4115.        db   0C0h,078h,00Ch,0F8h,030h,000h,000h,0C6h
  4116.        db   0CCh,018h,030h,066h,0C6h,000h,038h,06Ch
  4117.        db   038h,076h,0DCh,0CCh,076h,000h,060h,060h
  4118.        db   0C0h,000h,000h,000h,000h,000h,018h,030h
  4119.        db   060h,060h,060h,030h,018h,000h,060h,030h
  4120.        db   018h,018h,018h,030h,060h,000h,000h,066h
  4121.        db   03Ch,0FFh,03Ch,066h,000h,000h,000h,030h
  4122.        db   030h,0FCh,030h,030h,000h,000h,000h,000h
  4123.        db   000h,000h,000h,030h,030h,060h,000h,000h
  4124.        db   000h,0FCh,000h,000h,000h,000h,000h,000h
  4125.        db   000h,000h,000h,030h,030h,000h,006h,00Ch
  4126.        db   018h,030h,060h,0C0h,080h,000h,07Ch,0C6h
  4127.        db   0CEh,0DEh,0F6h,0E6h,07Ch,000h,030h,070h
  4128.        db   030h,030h,030h,030h,0FCh,000h,078h,0CCh
  4129.        db   00Ch,038h,060h,0CCh,0FCh,000h,078h,0CCh
  4130.        db   00Ch,038h,00Ch,0CCh,078h,000h,01Ch,03Ch
  4131.        db   06Ch,0CCh,0FEh,00Ch,01Eh,000h,0FCh,0C0h
  4132.        db   0F8h,00Ch,00Ch,0CCh,078h,000h,038h,060h
  4133.        db   0C0h,0F8h,0CCh,0CCh,078h,000h,0FCh,0CCh
  4134.        db   00Ch,018h,030h,030h,030h,000h,078h,0CCh
  4135.        db   0CCh,078h,0CCh,0CCh,078h,000h,078h,0CCh
  4136.        db   0CCh,07Ch,00Ch,018h,070h,000h,000h,030h
  4137.        db   030h,000h,000h,030h,030h,000h,000h,030h
  4138.        db   030h,000h,000h,030h,030h,060h,018h,030h
  4139.        db   060h,0C0h,060h,030h,018h,000h,000h,000h
  4140.        db   0FCh,000h,000h,0FCh,000h,000h,060h,030h
  4141.        db   018h,00Ch,018h,030h,060h,000h,078h,0CCh
  4142.        db   00Ch,018h,030h,000h,030h,000h,07Ch,0C6h
  4143.        db   0DEh,0DEh,0DEh,0C0h,078h,000h,030h,078h
  4144.        db   0CCh,0CCh,0FCh,0CCh,0CCh,000h,0FCh,066h
  4145.        db   066h,07Ch,066h,066h,0FCh,000h,03Ch,066h
  4146.        db   0C0h,0C0h,0C0h,066h,03Ch,000h,0F8h,06Ch
  4147.        db   066h,066h,066h,06Ch,0F8h,000h,0FEh,062h
  4148.        db   068h,078h,068h,062h,0FEh,000h,0FEh,062h
  4149.        db   068h,078h,068h,060h,0F0h,000h,03Ch,066h
  4150.        db   0C0h,0C0h,0CEh,066h,03Eh,000h,0CCh,0CCh
  4151.        db   0CCh,0FCh,0CCh,0CCh,0CCh,000h,078h,030h
  4152.        db   030h,030h,030h,030h,078h,000h,01Eh,00Ch
  4153.        db   00Ch,00Ch,0CCh,0CCh,078h,000h,0E6h,066h
  4154.        db   06Ch,078h,06Ch,066h,0E6h,000h,0F0h,060h
  4155.        db   060h,060h,062h,066h,0FEh,000h,0C6h,0EEh
  4156.        db   0FEh,0FEh,0D6h,0C6h,0C6h,000h,0C6h,0E6h
  4157.        db   0F6h,0DEh,0CEh,0C6h,0C6h,000h,038h,06Ch
  4158.        db   0C6h,0C6h,0C6h,06Ch,038h,000h,0FCh,066h
  4159.        db   066h,07Ch,060h,060h,0F0h,000h,078h,0CCh
  4160.        db   0CCh,0CCh,0DCh,078h,01Ch,000h,0FCh,066h
  4161.        db   066h,07Ch,06Ch,066h,0E6h,000h,078h,0CCh
  4162.        db   0E0h,070h,01Ch,0CCh,078h,000h,0FCh,0B4h
  4163.        db   030h,030h,030h,030h,078h,000h,0CCh,0CCh
  4164.        db   0CCh,0CCh,0CCh,0CCh,0FCh,000h,0CCh,0CCh
  4165.        db   0CCh,0CCh,0CCh,078h,030h,000h,0C6h,0C6h
  4166.        db   0C6h,0D6h,0FEh,0EEh,0C6h,000h,0C6h,0C6h
  4167.        db   06Ch,038h,038h,06Ch,0C6h,000h,0CCh,0CCh
  4168.        db   0CCh,078h,030h,030h,078h,000h,0FEh,0C6h
  4169.        db   08Ch,018h,032h,066h,0FEh,000h,078h,060h
  4170.        db   060h,060h,060h,060h,078h,000h,0C0h,060h
  4171.        db   030h,018h,00Ch,006h,002h,000h,078h,018h
  4172.        db   018h,018h,018h,018h,078h,000h,010h,038h
  4173.        db   06Ch,0C6h,000h,000h,000h,000h,000h,000h
  4174.        db   000h,000h,000h,000h,000h,0FFh,030h,030h
  4175.        db   018h,000h,000h,000h,000h,000h,000h,000h
  4176.        db   078h,00Ch,07Ch,0CCh,076h,000h,0E0h,060h
  4177.        db   060h,07Ch,066h,066h,0DCh,000h,000h,000h
  4178.        db   078h,0CCh,0C0h,0CCh,078h,000h,01Ch,00Ch
  4179.        db   00Ch,07Ch,0CCh,0CCh,076h,000h,000h,000h
  4180.        db   078h,0CCh,0FCh,0C0h,078h,000h,038h,06Ch
  4181.        db   060h,0F0h,060h,060h,0F0h,000h,000h,000h
  4182.        db   076h,0CCh,0CCh,07Ch,00Ch,0F8h,0E0h,060h
  4183.        db   06Ch,076h,066h,066h,0E6h,000h,030h,000h
  4184.        db   070h,030h,030h,030h,078h,000h,00Ch,000h
  4185.        db   00Ch,00Ch,00Ch,0CCh,0CCh,078h,0E0h,060h
  4186.        db   066h,06Ch,078h,06Ch,0E6h,000h,070h,030h
  4187.        db   030h,030h,030h,030h,078h,000h,000h,000h
  4188.        db   0CCh,0FEh,0FEh,0D6h,0C6h,000h,000h,000h
  4189.        db   0F8h,0CCh,0CCh,0CCh,0CCh,000h,000h,000h
  4190.        db   078h,0CCh,0CCh,0CCh,078h,000h,000h,000h
  4191.        db   0DCh,066h,066h,07Ch,060h,0F0h,000h,000h
  4192.        db   076h,0CCh,0CCh,07Ch,00Ch,01Eh,000h,000h
  4193.        db   0DCh,076h,066h,060h,0F0h,000h,000h,000h
  4194.        db   07Ch,0C0h,078h,00Ch,0F8h,000h,010h,030h
  4195.        db   07Ch,030h,030h,034h,018h,000h,000h,000h
  4196.        db   0CCh,0CCh,0CCh,0CCh,076h,000h,000h,000h
  4197.        db   0CCh,0CCh,0CCh,078h,030h,000h,000h,000h
  4198.        db   0C6h,0D6h,0FEh,0FEh,06Ch,000h,000h,000h
  4199.        db   0C6h,06Ch,038h,06Ch,0C6h,000h,000h,000h
  4200.        db   0CCh,0CCh,0CCh,07Ch,00Ch,0F8h,000h,000h
  4201.        db   0FCh,098h,030h,064h,0FCh,000h,01Ch,030h
  4202.        db   030h,0E0h,030h,030h,01Ch,000h,018h,018h
  4203.        db   018h,000h,018h,018h,018h,000h,0E0h,030h
  4204.        db   030h,01Ch,030h,030h,0E0h,000h,076h,0DCh
  4205.        db   000h,000h,000h,000h,000h,000h,000h,010h
  4206.        db   038h,06Ch,0C6h,0C6h,0FEh,000h,078h,0CCh
  4207.        db   0C0h,0CCh,078h,018h,00Ch,078h,000h,0CCh
  4208.        db   000h,0CCh,0CCh,0CCh,07Eh,000h,01Ch,000h
  4209.        db   078h,0CCh,0FCh,0C0h,078h,000h,07Eh,0C3h
  4210.        db   03Ch,006h,03Eh,066h,03Fh,000h,0CCh,000h
  4211.        db   078h,00Ch,07Ch,0CCh,07Eh,000h,0E0h,000h
  4212.        db   078h,00Ch,07Ch,0CCh,07Eh,000h,030h,030h
  4213.        db   078h,00Ch,07Ch,0CCh,07Eh,000h,000h,000h
  4214.        db   078h,0C0h,0C0h,078h,00Ch,038h,07Eh,0C3h
  4215.        db   03Ch,066h,07Eh,060h,03Ch,000h,0CCh,000h
  4216.        db   078h,0CCh,0FCh,0C0h,078h,000h,0E0h,000h
  4217.        db   078h,0CCh,0FCh,0C0h,078h,000h,0CCh,000h
  4218.        db   070h,030h,030h,030h,078h,000h,07Ch,0C6h
  4219.        db   038h,018h,018h,018h,03Ch,000h,0E0h,000h
  4220.        db   070h,030h,030h,030h,078h,000h,0C6h,038h
  4221.        db   06Ch,0C6h,0FEh,0C6h,0C6h,000h,030h,030h
  4222.        db   000h,078h,0CCh,0FCh,0CCh,000h,01Ch,000h
  4223.        db   0FCh,060h,078h,060h,0FCh,000h,000h,000h
  4224.        db   07Fh,00Ch,07Fh,0CCh,07Fh,000h,03Eh,06Ch
  4225.        db   0CCh,0FEh,0CCh,0CCh,0CEh,000h,078h,0CCh
  4226.        db   000h,078h,0CCh,0CCh,078h,000h,000h,0CCh
  4227.        db   000h,078h,0CCh,0CCh,078h,000h,000h,0E0h
  4228.        db   000h,078h,0CCh,0CCh,078h,000h,078h,0CCh
  4229.        db   000h,0CCh,0CCh,0CCh,07Eh,000h,000h,0E0h
  4230.        db   000h,0CCh,0CCh,0CCh,07Eh,000h,000h,0CCh
  4231.        db   000h,0CCh,0CCh,07Ch,00Ch,0F8h,0C3h,018h
  4232.        db   03Ch,066h,066h,03Ch,018h,000h,0CCh,000h
  4233.        db   0CCh,0CCh,0CCh,0CCh,078h,000h,018h,018h
  4234.        db   07Eh,0C0h,0C0h,07Eh,018h,018h,038h,06Ch
  4235.        db   064h,0F0h,060h,0E6h,0FCh,000h,0CCh,0CCh
  4236.        db   078h,0FCh,030h,0FCh,030h,030h,0F8h,0CCh
  4237.        db   0CCh,0FAh,0C6h,0CFh,0C6h,0C7h,00Eh,01Bh
  4238.        db   018h,03Ch,018h,018h,0D8h,070h,01Ch,000h
  4239.        db   078h,00Ch,07Ch,0CCh,07Eh,000h,038h,000h
  4240.        db   070h,030h,030h,030h,078h,000h,000h,01Ch
  4241.        db   000h,078h,0CCh,0CCh,078h,000h,000h,01Ch
  4242.        db   000h,0CCh,0CCh,0CCh,07Eh,000h,000h,0F8h
  4243.        db   000h,0F8h,0CCh,0CCh,0CCh,000h,0FCh,000h
  4244.        db   0CCh,0ECh,0FCh,0DCh,0CCh,000h,03Ch,06Ch
  4245.        db   06Ch,03Eh,000h,07Eh,000h,000h,038h,06Ch
  4246.        db   06Ch,038h,000h,07Ch,000h,000h,030h,000h
  4247.        db   030h,060h,0C0h,0CCh,078h,000h,000h,000h
  4248.        db   000h,0FCh,0C0h,0C0h,000h,000h,000h,000h
  4249.        db   000h,0FCh,00Ch,00Ch,000h,000h,0C3h,0C6h
  4250.        db   0CCh,0DEh,033h,066h,0CCh,00Fh,0C3h,0C6h
  4251.        db   0CCh,0DBh,037h,06Fh,0CFh,003h,018h,018h
  4252.        db   000h,018h,018h,018h,018h,000h,000h,033h
  4253.        db   066h,0CCh,066h,033h,000h,000h,000h,0CCh
  4254.        db   066h,033h,066h,0CCh,000h,000h,022h,088h
  4255.        db   022h,088h,022h,088h,022h,088h,055h,0AAh
  4256.        db   055h,0AAh,055h,0AAh,055h,0AAh,0DBh,077h
  4257.        db   0DBh,0EEh,0DBh,077h,0DBh,0EEh,018h,018h
  4258.        db   018h,018h,018h,018h,018h,018h,018h,018h
  4259.        db   018h,018h,0F8h,018h,018h,018h,018h,018h
  4260.        db   0F8h,018h,0F8h,018h,018h,018h,036h,036h
  4261.        db   036h,036h,0F6h,036h,036h,036h,000h,000h
  4262.        db   000h,000h,0FEh,036h,036h,036h,000h,000h
  4263.        db   0F8h,018h,0F8h,018h,018h,018h,036h,036h
  4264.        db   0F6h,006h,0F6h,036h,036h,036h,036h,036h
  4265.        db   036h,036h,036h,036h,036h,036h,000h,000h
  4266.        db   0FEh,006h,0F6h,036h,036h,036h,036h,036h
  4267.        db   0F6h,006h,0FEh,000h,000h,000h,036h,036h
  4268.        db   036h,036h,0FEh,000h,000h,000h,018h,018h
  4269.        db   0F8h,018h,0F8h,000h,000h,000h,000h,000h
  4270.        db   000h,000h,0F8h,018h,018h,018h,018h,018h
  4271.        db   018h,018h,01Fh,000h,000h,000h,018h,018h
  4272.        db   018h,018h,0FFh,000h,000h,000h,000h,000h
  4273.        db   000h,000h,0FFh,018h,018h,018h,018h,018h
  4274.        db   018h,018h,01Fh,018h,018h,018h,000h,000h
  4275.        db   000h,000h,0FFh,000h,000h,000h,018h,018h
  4276.        db   018h,018h,0FFh,018h,018h,018h,018h,018h
  4277.        db   01Fh,018h,01Fh,018h,018h,018h,036h,036h
  4278.        db   036h,036h,037h,036h,036h,036h,036h,036h
  4279.        db   037h,030h,03Fh,000h,000h,000h,000h,000h
  4280.        db   03Fh,030h,037h,036h,036h,036h,036h,036h
  4281.        db   0F7h,000h,0FFh,000h,000h,000h,000h,000h
  4282.        db   0FFh,000h,0F7h,036h,036h,036h,036h,036h
  4283.        db   037h,030h,037h,036h,036h,036h,000h,000h
  4284.        db   0FFh,000h,0FFh,000h,000h,000h,036h,036h
  4285.        db   0F7h,000h,0F7h,036h,036h,036h,018h,018h
  4286.        db   0FFh,000h,0FFh,000h,000h,000h,036h,036h
  4287.        db   036h,036h,0FFh,000h,000h,000h,000h,000h
  4288.        db   0FFh,000h,0FFh,018h,018h,018h,000h,000h
  4289.        db   000h,000h,0FFh,036h,036h,036h,036h,036h
  4290.        db   036h,036h,03Fh,000h,000h,000h,018h,018h
  4291.        db   01Fh,018h,01Fh,000h,000h,000h,000h,000h
  4292.        db   01Fh,018h,01Fh,018h,018h,018h,000h,000h
  4293.        db   000h,000h,03Fh,036h,036h,036h,036h,036h
  4294.        db   036h,036h,0FFh,036h,036h,036h,018h,018h
  4295.        db   0FFh,018h,0FFh,018h,018h,018h,018h,018h
  4296.        db   018h,018h,0F8h,000h,000h,000h,000h,000h
  4297.        db   000h,000h,01Fh,018h,018h,018h,0FFh,0FFh
  4298.        db   0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,000h,000h
  4299.        db   000h,000h,0FFh,0FFh,0FFh,0FFh,0F0h,0F0h
  4300.        db   0F0h,0F0h,0F0h,0F0h,0F0h,0F0h,00Fh,00Fh
  4301.        db   00Fh,00Fh,00Fh,00Fh,00Fh,00Fh,0FFh,0FFh
  4302.        db   0FFh,0FFh,000h,000h,000h,000h,000h,000h
  4303.        db   076h,0DCh,0C8h,0DCh,076h,000h,000h,078h
  4304.        db   0CCh,0F8h,0CCh,0F8h,0C0h,0C0h,000h,0FCh
  4305.        db   0CCh,0C0h,0C0h,0C0h,0C0h,000h,000h,0FEh
  4306.        db   06Ch,06Ch,06Ch,06Ch,06Ch,000h,0FCh,0CCh
  4307.        db   060h,030h,060h,0CCh,0FCh,000h,000h,000h
  4308.        db   07Eh,0D8h,0D8h,0D8h,070h,000h,000h,066h
  4309.        db   066h,066h,066h,07Ch,060h,0C0h,000h,076h
  4310.        db   0DCh,018h,018h,018h,018h,000h,0FCh,030h
  4311.        db   078h,0CCh,0CCh,078h,030h,0FCh,038h,06Ch
  4312.        db   0C6h,0FEh,0C6h,06Ch,038h,000h,038h,06Ch
  4313.        db   0C6h,0C6h,06Ch,06Ch,0EEh,000h,01Ch,030h
  4314.        db   018h,07Ch,0CCh,0CCh,078h,000h,000h,000h
  4315.        db   07Eh,0DBh,0DBh,07Eh,000h,000h,006h,00Ch
  4316.        db   07Eh,0DBh,0DBh,07Eh,060h,0C0h,038h,060h
  4317.        db   0C0h,0F8h,0C0h,060h,038h,000h,078h,0CCh
  4318.        db   0CCh,0CCh,0CCh,0CCh,0CCh,000h,000h,0FCh
  4319.        db   000h,0FCh,000h,0FCh,000h,000h,030h,030h
  4320.        db   0FCh,030h,030h,000h,0FCh,000h,060h,030h
  4321.        db   018h,030h,060h,000h,0FCh,000h,018h,030h
  4322.        db   060h,030h,018h,000h,0FCh,000h,00Eh,01Bh
  4323.        db   01Bh,018h,018h,018h,018h,018h,018h,018h
  4324.        db   018h,018h,018h,0D8h,0D8h,070h,030h,030h
  4325.        db   000h,0FCh,000h,030h,030h,000h,000h,076h
  4326.        db   0DCh,000h,076h,0DCh,000h,000h,038h,06Ch
  4327.        db   06Ch,038h,000h,000h,000h,000h,000h,000h
  4328.        db   000h,018h,018h,000h,000h,000h,000h,000h
  4329.        db   000h,000h,018h,000h,000h,000h,00Fh,00Ch
  4330.        db   00Ch,00Ch,0ECh,06Ch,03Ch,01Ch,078h,06Ch
  4331.        db   06Ch,06Ch,06Ch,000h,000h,000h,070h,018h
  4332.        db   030h,060h,078h,000h,000h,000h,000h,000h
  4333.        db   03Ch,03Ch,03Ch,03Ch,000h,000h,000h,000h
  4334.        db   000h,000h,000h,000h,000h,000h
  4335.  
  4336.  ;=====End of Font
  4337.  
  4338.  .CODE
  4339.  
  4340.  ;********************************************************
  4341.  ;DefaultFont - Returns the Segment:Offset address of the
  4342.  ;               default font
  4343.  ;
  4344.  ; DefaultFont Segment%, Offset%
  4345.  
  4346.  PUBLIC DefaultFont
  4347.  DefaultFont PROC
  4348.     push  bp
  4349.     mov   bp,sp
  4350.  
  4351.     les         bx,[bp+10]                ;put address of first arg in es:si
  4352.     mov         es:[bx],SEG _IBM8_def        ;move segment address to first ar
  4353.  
  4354.     les         bx,[bp+6]                ;repeat above for offset address of f
  4355.     mov         word ptr es:[bx],OFFSET _IBM8_def
  4356.  
  4357.     pop   bp
  4358.     ret   8
  4359.  DefaultFont ENDP
  4360.  
  4361.        END
  4362.  
  4363.  
  4364.  CHRTB.BAS
  4365.  CD-ROM Disc Path:   \SAMPCODE\BASIC\CHRTB.BAS
  4366.  
  4367.  '*** CHRTB.BAS - Chart Routines for the Presentation Graphics Toolbox in
  4368.  '           Microsoft BASIC 7.0, Professional Development System
  4369.  '              Copyright (C) 1987-1989, Microsoft Corporation
  4370.  '
  4371.  '  NOTE:  This sample source code toolbox is intended to demonstrate some
  4372.  '  of the extended capabilities of Microsoft BASIC 7.0 Professional Developme
  4373.  '  system that can help to leverage the professional developer's time more
  4374.  '  effectively.  While you are free to use, modify, or distribute the routine
  4375.  '  in this module in any way you find useful, it should be noted that these a
  4376.  '  examples only and should not be relied upon as a fully-tested "add-on"
  4377.  '  library.
  4378.  '
  4379.  '  PURPOSE: This file contains the BASIC source code for the Presentation
  4380.  '           Graphics Toolbox Chart Routines.
  4381.  '
  4382.  '  To create a library and QuickLib containing the charting routines found
  4383.  '  in this file, follow these steps:
  4384.  '       BC /X/FS chrtb.bas
  4385.  '       LIB chrtb.lib + chrtb + chrtasm + qbx.lib;
  4386.  '       LINK /Q chrtb.lib, chrtb.qlb,,qbxqlb.lib;
  4387.  '  If you are going to use this CHRTB.QLB QuickLib in conjunction with
  4388.  '  the font source code (FONTB.BAS) or the UI toobox source code
  4389.  '  (GENERAL.BAS, WINDOW.BAS, MENU.BAS and MOUSE.BAS), you need to
  4390.  '  include the assembly code routines referenced in these files.  For the
  4391.  '  font routines, create CHRTB.LIB as follows before you create the
  4392.  '  QuickLib:
  4393.  '       LIB chrtb.lib + chrtb + chrtasm + fontasm + qbx.lib;
  4394.  '  For the UI toolbox routines, create the library as follows:
  4395.  '       LIB chrtb.lib + chrtb + chrtasm + uiasm + qbx.lib;
  4396.  '**************************************************************************
  4397.  
  4398.  ' Constants:
  4399.  
  4400.  CONST cTicSize = .02            ' Percent of axis length to use for tic lengt
  4401.  CONST cMaxChars = 255           ' Maximum ASCII value allowed for character
  4402.  CONST cBarWid = .8              ' Percent of category width to use for bar
  4403.  CONST cPiVal = 3.141592         ' A value for PI
  4404.  CONST cFalse = 0                ' Logical false
  4405.  CONST cTrue = NOT cFalse        ' Logical true
  4406.  
  4407.  ' CHRTB.BI contains all of the TYPE definitions and SUB declarations
  4408.  ' that are accessible to the library user as well as CONST definitions for
  4409.  ' some routine parameters and error messages:
  4410.  
  4411.  '$INCLUDE: 'CHRTB.BI'
  4412.  
  4413.  ' FONTB.BI contains all of the TYPE definitions and SUB declarations
  4414.  ' required for graphics text:
  4415.  
  4416.  '$INCLUDE: 'FONTB.BI'
  4417.  
  4418.  ' Below are TYPE definitions local to this module:
  4419.  
  4420.  ' TYPE for recording information on title spacing:
  4421.  TYPE TitleLayout
  4422.          Top         AS INTEGER        ' Space above first title
  4423.          TitleOne    AS INTEGER        ' Height of first title
  4424.          Middle      AS INTEGER        ' Space between first and second titles
  4425.          TitleTwo    AS INTEGER        ' Height of second title
  4426.          Bottom      AS INTEGER        ' Space below second title
  4427.          TotalSize   AS INTEGER        ' Sum of all the above
  4428.  END TYPE
  4429.  
  4430.  ' TYPE for recording information on the legend layout:
  4431.  TYPE LegendLayout
  4432.          NumCol      AS INTEGER        ' Number of columns in legend
  4433.          NumRow      AS INTEGER        ' Number of rows in legend
  4434.          SymbolSize  AS INTEGER        ' Height of symbol
  4435.          LabelOffset AS INTEGER        ' Space between start of symbol and lab
  4436.          RowSpacing  AS INTEGER        ' Space between tops of rows
  4437.          ColSpacing  AS INTEGER        ' Spacing between beginnings of columns
  4438.          HorizBorder AS INTEGER        ' Top and bottom border
  4439.          VertBorder  AS INTEGER        ' Left and right border
  4440.  END TYPE
  4441.  
  4442.  ' TYPE for a group of global parameters:
  4443.  TYPE GlobalParams
  4444.          SysFlag     AS INTEGER        ' cYes means Analyze call is from syste
  4445.          Initialized AS INTEGER        ' cYes means clInitChart has been calle
  4446.  
  4447.          PaletteScrn AS INTEGER        ' Screen mode for which palette is set
  4448.          PaletteBits AS INTEGER        ' Bits per pixel for current screen mod
  4449.          PaletteSet  AS INTEGER        ' cYes means palette has been initializ
  4450.          White       AS INTEGER        ' White attribute in current screen mod
  4451.  
  4452.          Aspect      AS SINGLE         ' Current screen aspect
  4453.          MaxXPix     AS INTEGER        ' Screen size along X axis
  4454.          MaxYPix     AS INTEGER        ' Screen size along Y axis
  4455.          MaxColor    AS INTEGER        ' Maximum color number for current scre
  4456.  
  4457.          ChartWid    AS INTEGER        ' Width of chart window
  4458.          ChartHgt    AS INTEGER        ' Height of chart window
  4459.          CwX1        AS INTEGER        ' Left side of chart window
  4460.          CwY1        AS INTEGER        ' Top edge of chart window
  4461.          CwX2        AS INTEGER        ' Right side of chart window
  4462.          CwY2        AS INTEGER        ' Bottom edge of chart window
  4463.  
  4464.          XStagger    AS INTEGER        ' Boolean, true if category labels over
  4465.          ValLenX     AS INTEGER        ' Maximum length of value labels on X-a
  4466.          ValLenY     AS INTEGER        ' Maximum length of value labels on Y-a
  4467.  
  4468.          NVals       AS INTEGER        ' Number of data values in data series
  4469.          NSeries     AS INTEGER        ' Number of series of data
  4470.          MSeries     AS INTEGER        ' If multiple-series chart then cYes, e
  4471.  
  4472.          XMode       AS INTEGER        ' Axis mode of x axis
  4473.          YMode       AS INTEGER        ' Axis mode of y axis
  4474.  END TYPE
  4475.  
  4476.  ' FUNCTION and SUB declarations for procedures local to this module:
  4477.  
  4478.  DECLARE FUNCTION clBuildBitP$ (Bits%, C%, InP$)
  4479.  DECLARE FUNCTION clBuildPlaneP$ (Bits%, C%, InP$)
  4480.  DECLARE FUNCTION clColorMaskL% (Bits%, Colr%)
  4481.  DECLARE FUNCTION clGetStyle% (StyleNum%)
  4482.  DECLARE FUNCTION clMaxVal (A, B)
  4483.  DECLARE FUNCTION clMap2Pal% (N%)
  4484.  DECLARE FUNCTION clMap2Attrib% (N%)
  4485.  DECLARE FUNCTION clMaxStrLen% (Txt$(), First%, Last%)
  4486.  DECLARE FUNCTION clVal2Str$ (X, Places%, Format%)
  4487.  
  4488.  DECLARE SUB clAdjustScale (Axis AS AxisType)
  4489.  DECLARE SUB clAnalyzeC (Cat$(), N%, SLabels$(), First%, Last%)
  4490.  DECLARE SUB clAnalyzeS (N%, SLabels$(), First%, Last%)
  4491.  DECLARE SUB clBuildPalette (ScrnMode%, Bits%)
  4492.  DECLARE SUB clChkInit ()
  4493.  DECLARE SUB clChkFonts ()
  4494.  DECLARE SUB clChkForErrors (Env AS ChartEnvironment, TypeMin%, TypeMax%, N%,
  4495.  DECLARE SUB clChkChartWindow (Env AS ChartEnvironment)
  4496.  DECLARE SUB clChkPalettes (C%(), s%(), P$(), Char%(), B%())
  4497.  DECLARE SUB clClearError ()
  4498.  DECLARE SUB clColorMaskH (Bits%, Colr%, CMask%())
  4499.  DECLARE SUB clDrawAxes (Cat$())
  4500.  DECLARE SUB clDrawDataWindow ()
  4501.  DECLARE SUB clDrawChartWindow ()
  4502.  DECLARE SUB clDrawTitles ()
  4503.  DECLARE SUB clDrawLegend (SeriesLabel$(), First AS INTEGER, Last AS INTEGER)
  4504.  DECLARE SUB clDrawBarData ()
  4505.  DECLARE SUB clDrawColumnData ()
  4506.  DECLARE SUB clDrawLineData ()
  4507.  DECLARE SUB clDrawPieData (value(), Expl%(), N%)
  4508.  DECLARE SUB clDrawScatterData ()
  4509.  DECLARE SUB clFilter (A AS AxisType, AxisMode%, D1(), D2(), N%)
  4510.  DECLARE SUB clFilterMS (A AS AxisType, AxisMode%, D1(), D2(), N%, First%, Las
  4511.  DECLARE SUB clFlagSystem ()
  4512.  DECLARE SUB clFormatTics (A AS AxisType)
  4513.  DECLARE SUB clHPrint (X%, Y%, Txt$)
  4514.  DECLARE SUB clInitChart ()
  4515.  DECLARE SUB clInitStdStruc ()
  4516.  DECLARE SUB clLabelXTics (Axis AS AxisType, Cat$(), TicX, TicTotX%, TicY, YBo
  4517.  DECLARE SUB clLabelYTics (Axis AS AxisType, Cat$(), TicX, TicY, TicTotY%)
  4518.  DECLARE SUB clLayoutTitle (TL AS ANY, T1 AS ANY, T2 AS ANY)
  4519.  DECLARE SUB clPrintTitle (TitleVar AS TitleType, Y%)
  4520.  DECLARE SUB clRenderBar (X1, Y1, X2, Y2, C%)
  4521.  DECLARE SUB clRenderWindow (W AS RegionType)
  4522.  DECLARE SUB clScaleAxis (A AS AxisType, AxisMode%, D1())
  4523.  DECLARE SUB clSelectChartWindow ()
  4524.  DECLARE SUB clSelectRelWindow (W AS RegionType)
  4525.  DECLARE SUB clSetAxisModes ()
  4526.  DECLARE SUB clSetChartFont (N AS INTEGER)
  4527.  DECLARE SUB clSetError (ErrNo AS INTEGER)
  4528.  DECLARE SUB clSetCharColor (N%)
  4529.  DECLARE SUB clSetGlobalParams ()
  4530.  DECLARE SUB clSizeDataWindow (Cat$())
  4531.  DECLARE SUB clLayoutLegend (SeriesLabel$(), First%, Last%)
  4532.  DECLARE SUB clSpaceTics ()
  4533.  DECLARE SUB clSpaceTicsA (A AS AxisType, AxisMode%, AxisLen%, TicWid%)
  4534.  DECLARE SUB clTitleXAxis (A AS AxisType, X1%, X2%, YBoundry%)
  4535.  DECLARE SUB clTitleYAxis (A AS AxisType, Y1%, Y2%)
  4536.  DECLARE SUB clUnFlagSystem ()
  4537.  DECLARE SUB clVPrint (X%, Y%, Txt$)
  4538.  
  4539.  
  4540.  ' Variable definitions local to this module:
  4541.  
  4542.  DIM PaletteC%(0 TO cPalLen)            ' List of colors     for drawing data
  4543.  DIM PaletteS%(0 TO cPalLen)            ' List of styles     for drawing data
  4544.  DIM PaletteP$(0 TO cPalLen)            ' List of patterns   for drawing data
  4545.  DIM PaletteCh%(0 TO cPalLen)           ' List of plot chars for drawing data
  4546.  DIM PaletteB%(0 TO cPalLen)            ' List of patterns   for borders
  4547.  
  4548.  DIM StdChars%(0 TO cPalLen)            ' Holds default plot characters
  4549.  
  4550.  DIM DAxis         AS AxisType          ' Default axis settings
  4551.  DIM DWindow       AS RegionType        ' Default window settings
  4552.  DIM DLegend       AS LegendType        ' Default legend settings
  4553.  DIM DTitle        AS TitleType         ' Default title settings
  4554.  
  4555.  DIM XTitleLayout  AS TitleLayout       ' X-axis layout information
  4556.  DIM YTitleLayout  AS TitleLayout       ' Y-axis layout information
  4557.  DIM TTitleLayout  AS TitleLayout       ' Main/Sub layout information
  4558.  
  4559.  DIM LLayout       AS LegendLayout      ' Legend layout information
  4560.  
  4561.  DIM GFI           AS FontInfo          ' Global font information
  4562.  DIM GE            AS ChartEnvironment  ' An internal global chart environment
  4563.  DIM GP            AS GlobalParams      ' Holds a number of global parameters
  4564.  
  4565.  
  4566.  
  4567.  '$DYNAMIC
  4568.  DIM V1(1, 1), V2(1, 1)                 ' Internal dynamic data arrays.
  4569.  '$STATIC
  4570.  
  4571.  '============================================================
  4572.  '==============      Main Level Code     ====================
  4573.  '============================================================
  4574.  
  4575.  ' This error trap is set in the ChartScreen routine and will
  4576.  ' be evoked if an invalid screen mode is used:
  4577.  ScreenErr:
  4578.          clSetError cBadScreen
  4579.          RESUME NEXT
  4580.  
  4581.  ' This error trap should catch all errors that arise in using
  4582.  ' the charting library that are not expected:
  4583.  UnexpectedErr:
  4584.          clSetError cCLUnexpectedOff + ERR
  4585.          RESUME NEXT
  4586.  
  4587.  '=== AnalyzeChart - Sets up scales and data window sizes
  4588.  '
  4589.  '  Arguments:
  4590.  '     Env        - A ChartEnvironment variable
  4591.  '
  4592.  '     Cat$(1)    - One-dimensional array of category labels
  4593.  '
  4594.  '     Value(1)   - One-dimensional array of values to chart
  4595.  '
  4596.  '     N%         - The number of data values in data series
  4597.  '
  4598.  '  Return Values:
  4599.  '     Scale and Data-Window values are changed as appropriate.
  4600.  '
  4601.  '=================================================================
  4602.  SUB AnalyzeChart (Env AS ChartEnvironment, Cat$(), value(), N AS INTEGER)
  4603.  
  4604.  SHARED GE AS ChartEnvironment, GP AS GlobalParams
  4605.  SHARED V1()
  4606.  REDIM V1(1 TO N%, 1 TO 1)
  4607.  DIM Dum$(1 TO 1)
  4608.  
  4609.          ' Check initialization and fonts:
  4610.          clClearError
  4611.          clChkInit
  4612.          clChkFonts
  4613.          IF ChartErr >= 100 THEN EXIT SUB
  4614.  
  4615.          ' Set a global flag to indicate that this isn't a multiple-series cha
  4616.          GP.MSeries = cNo
  4617.  
  4618.          ' Check for obvious parameter and ChartEnvironment errors:
  4619.          clChkForErrors Env, 1, 3, N, 0, 0
  4620.          IF ChartErr > 100 THEN EXIT SUB
  4621.  
  4622.          ' Make a copy of the user's ChartEnvironment variable to the library'
  4623.          ' global environment variable:
  4624.          GE = Env
  4625.  
  4626.          ' Set the correct axis modes for the type of chart specified in the
  4627.          ' chart environment:
  4628.          clSetAxisModes
  4629.  
  4630.          ' Transfer the input data to the dynamic working data array.  Do this
  4631.          ' for each axis because, depending on the chart type, either one may
  4632.          ' the value axis.  The Filter routine automatically ignores the call
  4633.          ' the axis is a category axis:
  4634.          clFilter GE.XAxis, GP.XMode, value(), V1(), N
  4635.          clFilter GE.YAxis, GP.YMode, value(), V1(), N
  4636.  
  4637.          ' Analyze the data for scale-maximum and -minimum and set the scale-
  4638.          ' factor, etc. depending on the options set in the chart environment:
  4639.          clAnalyzeC Cat$(), N, Dum$(), 1, 1
  4640.  
  4641.          ' Copy the global chart environment back to the user's ChartEnvironme
  4642.          ' variable so that the settings that were calculated by the library a
  4643.          ' accessible.  Then, if this routine wasn't called by the library its
  4644.          ' in the course of drawing a bar, column or line chart, deallocate th
  4645.          ' working data array:
  4646.          Env = GE
  4647.          IF GP.SysFlag = cNo THEN ERASE V1
  4648.  
  4649.  END SUB
  4650.  
  4651.  '=== AnalyzeChartMS - Analyzes multiple-series data for scale/window size.
  4652.  '
  4653.  '  Arguments:
  4654.  '     Env             - ChartEnvironment variable
  4655.  '
  4656.  '     Cat$(1)         - One-dimensional array of category labels
  4657.  '
  4658.  '     Value(2)        - Two-dimensional array of values to chart.  First
  4659.  '                       dimension (rows) represents different values within
  4660.  '                       a series.  Second dimension (columns) represents
  4661.  '                       different series.
  4662.  '
  4663.  '     N%              - Number of values (beginning with 1) to chart per
  4664.  '                       series.
  4665.  '
  4666.  '     First%          - First series to analyze
  4667.  '
  4668.  '     Last%           - Last series to analyze
  4669.  '
  4670.  '     SeriesLabel$(1) - Labels for the different series
  4671.  '
  4672.  '  Return Values:
  4673.  '     Various settings in the Env variable are altered in accordance with
  4674.  '     the analysis.
  4675.  '
  4676.  '=================================================================
  4677.  SUB AnalyzeChartMS (Env AS ChartEnvironment, Cat$(), value() AS SINGLE, N AS
  4678.  
  4679.  SHARED GE AS ChartEnvironment, GP AS GlobalParams
  4680.  SHARED V1()
  4681.  REDIM V1(1 TO N, 1 TO Last - First + 1)
  4682.  
  4683.          ' Check initialization and fonts:
  4684.          clClearError
  4685.          clChkInit
  4686.          clChkFonts
  4687.          IF ChartErr >= 100 THEN EXIT SUB
  4688.  
  4689.          ' Set a global flag to indicate that this is a multiple-series chart:
  4690.          GP.MSeries = cYes
  4691.  
  4692.          ' Check for obvious parameter and ChartEnvironment errors:
  4693.          clChkForErrors Env, 1, 3, N, 0, 0
  4694.          IF ChartErr > 100 THEN EXIT SUB
  4695.  
  4696.          ' Make a copy of the user's ChartEnvironment variable to the library'
  4697.          ' global environment variable:
  4698.          GE = Env
  4699.  
  4700.          ' Set the correct axis modes for the type of chart specified in the
  4701.          ' chart environment:
  4702.          clSetAxisModes
  4703.  
  4704.          ' Transfer the input data to the dynamic working data array.  Do this
  4705.          ' for each axis because, depending on the chart type, either one may
  4706.          ' the value axis.  The Filter routine automatically ignores the call
  4707.          ' the axis is a category axis:
  4708.          clFilterMS GE.XAxis, GP.XMode, value(), V1(), N, First, Last
  4709.          clFilterMS GE.YAxis, GP.YMode, value(), V1(), N, First, Last
  4710.  
  4711.          ' Analyze the data for scale maximums and minimums and set the scale
  4712.          ' factor, etc. depending on the options set in the chart environment:
  4713.          clAnalyzeC Cat$(), N, SeriesLabel$(), First, Last
  4714.  
  4715.          ' Copy the global chart environment back to the user's ChartEnvironme
  4716.          ' variable so that the settings that were calculated by the library a
  4717.          ' accessible.  Then, if this routine wasn't called by the library its
  4718.          ' in the course of drawing a bar, column or line chart, deallocate th
  4719.          ' working data array:
  4720.          Env = GE
  4721.          IF GP.SysFlag = cNo THEN ERASE V1
  4722.  
  4723.  END SUB
  4724.  
  4725.  '=== AnalyzePie - Analyzes data for a pie chart
  4726.  '
  4727.  '  Arguments:
  4728.  '     Env      - A ChartEnvironment variable
  4729.  '
  4730.  '     Cat$()   - One-dimensional array of category names
  4731.  '
  4732.  '     Value()  - One-dimensional array of values to chart
  4733.  '
  4734.  '     Expl()   - One dimensional array of flags indicating whether slices
  4735.  '                are to be "exploded" (0 means no, 1 means yes).
  4736.  '                Ignored if Env.ChartStyle <> 1.
  4737.  '
  4738.  '     N        - The number of values to chart
  4739.  '
  4740.  '  Return Values:
  4741.  '     None.
  4742.  '
  4743.  '=================================================================
  4744.  SUB AnalyzePie (Env AS ChartEnvironment, Cat$(), value() AS SINGLE, Expl() AS
  4745.  SHARED GE AS ChartEnvironment
  4746.  SHARED GP AS GlobalParams
  4747.  SHARED TTitleLayout AS TitleLayout
  4748.  SHARED XTitleLayout AS TitleLayout
  4749.  SHARED YTitleLayout AS TitleLayout
  4750.  SHARED V1()
  4751.  DIM EmptyTitle AS TitleType
  4752.  
  4753.          ' Check initialization and fonts:
  4754.          clClearError
  4755.          clChkInit
  4756.          clChkFonts
  4757.          IF ChartErr >= 100 THEN EXIT SUB
  4758.  
  4759.          ' This is a multiple series chart (a pie chart is treated as a
  4760.          ' multiple series chart with each series having one value):
  4761.          GP.MSeries = cYes
  4762.          GP.NSeries = N
  4763.  
  4764.          ' Check for obvious parameter and ChartEnvironment errors:
  4765.          clChkForErrors Env, cPie, cPie, 2, 1, N
  4766.          IF ChartErr > 100 THEN EXIT SUB
  4767.  
  4768.          ' Make a copy of the user's ChartEnvironment variable to the library'
  4769.          ' global environment variable:
  4770.          GE = Env
  4771.  
  4772.          ' Set the correct axis modes for the type of chart specified in the
  4773.          ' chart environment:
  4774.          clSetAxisModes
  4775.  
  4776.          ' Set global parameters and layout main title:
  4777.          clSetGlobalParams
  4778.  
  4779.          ' Layout titles (ignore X and Y axis titles):
  4780.          clLayoutTitle TTitleLayout, GE.MainTitle, GE.SubTitle
  4781.          EmptyTitle.Title = ""
  4782.          clLayoutTitle XTitleLayout, EmptyTitle, EmptyTitle
  4783.          clLayoutTitle YTitleLayout, EmptyTitle, EmptyTitle
  4784.  
  4785.          ' Calculate the size for LegendWindow and DataWindow:
  4786.          clLayoutLegend Cat$(), 1, N
  4787.          IF ChartErr > 100 THEN EXIT SUB
  4788.          clSizeDataWindow Cat$()
  4789.          IF ChartErr > 100 THEN EXIT SUB
  4790.  
  4791.          ' Copy the global chart environment back to the user's ChartEnvironme
  4792.          ' variable so that the settings that were calculated by the library a
  4793.          ' accessible.  Then, if this routine wasn't called by the library its
  4794.          ' in the course of drawing a pie chart, deallocate the working data a
  4795.          Env = GE
  4796.  
  4797.  END SUB
  4798.  
  4799.  '=== AnalyzeScatter - Sets up scales and data-window sizes for scatter chart
  4800.  '
  4801.  '  Arguments:
  4802.  '     Env        - A ChartEnvironment variable
  4803.  '
  4804.  '     ValX(1)    - One-dimensional array of values for X axis
  4805.  '
  4806.  '     ValY(1)    - One-dimensional array of values for Y axis
  4807.  '
  4808.  '     N%         - The number of data values in data series
  4809.  '
  4810.  '  Return Values:
  4811.  '     Scale and data-window values are changed as appropriate.
  4812.  '
  4813.  '=================================================================
  4814.  SUB AnalyzeScatter (Env AS ChartEnvironment, ValX() AS SINGLE, ValY() AS SING
  4815.  
  4816.  SHARED GE AS ChartEnvironment, GP AS GlobalParams
  4817.  SHARED V1(), V2()
  4818.  REDIM V1(1 TO N, 1 TO 1), V2(1 TO N, 1 TO 1)
  4819.  DIM Dum$(1 TO 1)
  4820.  
  4821.          ' Check initialization and fonts:
  4822.          clClearError
  4823.          clChkInit
  4824.          clChkFonts
  4825.          IF ChartErr >= 100 THEN EXIT SUB
  4826.  
  4827.          ' Set a global flag to indicate that this isn't a multiple-series cha
  4828.          GP.MSeries = cNo
  4829.  
  4830.          ' Check for obvious parameter and ChartEnvironment errors:
  4831.          clChkForErrors Env, 4, 4, N%, 0, 0
  4832.          IF ChartErr > 100 THEN EXIT SUB
  4833.  
  4834.          ' Make a copy of the user's ChartEnvironment variable to the library'
  4835.          ' global environment variable:
  4836.          GE = Env
  4837.  
  4838.          ' Set the correct axis modes for the type of chart specified in the
  4839.          ' chart environment:
  4840.          clSetAxisModes
  4841.  
  4842.          ' Transfer the input data to the dynamic working data arrays (one
  4843.          ' for each axis):
  4844.          clFilter GE.XAxis, GP.XMode, ValX(), V1(), N
  4845.          clFilter GE.YAxis, GP.YMode, ValY(), V2(), N
  4846.  
  4847.          ' Analyze the data for scale-maximum and -minimum and set the scale-
  4848.          ' factor, etc. depending on the options set in the chart environment:
  4849.          clAnalyzeS N, Dum$(), 1, 1
  4850.  
  4851.          ' Copy the global chart environment back to the user's ChartEnvironme
  4852.          ' variable so that the settings that were calculated by the library a
  4853.          ' accessible.  Then, if this routine wasn't called by the library its
  4854.          ' in the course of drawing a scatter chart, deallocate the working
  4855.          ' data arrays:
  4856.          Env = GE
  4857.          IF GP.SysFlag = cNo THEN ERASE V1, V2
  4858.  
  4859.  END SUB
  4860.  
  4861.  '=== AnalyzeScatterMS - Analyzes multiple-series data for scale/window size
  4862.  '
  4863.  '  Arguments:
  4864.  '     Env             - A ChartEnvironment variable
  4865.  '
  4866.  '     ValX(2)         - Two-dimensional array of values for X axis.  First
  4867.  '                       dimension (rows) represents different values within
  4868.  '                       a series.  Second dimension (columns) represents
  4869.  '                       different series.
  4870.  '
  4871.  '     ValY(2)         - Two-dimensional array of values for Y axis.  Above
  4872.  '                       comments apply
  4873.  '
  4874.  '     N%              - Number of values (beginning with 1) to chart per
  4875.  '                       series
  4876.  '
  4877.  '     First%          - First series to analyze
  4878.  '
  4879.  '     Last%           - Last series to analyze
  4880.  '
  4881.  '     SeriesLabel$(1) - Labels for the different series
  4882.  '
  4883.  '  Return Values:
  4884.  '     Various settings in the Env variable are altered in accordance with
  4885.  '     the analysis.
  4886.  '
  4887.  '=================================================================
  4888.  SUB AnalyzeScatterMS (Env AS ChartEnvironment, ValX() AS SINGLE, ValY() AS SI
  4889.  
  4890.  SHARED GE AS ChartEnvironment, GP AS GlobalParams
  4891.  SHARED V1(), V2()
  4892.  REDIM V1(1 TO N, 1 TO Last - First + 1), V2(1 TO N, 1 TO Last - First + 1)
  4893.  DIM Dum$(1 TO 1)
  4894.  
  4895.          ' Check initialization and fonts:
  4896.          clClearError
  4897.          clChkInit
  4898.          clChkFonts
  4899.          IF ChartErr >= 100 THEN EXIT SUB
  4900.  
  4901.          ' Set a global flag to indicate that this is a multiple-series chart:
  4902.          GP.MSeries = cYes
  4903.  
  4904.          ' Check for obvious parameter and ChartEnvironment errors:
  4905.          clChkForErrors Env, 4, 4, N%, 0, 0
  4906.          IF ChartErr > 100 THEN EXIT SUB
  4907.  
  4908.          ' Make a copy of the user's ChartEnvironment variable to the library'
  4909.          ' global environment variable:
  4910.          GE = Env
  4911.  
  4912.          ' Set the correct axis modes for the type of chart specified in the
  4913.          ' chart environment:
  4914.          clSetAxisModes
  4915.  
  4916.          ' Transfer the input data to the dynamic working data arrays (one
  4917.          ' for each axis):
  4918.          clFilterMS GE.XAxis, GP.XMode, ValX(), V1(), N, First, Last
  4919.          clFilterMS GE.YAxis, GP.YMode, ValY(), V2(), N, First, Last
  4920.  
  4921.          ' Analyze the data for scale-maximum and -minimum and set the scale-
  4922.          ' factor, etc. depending on the options set in the chart environment:
  4923.          clAnalyzeS N, SeriesLabel$(), First%, Last%
  4924.  
  4925.          ' Copy the global chart environment back to the user's ChartEnvironme
  4926.          ' variable so that the settings that were calculated by the library a
  4927.          ' accessible.  Then, if this routine wasn't called by the library its
  4928.          ' in the course of drawing a scatter chart, deallocate the working
  4929.          ' data arrays:
  4930.          Env = GE
  4931.          IF GP.SysFlag = cNo THEN ERASE V1, V2
  4932.  
  4933.  END SUB
  4934.  
  4935.  '=== Chart - Draws a single-series category/value chart
  4936.  '
  4937.  '  Arguments:
  4938.  '     Env        - A ChartEnvironment variable
  4939.  '
  4940.  '     Cat$(1)    - One-dimensional array of category labels
  4941.  '
  4942.  '     Value(1)   - One-dimensional array of values to plot
  4943.  '
  4944.  '     N          - The number of data values in data series
  4945.  '
  4946.  '  Return Values:
  4947.  '     Some elements of the Env variable are altered by plotting routines
  4948.  '
  4949.  '  Remarks:
  4950.  '     This routine takes all of the parameters set in the Env variable
  4951.  '     and draws a single-series chart of type Bar, Column, or Line
  4952.  '     depending on the chart type specified in the Env variable.
  4953.  '
  4954.  '=================================================================
  4955.  SUB Chart (Env AS ChartEnvironment, Cat$(), value() AS SINGLE, N AS INTEGER)
  4956.  
  4957.  SHARED V1()
  4958.  
  4959.          ' Analyze data for scale and window settings:
  4960.          clFlagSystem
  4961.          AnalyzeChart Env, Cat$(), value(), N
  4962.          clUnFlagSystem
  4963.          IF ChartErr < 100 THEN
  4964.  
  4965.                  ' Draw the different elements of the chart:
  4966.                  clDrawChartWindow
  4967.                  clDrawTitles
  4968.                  clDrawDataWindow
  4969.                  clDrawAxes Cat$()
  4970.  
  4971.                  ' Call appropriate Draw...Data routine for chart type:
  4972.                  SELECT CASE Env.ChartType
  4973.                          CASE 1: clDrawBarData
  4974.                          CASE 2: clDrawColumnData
  4975.                          CASE 3: clDrawLineData
  4976.                  END SELECT
  4977.  
  4978.          END IF
  4979.  
  4980.          ' Deallocate the data array:
  4981.          ERASE V1
  4982.  
  4983.  END SUB
  4984.  
  4985.  '=== ChartMS - Draws a multiple-series category/value chart
  4986.  '
  4987.  '  Arguments:
  4988.  '     Env               - A ChartEnvironment variable
  4989.  '
  4990.  '     Cat$(1)           - A one-dimensional array of category names for the
  4991.  '                         different data values
  4992.  '
  4993.  '     Value(2)          - A two-dimensional array of values--one column for
  4994.  '                         each series of data
  4995.  '
  4996.  '     N%                - The number of data points in each series of data
  4997.  '
  4998.  '     First%            - The first series to be plotted
  4999.  '
  5000.  '     Last%             - The last series to be plotted
  5001.  '
  5002.  '     SeriesLabel$(1)   - Labels used for each series in the legend
  5003.  '
  5004.  '  Return Values:
  5005.  '     Some elements of the Env variable are altered by plotting routines
  5006.  '
  5007.  '  Remarks:
  5008.  '     This routine takes all of the parameters set in the Env variable
  5009.  '     and draws a multiple-series chart of type Bar, Column, or Line
  5010.  '     depending on the chart type specified in the Env variable.
  5011.  '
  5012.  '=================================================================
  5013.  SUB ChartMS (Env AS ChartEnvironment, Cat$(), value() AS SINGLE, N AS INTEGER
  5014.  
  5015.  SHARED V1()
  5016.  
  5017.          ' Analyze data for scale settings:
  5018.          clFlagSystem
  5019.          AnalyzeChartMS Env, Cat$(), value(), N, First, Last, SeriesLabel$()
  5020.          clUnFlagSystem
  5021.          IF ChartErr < 100 THEN
  5022.  
  5023.                  ' Draw the different elements of the chart:
  5024.                  clDrawChartWindow
  5025.                  clDrawTitles
  5026.                  clDrawDataWindow
  5027.                  clDrawAxes Cat$()
  5028.  
  5029.                  ' Call appropriate Draw...DataMS routine for chart type:
  5030.                  SELECT CASE Env.ChartType
  5031.                          CASE 1: clDrawBarData
  5032.                          CASE 2: clDrawColumnData
  5033.                          CASE 3: clDrawLineData
  5034.                  END SELECT
  5035.  
  5036.                  ' Lastly, add the legend:
  5037.                  clDrawLegend SeriesLabel$(), First, Last
  5038.  
  5039.          END IF
  5040.  
  5041.          ' Deallocate the data array:
  5042.          ERASE V1
  5043.  
  5044.  END SUB
  5045.  
  5046.  '=== ChartPie - Draws a pie chart
  5047.  '
  5048.  '  Arguments:
  5049.  '     Env      - A ChartEnvironment variable
  5050.  '
  5051.  '     Cat$()   - One-dimensional array of category names
  5052.  '
  5053.  '     Value()  - One-dimensional array of values to chart
  5054.  '
  5055.  '     Expl%()  - One-dimensional array of flags indicating whether slices
  5056.  '                are to be "exploded" or not (0 means no, 1 means yes),
  5057.  '                ignored if ChartStyle <> 1
  5058.  '
  5059.  '     N%       - The number of values to chart
  5060.  '
  5061.  '  Return Values:
  5062.  '     No return values
  5063.  '
  5064.  '=================================================================
  5065.  SUB ChartPie (Env AS ChartEnvironment, Cat$(), value() AS SINGLE, Expl() AS I
  5066.  SHARED GP AS GlobalParams
  5067.          ' Set the global system flag to tell the AnalyzePie routine that it
  5068.          ' is being called by the system and not the user:
  5069.          clFlagSystem
  5070.  
  5071.          ' Calculate the size of the Data- and Legend-window:
  5072.          AnalyzePie Env, Cat$(), value(), Expl(), N
  5073.  
  5074.          ' Remove the system flag:
  5075.          clUnFlagSystem
  5076.  
  5077.          ' If there were no errors during analysis draw the chart:
  5078.          IF ChartErr < 100 THEN
  5079.  
  5080.                  ' Draw the different chart elements:
  5081.                  clDrawChartWindow
  5082.                  clDrawTitles
  5083.                  clDrawDataWindow
  5084.                  clDrawPieData value(), Expl(), N
  5085.                  IF ChartErr <> 0 THEN EXIT SUB
  5086.                  clDrawLegend Cat$(), 1, N
  5087.  
  5088.          END IF
  5089.  
  5090.  END SUB
  5091.  
  5092.  '=== ChartScatter - Draws a single-series scatter chart
  5093.  '
  5094.  '  Arguments:
  5095.  '     Env      - A ChartEnvironment variable
  5096.  '
  5097.  '     ValX(1)  - One-dimensional array of values for X axis
  5098.  '
  5099.  '     ValY(1)  - One-dimensional array of values for Y axis
  5100.  '
  5101.  '     N%       - The number of values to chart
  5102.  '
  5103.  '
  5104.  '  Return Values:
  5105.  '     Some elements of Env variable may be changed by drawing routines
  5106.  '
  5107.  '  Remarks:
  5108.  '     ChartScatter should be called when a chart with two value axes is
  5109.  '     desired
  5110.  '
  5111.  '=================================================================
  5112.  SUB ChartScatter (Env AS ChartEnvironment, ValX() AS SINGLE, ValY() AS SINGLE
  5113.  DIM Dum$(1 TO 1)
  5114.  SHARED V1(), V2()
  5115.  
  5116.          ' Set the global system flag to tell the AnalyzeScatter routine that
  5117.          ' is being called by the system and not the user:
  5118.          clFlagSystem
  5119.  
  5120.          ' Calculate the scale maximums and minimums and scale factor. Also
  5121.          ' calculate the sizes for the Data- and Legend-windows:
  5122.          AnalyzeScatter Env, ValX(), ValY(), N
  5123.  
  5124.          ' Remove the system flag:
  5125.          clUnFlagSystem
  5126.  
  5127.          ' If there were no errors during analysis draw the chart:
  5128.          IF ChartErr < 100 THEN
  5129.  
  5130.                  ' Draw the different elements of the chart:
  5131.                  clDrawChartWindow
  5132.                  clDrawTitles
  5133.                  clDrawDataWindow
  5134.                  clDrawAxes Dum$()
  5135.                  clDrawScatterData
  5136.  
  5137.          END IF
  5138.  
  5139.          ' Deallocate the dynamic working data arrays:
  5140.          ERASE V1, V2
  5141.  
  5142.  END SUB
  5143.  
  5144.  '=== ChartScatterMS - Draws a multiple-series scatter chart
  5145.  '
  5146.  '  Arguments:
  5147.  '     Env            - A ChartEnvironment variable
  5148.  '
  5149.  '     ValX(2)        - Two-dimensional array of values for X axis
  5150.  '
  5151.  '     ValY(2)        - Two-dimensional array of values for Y axis
  5152.  '
  5153.  '     N%             - The number of values in each series
  5154.  '
  5155.  '     First%         - First series to chart (first column)
  5156.  '
  5157.  '     Last%          - Last series to chart (last column)
  5158.  '
  5159.  '     SeriesLabel$() - Label used for each series in legend
  5160.  '
  5161.  '
  5162.  '  Return Values:
  5163.  '     Some elements in Env variable may be changed by drawing routines
  5164.  '
  5165.  '  Remarks:
  5166.  '     A scatter chart uses two value axes so it must have values for both
  5167.  '     the X and Y axes (ValX(), ValY()).  The first dimension denotes
  5168.  '     the different values within a series.  The second dimension specifies
  5169.  '     different data series (e.g. ValX(4,3) would represent the fourth value
  5170.  '     in the third series of data).
  5171.  '
  5172.  '=================================================================
  5173.  SUB ChartScatterMS (Env AS ChartEnvironment, ValX() AS SINGLE, ValY() AS SING
  5174.  DIM Dum$(1 TO 1)
  5175.  SHARED V1(), V2()
  5176.  
  5177.          ' Set the global system flag to tell the AnalyzeScatterMS routine tha
  5178.          ' is being called by the system and not the user:
  5179.          clFlagSystem
  5180.  
  5181.          ' Calculate the scale maximums and minimums and scale factor. Also
  5182.          ' calculate the sizes for the Data- and Legend-windows:
  5183.          AnalyzeScatterMS Env, ValX(), ValY(), N, First, Last, SeriesLabel$()
  5184.  
  5185.          ' Remove the system flag:
  5186.          clUnFlagSystem
  5187.  
  5188.          ' If there were no errors during analysis draw the chart:
  5189.          IF ChartErr < 100 THEN
  5190.  
  5191.                  ' Draw the different elements of the chart:
  5192.                  clDrawChartWindow
  5193.                  clDrawTitles
  5194.                  clDrawDataWindow
  5195.                  clDrawAxes Dum$()
  5196.                  clDrawScatterData
  5197.                  clDrawLegend SeriesLabel$(), First, Last
  5198.  
  5199.          END IF
  5200.  
  5201.          ' Deallocate the dynamic working data arrays:
  5202.          ERASE V1, V2
  5203.  
  5204.  END SUB
  5205.  
  5206.  '=== ChartScreen - Sets the SCREEN mode and default palettes
  5207.  '
  5208.  '  Arguments:
  5209.  '     N%    - A valid BASIC graphic mode, or mode 0
  5210.  '
  5211.  '  Return Values:
  5212.  '     All palettes may be altered
  5213.  '
  5214.  '=================================================================
  5215.  SUB ChartScreen (N AS INTEGER)
  5216.  SHARED GP AS GlobalParams
  5217.  
  5218.          ' Check initialization and fonts:
  5219.          clClearError
  5220.          clChkInit
  5221.  
  5222.          ' Set up branch to error processor and attempt to set the specified
  5223.          ' screen mode and draw to it:
  5224.          ON ERROR GOTO ScreenErr
  5225.          SCREEN N
  5226.          IF N <> 0 THEN PRESET (0, 0)
  5227.          ON ERROR GOTO UnexpectedErr
  5228.  
  5229.          ' If the above PRESET failed, then the TestScreen error processor wil
  5230.          ' have set the ChartErr error variable to a nonzero value.  If the la
  5231.          ' call to ChartScreen used the same mode, GP.PaletteScrn will equal N
  5232.          ' there is no need to rebuild palettes.  In either case there is no n
  5233.          ' to do anything else, so exit:
  5234.          IF ChartErr <> 0 OR (GP.PaletteScrn = N AND GP.PaletteSet) THEN EXIT
  5235.  
  5236.          ' This is a new screen mode so use the SELECT CASE statement below
  5237.          ' to handle it.  It sets the number of bits per pixel for a screen
  5238.          ' mode so that the palettes can be built properly:
  5239.          SELECT CASE N
  5240.  
  5241.                  ' Screen mode 0 is not a graphics mode and is included mainly
  5242.                  ' completeness.  The actual screen mode has been set above, s
  5243.                  CASE 0:
  5244.                          EXIT SUB
  5245.  
  5246.                  CASE 1:  Bits% = 2
  5247.                  CASE 2:  Bits% = 1
  5248.                  CASE 3:  Bits% = 1
  5249.                  CASE 4:  Bits% = 1
  5250.                  CASE 7:  Bits% = 4
  5251.                  CASE 8:  Bits% = 4
  5252.                  CASE 9:
  5253.                                          ' For screen mode 9, assume a 256K EG
  5254.                                          ' a color to 63.  If that fails, assu
  5255.                                          ' (the number of bit planes is four f
  5256.                                          ' 64K):
  5257.                                          Bits% = 4
  5258.                                          ON ERROR GOTO ScreenErr
  5259.                                          clClearError
  5260.                                          COLOR 15
  5261.                                          IF ChartErr <> 0 THEN Bits% = 2
  5262.                                          clClearError
  5263.                                          ON ERROR GOTO UnexpectedErr
  5264.  
  5265.                  CASE 10: Bits% = 2
  5266.                  CASE 11: Bits% = 1
  5267.                  CASE 12: Bits% = 4
  5268.                  CASE 13: Bits% = 8
  5269.  
  5270.                  ' If none of the above match then a valid screen mode was spe
  5271.                  ' however the mode is un-supported so set error and exit:
  5272.                  CASE ELSE: clSetError cBadScreen
  5273.                                            EXIT SUB
  5274.          END SELECT
  5275.  
  5276.          ' The screen aspect is 4/3 * MaxY/MaxX:
  5277.          VIEW
  5278.          WINDOW (0, 0)-(1, 1)
  5279.          GP.MaxXPix% = PMAP(1, 0) + 1
  5280.          GP.MaxYPix% = PMAP(0, 1) + 1
  5281.          GP.Aspect = 1.33333 * (GP.MaxYPix% - 1) / (GP.MaxXPix% - 1)
  5282.          WINDOW
  5283.  
  5284.          ' The number of colors available:
  5285.          GP.MaxColor = 2 ^ Bits% - 1
  5286.  
  5287.          ' Specify which color to use for white:
  5288.          SELECT CASE N
  5289.                  CASE 13: GP.White = 15
  5290.                  CASE ELSE: GP.White = GP.MaxColor
  5291.          END SELECT
  5292.  
  5293.          ' Build palette for this screen mode:
  5294.          clBuildPalette N, Bits%
  5295.  
  5296.  END SUB
  5297.  
  5298.  '=== clAdjustScale - Calculates scaling factor for an axis and adjusts max-mi
  5299.  '                  as appropriate for scale factor and log base if log axis:
  5300.  '
  5301.  '  Arguments:
  5302.  '     Axis  -  AxisType variable describing axis to be scaled.
  5303.  '
  5304.  '  Return Values:
  5305.  '     May set the ScaleFactor and ScaleTitle elements and alter
  5306.  '     ScaleMin and ScaleMax elements of the Axis variable.
  5307.  '
  5308.  '=================================================================
  5309.  SUB clAdjustScale (Axis AS AxisType)
  5310.  
  5311.          ' Don't try to scale a log axis:
  5312.          IF Axis.RangeType = cLog THEN
  5313.  
  5314.                  Axis.ScaleFactor = 1
  5315.                  Axis.ScaleTitle.Title = "Log" + STR$(Axis.LogBase)
  5316.  
  5317.          ' For a linear axis, choose a scale factor up to Trillions depending
  5318.          ' on the size of the axis limits:
  5319.          ELSE
  5320.  
  5321.                  ' Choose the largest ABS from Max and Min for the axis:
  5322.                  IF ABS(Axis.ScaleMax) > ABS(Axis.ScaleMin) THEN
  5323.                          Max = ABS(Axis.ScaleMax)
  5324.                  ELSE
  5325.                          Max = ABS(Axis.ScaleMin)
  5326.                  END IF
  5327.  
  5328.                  ' Find out power of three by which to scale:
  5329.                  Power% = INT((LOG(Max) / LOG(10)) / 3)
  5330.  
  5331.                  ' And, choose the correct title to go with it:
  5332.                  SELECT CASE Power%
  5333.                          CASE -4:     Axis.ScaleTitle.Title = "Trillionths"
  5334.                          CASE -3:     Axis.ScaleTitle.Title = "Billionths"
  5335.                          CASE -2:     Axis.ScaleTitle.Title = "Millionths"
  5336.                          CASE -1:     Axis.ScaleTitle.Title = "Thousandths"
  5337.                          CASE 0:     Axis.ScaleTitle.Title = ""
  5338.                          CASE 1:     Axis.ScaleTitle.Title = "Thousands"
  5339.                          CASE 2:     Axis.ScaleTitle.Title = "Millions"
  5340.                          CASE 3:     Axis.ScaleTitle.Title = "Billions"
  5341.                          CASE 4:     Axis.ScaleTitle.Title = "Trillions"
  5342.                          CASE ELSE:  Axis.ScaleTitle.Title = "10^" + LTRIM$(ST
  5343.                  END SELECT
  5344.  
  5345.                  ' Calculate the actual scale factor:
  5346.                  Axis.ScaleFactor = 10 ^ (3 * Power%)
  5347.  
  5348.                  ' Finally, scale Max and Min by ScaleFactor:
  5349.                  Axis.ScaleMin = Axis.ScaleMin / Axis.ScaleFactor
  5350.                  Axis.ScaleMax = Axis.ScaleMax / Axis.ScaleFactor
  5351.  
  5352.          END IF
  5353.  
  5354.  END SUB
  5355.  
  5356.  '=== clAnalyzeC - Does analysis of category/value data
  5357.  '
  5358.  '  Arguments:
  5359.  '     Cat$(1)     -  List of category names
  5360.  '
  5361.  '     N%          -  Number of data values per series
  5362.  '
  5363.  '     SLabels$    -  Labels for the different data series
  5364.  '
  5365.  '     First%      -  First series to chart
  5366.  '
  5367.  '     Last%       -  Last series to chart
  5368.  '
  5369.  '  Return Values:
  5370.  '     Some values in GE are altered.
  5371.  '
  5372.  '=================================================================
  5373.  SUB clAnalyzeC (Cat$(), N%, SLabels$(), First%, Last%)
  5374.  SHARED GE AS ChartEnvironment, GP AS GlobalParams
  5375.  SHARED TTitleLayout AS TitleLayout
  5376.  SHARED XTitleLayout AS TitleLayout
  5377.  SHARED YTitleLayout AS TitleLayout
  5378.  SHARED V1()
  5379.  
  5380.          ' Save the number of values and the number of series in the chart in
  5381.          ' the global parameter variables:
  5382.          GP.NVals = N%
  5383.          GP.NSeries = Last% - First% + 1
  5384.  
  5385.          ' Analyze data for scale-maximim and -minimum and scale-factor:
  5386.          clScaleAxis GE.XAxis, GP.XMode, V1()
  5387.          IF ChartErr > 100 THEN EXIT SUB
  5388.  
  5389.          clScaleAxis GE.YAxis, GP.YMode, V1()
  5390.          IF ChartErr > 100 THEN EXIT SUB
  5391.  
  5392.          ' Format tic labels (needed for sizing routines) and set global
  5393.          ' parameters (again used by sizing and other routines):
  5394.          clFormatTics GE.XAxis
  5395.          clFormatTics GE.YAxis
  5396.          clSetGlobalParams
  5397.  
  5398.          ' Layout Titles
  5399.          clLayoutTitle TTitleLayout, GE.MainTitle, GE.SubTitle
  5400.          clLayoutTitle YTitleLayout, GE.YAxis.AxisTitle, GE.YAxis.ScaleTitle
  5401.          clLayoutTitle XTitleLayout, GE.XAxis.AxisTitle, GE.XAxis.ScaleTitle
  5402.  
  5403.          ' If this is a multiple-series chart, calculate the legend size:
  5404.          IF GP.MSeries = cYes THEN clLayoutLegend SLabels$(), First%, Last%
  5405.          IF ChartErr > 100 THEN EXIT SUB
  5406.  
  5407.          ' Calculate the data-window size:
  5408.          clSizeDataWindow Cat$()
  5409.          IF ChartErr > 100 THEN EXIT SUB
  5410.  
  5411.          ' Finally, figure out the distance between tic marks:
  5412.          clSpaceTics
  5413.  
  5414.  END SUB
  5415.  
  5416.  '=== clAnalyzeS - Does actual analysis of scatter data
  5417.  '
  5418.  '  Arguments:
  5419.  '     N%          -  Number of values per data series
  5420.  '
  5421.  '     SLabels$(1) -  Labels for the data series
  5422.  '
  5423.  '     First%      -  First series to analyze
  5424.  '
  5425.  '     Last%       -  Last series to analyze
  5426.  '
  5427.  '  Return Values:
  5428.  '     Values in GE are altered.
  5429.  '
  5430.  '=================================================================
  5431.  SUB clAnalyzeS (N%, SLabels$(), First%, Last%)
  5432.  SHARED GE AS ChartEnvironment, GP AS GlobalParams
  5433.  SHARED TTitleLayout AS TitleLayout
  5434.  SHARED XTitleLayout AS TitleLayout
  5435.  SHARED YTitleLayout AS TitleLayout
  5436.  SHARED V1(), V2()
  5437.  DIM Dum$(1 TO 1)
  5438.  
  5439.          ' Save the number of values and the number of series in the chart in
  5440.          ' the global parameter variables:
  5441.          GP.NVals = N%
  5442.          GP.NSeries = Last% - First% + 1
  5443.  
  5444.          ' Analyze data for scale-maximim and -minimum and scale-factor:
  5445.          clScaleAxis GE.XAxis, GP.XMode, V1()
  5446.          IF ChartErr > 100 THEN EXIT SUB
  5447.  
  5448.          clScaleAxis GE.YAxis, GP.YMode, V2()
  5449.          IF ChartErr > 100 THEN EXIT SUB
  5450.  
  5451.          ' Format tic labels (needed for sizing routines) and set global
  5452.          ' parameters (again used by sizing and other routines):
  5453.          clFormatTics GE.XAxis
  5454.          clFormatTics GE.YAxis
  5455.          clSetGlobalParams
  5456.  
  5457.          ' Layout Titles
  5458.          clLayoutTitle TTitleLayout, GE.MainTitle, GE.SubTitle
  5459.          clLayoutTitle YTitleLayout, GE.YAxis.AxisTitle, GE.YAxis.ScaleTitle
  5460.          clLayoutTitle XTitleLayout, GE.XAxis.AxisTitle, GE.XAxis.ScaleTitle
  5461.  
  5462.          ' If this is a multiple-series chart, calculate the legend size:
  5463.          IF GP.MSeries = cYes THEN clLayoutLegend SLabels$(), First%, Last%
  5464.          IF ChartErr > 100 THEN EXIT SUB
  5465.  
  5466.          ' Calculate the data window size:
  5467.          clSizeDataWindow Dum$()
  5468.          IF ChartErr > 100 THEN EXIT SUB
  5469.  
  5470.          ' Finally, figure out the distance between tic marks:
  5471.          clSpaceTics
  5472.  
  5473.  END SUB
  5474.  
  5475.  '=== clBuildBitP$ - Builds a pattern tile for a one bit-plane screen mode
  5476.  '
  5477.  '  Arguments:
  5478.  '     Bits%    =  Number of bits per pixel in this screen mode
  5479.  '
  5480.  '     C%       =  The color used to make the pattern.
  5481.  '
  5482.  '     InP$     =  Reference pattern
  5483.  '
  5484.  '  Return Values:
  5485.  '     Returns the specified pattern in specified color.
  5486.  '
  5487.  '  Remarks:
  5488.  '     In screen modes where a pixel on the screen is represented by 1 or
  5489.  '     more bits that are adjacent in memory, a byte of memory represents
  5490.  '     one or more pixels depending on the number of bits per pixel the
  5491.  '     mode uses (e.g. screen mode 1 uses 2 bits per pixel so each byte
  5492.  '     contains 4 pixels).  To make a pattern tile in a specific color
  5493.  '     you first decide which pixels should be on and which ones off.
  5494.  '     Then, you set the corresponding two-bit pixels in the tile bytes
  5495.  '     to the value of the color you want the pattern to be.  This routine
  5496.  '     does this semi-automatically.  First it inputs a reference pattern that
  5497.  '     contains the pattern defined in the highest color available for a
  5498.  '     screen mode (all bits in a pixel set to one).  Then a color mask byte
  5499.  '     is prepared with each pixel set to the color that was specified as
  5500.  '     input to the routine.  When these two components (reference pattern
  5501.  '     and color mask) are combined using a logical "AND" any pixel in the
  5502.  '     reference pattern that was black (all zero) will remain black and any
  5503.  '     pixel that was white will be of the input color.  The nice feature of
  5504.  '     this scheme is that you can use one pattern set for any color
  5505.  '     available for the screen mode.
  5506.  '
  5507.  '     Example: Screen mode 1; 2 bits per pixel; to build a pattern
  5508.  '              with pixels alternating on and off in color 2:
  5509.  '
  5510.  '     Reference pattern:   11 00 11 00    (8 bits = 1 byte)
  5511.  '     Color mask:          10 10 10 10    (each pixel set to color 2)
  5512.  '                         -------------
  5513.  '     Result of "AND"      10 00 10 00    (pattern in color 2)
  5514.  '
  5515.  '=================================================================
  5516.  FUNCTION clBuildBitP$ (Bits%, C%, InP$)
  5517.  
  5518.          ' First get color mask to match this color and pixel size (bits per p
  5519.          CMask% = clColorMaskL%(Bits%, C%)
  5520.  
  5521.          ' Initialize the output pattern to empty then combine the color
  5522.          ' mask with each byte in the input tile using a logical "AND":
  5523.          OutP$ = ""
  5524.          FOR i% = 1 TO LEN(InP$)
  5525.                  NxtCH% = CMask% AND ASC(MID$(InP$, i%, 1))
  5526.                  OutP$ = OutP$ + CHR$(NxtCH%)
  5527.          NEXT i%
  5528.  
  5529.          ' Return the completed pattern:
  5530.          clBuildBitP$ = OutP$
  5531.  
  5532.  END FUNCTION
  5533.  
  5534.  '=== clBuildPalette - Builds the five chart palettes
  5535.  '
  5536.  '  Arguments:
  5537.  '     N           -  Screen mode for which to build palettes
  5538.  '
  5539.  '  Return Values:
  5540.  '     Values in chart palettes set to standard ones for this mode
  5541.  '
  5542.  '  Remarks:
  5543.  '     The following code sets up the palettes that are referenced when the
  5544.  '     different chart elements are drawn.  See the charting library
  5545.  '     documentation for a complete description of how these palettes are
  5546.  '     used in drawing different portions of a chart.
  5547.  '
  5548.  '=================================================================
  5549.  SUB clBuildPalette (ScrnMode AS INTEGER, Bits AS INTEGER)
  5550.  SHARED PaletteC%(), PaletteS%(), PaletteP$(), PaletteCh%(), PaletteB%()
  5551.  SHARED StdChars%()
  5552.  SHARED GP AS GlobalParams
  5553.  
  5554.          ' Flag palette set and record the screen mode:
  5555.          GP.PaletteSet = cYes
  5556.          GP.PaletteScrn = ScrnMode
  5557.          GP.PaletteBits = Bits
  5558.  
  5559.          ' The first palettes to set are the character palette and the border
  5560.          ' style palette:
  5561.          PaletteCh%(0) = 0
  5562.          PaletteB%(0) = &HFFFF
  5563.          FOR i% = 1 TO cPalLen
  5564.                  PaletteCh%(i%) = StdChars%(i%)
  5565.                  PaletteB%(i%) = clGetStyle(i%)
  5566.          NEXT i%
  5567.  
  5568.          ' The next palette to set is the color palette, which is made up of
  5569.          ' a list of 10 (maybe repeating) colors.  Begin by setting the first
  5570.          ' two colors.  The first color (position 0) is always black and the
  5571.          ' second color is always white (or whatever the maximum color number
  5572.          ' is mapped to in the graphics-card palette).  Cycle through setting
  5573.          ' other colors.  They will be entered in order starting with color 1
  5574.          ' until the maximum number of colors is reached or the palette is fil
  5575.          ' (size governed by the cPalLen CONST).  If the maximum color is reac
  5576.          ' before the palette is filled then repeat the cycle again excluding
  5577.          ' color 0, and so on, until the color palette is filled:
  5578.  
  5579.          PaletteC%(0) = 0        ' Black
  5580.          PaletteC%(1) = GP.White ' White
  5581.  
  5582.          FOR i% = 2 TO cPalLen
  5583.                  MappedI% = ((i% - 2) MOD GP.MaxColor) + 1
  5584.                  PaletteC%(i%) = MappedI%
  5585.          NEXT i%
  5586.  
  5587.          ' Setting the line styles is almost the inverse of setting the colors
  5588.          ' in that each color within a cycle has the same line style.  When a
  5589.          ' new cycle of colors begins, though, the line style changes to
  5590.          ' differentiate the new cycle from previous ones.  The line style
  5591.          ' begins as &HFFFF or a solid line:
  5592.  
  5593.          ' The pattern component of the palette contains fill patterns for use
  5594.          ' filling bars and pie slices.  Fill patterns are "bit" oriented wher
  5595.          ' line styles are "pixel" oriented.  What this means is that a fill
  5596.          ' pattern of CHR$(&HFF) will be white regardless of what the current
  5597.          ' color is.  If you know that each pixel on the screen is represented
  5598.          ' 2 bits in RAM and you want a solid fill with color 2, the correspon
  5599.          ' definition would be CHR$(&HAA) (in binary 10 10 10 10 -- notice, fo
  5600.          ' pixels of two bits each set to 2).  The following code automaticall
  5601.          ' takes a fill pattern defined in terms of pixels, and by masking it
  5602.          ' with the current color generates the same fill pattern in the
  5603.          ' specified color.  Start with solid black (color 0):
  5604.  
  5605.          PaletteS%(0) = &HFFFF
  5606.          PaletteP$(0) = CHR$(0)
  5607.  
  5608.          FOR i% = 1 TO cPalLen
  5609.  
  5610.                  ' The cycle number starts at one and is incremented each time
  5611.                  ' the maximum number of colors for the current screen mode is
  5612.                  Cycle% = ((i% - 1) \ GP.MaxColor) + 1
  5613.  
  5614.                  ' Set the style palette from the standard styles (which have
  5615.                  ' previously been placed in the border palette):
  5616.                  PaletteS%(i%) = PaletteB%(Cycle%)
  5617.  
  5618.                  ' Get the default pattern and put it into the palette:
  5619.                  SELECT CASE ScrnMode
  5620.  
  5621.                          ' One bit plane modes:
  5622.                          CASE 1, 2, 11, 13: RefPattern$ = GetPattern$(Bits, Cy
  5623.  
  5624.                          ' Multiple bit plane modes:
  5625.                          CASE ELSE: RefPattern$ = GetPattern$(1, Cycle%)
  5626.  
  5627.                  END SELECT
  5628.                  PaletteP$(i%) = MakeChartPattern$(RefPattern$, PaletteC%(i%),
  5629.  
  5630.          NEXT i%
  5631.  
  5632.  END SUB
  5633.  
  5634.  '=== clBuildPlaneP$ - Builds a pattern tile for multiple bit-plane screen mod
  5635.  '
  5636.  '  Arguments:
  5637.  '     Bits%    =  Number of planes in this screen mode
  5638.  '
  5639.  '     C%       =  The color used to make the pattern
  5640.  '
  5641.  '     InP$     =  Reference pattern
  5642.  '
  5643.  '  Return Values:
  5644.  '     Returns the specified pattern in specified color
  5645.  '
  5646.  '  Remarks:
  5647.  '     PAINT tiles are different for screen modes that use 2 or more
  5648.  '     bit-planes than for the modes that use only one (see remarks for
  5649.  '     clBuildBitP$()).  When bit-planes are used each pixel requires only
  5650.  '     one bit per byte, but, there needs to be one byte for each bit-
  5651.  '     plane.  The process for building a pattern from a reference pattern
  5652.  '     and color mask are logically the same as in the one bit-plane modes
  5653.  '     the only difference is that a color mask requires several bytes
  5654.  '     (one for each bit-plane) rather than one.
  5655.  '
  5656.  '  Example: Screen mode 9 with 2 bit planes; pattern with alternating
  5657.  '           pixels on and off; color 2:
  5658.  '
  5659.  '           Reference pattern:   1 0 1 0 1 0 1 0
  5660.  '           Color mask:          0 0 0 0 0 0 0 0   (plane 1)
  5661.  '                                1 1 1 1 1 1 1 1   (plane 2)
  5662.  '                               -----------------
  5663.  '           Result of "AND"      0 0 0 0 0 0 0 0   (plane 1)
  5664.  '                                1 0 1 0 1 0 1 0   (plane 2)
  5665.  '
  5666.  '
  5667.  '=================================================================
  5668.  FUNCTION clBuildPlaneP$ (Bits%, C%, InP$)
  5669.  DIM CMask%(1 TO 4)
  5670.  
  5671.          ' First get color mask to match this color and pixel size (bits per p
  5672.          clColorMaskH Bits%, C%, CMask%()
  5673.  
  5674.          ' Initialize the output pattern to empty then combine the color
  5675.          ' mask with each byte in the input tile using a logical "AND":
  5676.          OutP$ = ""
  5677.          FOR TileByte% = 1 TO LEN(InP$)
  5678.                  RefTile% = ASC(MID$(InP$, TileByte%, 1))
  5679.  
  5680.                  ' Combine each bit-plane in the color mask with the pattern b
  5681.                  FOR Plane% = 1 TO Bits%
  5682.                          OutP$ = OutP$ + CHR$(RefTile% AND CMask%(Plane%))
  5683.                  NEXT Plane%
  5684.          NEXT TileByte%
  5685.  
  5686.          ' Return the completed pattern:
  5687.          clBuildPlaneP$ = OutP$
  5688.  
  5689.  END FUNCTION
  5690.  
  5691.  '=== clChkChartWindow - Makes sure the chart window is valid
  5692.  '
  5693.  '  Arguments:
  5694.  '     Env   -  A ChartEnvironment variable
  5695.  '
  5696.  '  Return Values:
  5697.  '     Changes global parameters for chart window
  5698.  '
  5699.  '  Remarks:
  5700.  '     This routine forces the chart window to be valid.  If the input
  5701.  '     values are invalid a full screen is chosen.  The valid chart window
  5702.  '     is stored in the global parameter set and used by other charting
  5703.  '     routines.  The last valid screen set by ChartScreen is used as
  5704.  '     reference.
  5705.  '
  5706.  '=================================================================
  5707.  SUB clChkChartWindow (Env AS ChartEnvironment)
  5708.  SHARED GP AS GlobalParams
  5709.  
  5710.          ' Make sure X1 < X2:
  5711.          IF Env.ChartWindow.X1 < Env.ChartWindow.X2 THEN
  5712.                  GP.CwX1 = Env.ChartWindow.X1
  5713.                  GP.CwX2 = Env.ChartWindow.X2
  5714.          ELSE
  5715.                  GP.CwX1 = Env.ChartWindow.X2
  5716.                  GP.CwX2 = Env.ChartWindow.X1
  5717.          END IF
  5718.  
  5719.          ' Make sure Y1 < Y2:
  5720.          IF Env.ChartWindow.Y1 < Env.ChartWindow.Y2 THEN
  5721.                  GP.CwY1 = Env.ChartWindow.Y1
  5722.                  GP.CwY2 = Env.ChartWindow.Y2
  5723.          ELSE
  5724.                  GP.CwY1 = Env.ChartWindow.Y2
  5725.                  GP.CwY2 = Env.ChartWindow.Y1
  5726.          END IF
  5727.  
  5728.          ' If the X coordinates of the chart window are invalid,
  5729.          ' set them to full screen:
  5730.          IF GP.CwX1 < 0 OR GP.CwX2 >= GP.MaxXPix OR GP.CwX1 = GP.CwX2 THEN
  5731.                  GP.CwX1 = 0
  5732.                  GP.CwX2 = GP.MaxXPix - 1
  5733.          END IF
  5734.  
  5735.          ' If the Y coordinates of the chart window are invalid,
  5736.          ' set them to full screen:
  5737.          IF GP.CwY1 < 0 OR GP.CwY2 >= GP.MaxYPix OR GP.CwY1 = GP.CwY2 THEN
  5738.                  GP.CwY1 = 0
  5739.                  GP.CwY2 = GP.MaxYPix - 1
  5740.          END IF
  5741.  
  5742.          ' Set chart height and width for use later:
  5743.          GP.ChartWid = GP.CwX2 - GP.CwX1 + 1
  5744.          GP.ChartHgt = GP.CwY2 - GP.CwY1 + 1
  5745.  
  5746.          ' Put the valid coordinates in Env:
  5747.          Env.ChartWindow.X1 = GP.CwX1
  5748.          Env.ChartWindow.Y1 = GP.CwY1
  5749.          Env.ChartWindow.X2 = GP.CwX2
  5750.          Env.ChartWindow.Y2 = GP.CwY2
  5751.  
  5752.  END SUB
  5753.  
  5754.  '=== clChkFonts - Checks that there is at least one loaded font
  5755.  '
  5756.  '  Arguments:
  5757.  '     none
  5758.  '
  5759.  '  Return Values:
  5760.  '     Chart error set if no room for a font
  5761.  '
  5762.  '=================================================================
  5763.  SUB clChkFonts
  5764.  
  5765.          ' See if a font is loaded:
  5766.          GetTotalFonts Reg%, Load%
  5767.  
  5768.          ' If not then find out the maximum number of fonts allowed and if
  5769.          ' there's room, then load the default font:
  5770.          IF Load% <= 0 THEN
  5771.                  GetMaxFonts MReg%, MLoad%
  5772.                  IF Reg% < MReg% AND Load% < MLoad% THEN
  5773.                          DefaultFont Segment%, Offset%
  5774.                          FontNum% = RegisterMemFont(Segment%, Offset%)
  5775.                          FontNum% = LoadFont("N" + STR$(Load% + 1))
  5776.  
  5777.                  ' If there's no room, then set an error:
  5778.                  ELSE
  5779.                          clSetError cNoFontSpace
  5780.                  END IF
  5781.          END IF
  5782.  END SUB
  5783.  
  5784.  '=== CheckForErrors - Checks for and tries to fix a variety of errors
  5785.  '
  5786.  '  Arguments:
  5787.  '     Env      -  ChartEnvironment variable
  5788.  '
  5789.  '     TypeMin% -  Minimum allowable ChartType
  5790.  '
  5791.  '     TypeMax% -  Maximum allowable ChartType
  5792.  '
  5793.  '     N%       -  Number of data values per series
  5794.  '
  5795.  '     First%   -  Column of data representing first series
  5796.  '
  5797.  '     Last%    -  Column of data representing last series
  5798.  '
  5799.  '  Return Values:
  5800.  '     This routine is the main one that checks for errors of input in
  5801.  '     the ChartEnvironment variable and routine parameters.
  5802.  '
  5803.  '=================================================================
  5804.  SUB clChkForErrors (Env AS ChartEnvironment, TypeMin%, TypeMax%, N%, First%,
  5805.  
  5806.          ' Clear any previous error:
  5807.          clClearError
  5808.  
  5809.          ' Check for correct chart type:
  5810.          IF Env.ChartType < TypeMin% OR Env.ChartType > TypeMax% THEN
  5811.                  clSetError cBadType
  5812.                  EXIT SUB
  5813.          END IF
  5814.  
  5815.          ' Check for valid chart style:
  5816.          IF Env.ChartStyle < 1 OR Env.ChartStyle > 2 THEN
  5817.                  clSetError cBadStyle
  5818.                  Env.ChartStyle = 1
  5819.          END IF
  5820.  
  5821.          ' The following things are not relevant for pie charts:
  5822.          IF Env.ChartType <> cPie THEN
  5823.  
  5824.                  ' Check LogBase for the X axis (default to 10):
  5825.                  IF Env.XAxis.RangeType = cLog AND Env.XAxis.LogBase <= 0 THEN
  5826.                          clSetError cBadLogBase
  5827.                          Env.XAxis.LogBase = 10
  5828.                  END IF
  5829.  
  5830.                  ' Check LogBase for the Y axis (default to 10):
  5831.                  IF Env.YAxis.RangeType = cLog AND Env.YAxis.LogBase <= 0 THEN
  5832.                          clSetError cBadLogBase
  5833.                          Env.YAxis.LogBase = 10
  5834.                  END IF
  5835.  
  5836.                  ' Check X axis ScaleFactor:
  5837.                  IF Env.XAxis.AutoScale <> cYes AND Env.XAxis.ScaleFactor = 0
  5838.                          clSetError cBadScaleFactor
  5839.                          Env.XAxis.ScaleFactor = 1
  5840.                  END IF
  5841.  
  5842.                  ' Check Y axis ScaleFactor:
  5843.                  IF Env.YAxis.AutoScale <> cYes AND Env.YAxis.ScaleFactor = 0
  5844.                          clSetError cBadScaleFactor
  5845.                          Env.YAxis.ScaleFactor = 1
  5846.                  END IF
  5847.          END IF
  5848.  
  5849.          ' Make sure N > 0:
  5850.          IF N% <= 0 THEN
  5851.                  clSetError cTooSmallN
  5852.                  EXIT SUB
  5853.          END IF
  5854.  
  5855.          ' Check that First series <= Last one:
  5856.          IF First% > Last% THEN
  5857.                  clSetError cTooFewSeries
  5858.                  EXIT SUB
  5859.          END IF
  5860.  
  5861.          ' Force ChartWindow to be valid:
  5862.          clChkChartWindow Env
  5863.  
  5864.  END SUB
  5865.  
  5866.  '=== clChkInit - Check that chartlib has been initialized
  5867.  '
  5868.  '  Arguments:
  5869.  '     none
  5870.  '
  5871.  '  Return Values:
  5872.  '     none
  5873.  '
  5874.  '=================================================================
  5875.  SUB clChkInit
  5876.  SHARED GP AS GlobalParams
  5877.  
  5878.          IF NOT GP.Initialized THEN clInitChart
  5879.  
  5880.  END SUB
  5881.  
  5882.  '=== clChkPalettes - Makes sure that palettes are dimensioned correctly
  5883.  '
  5884.  '  Arguments:
  5885.  '     C%()     -  Color palette array
  5886.  '
  5887.  '     S%()     -  Style palette array
  5888.  '
  5889.  '     P$()     -  Pattern palette array
  5890.  '
  5891.  '     Char%()  -  Plot character palette array
  5892.  '
  5893.  '     B%()     -  Border pattern palette array
  5894.  '
  5895.  '  Return Values:
  5896.  '     Chart error may be set to cBadPalette
  5897.  '
  5898.  '=================================================================
  5899.  SUB clChkPalettes (C() AS INTEGER, s() AS INTEGER, P$(), Char() AS INTEGER, B
  5900.  
  5901.          ' Check each palette array to be sure it is dimensioned from 0
  5902.          ' to cPalLen:
  5903.          FOR i% = 1 TO 5
  5904.                  SELECT CASE i%
  5905.                          CASE 1:  L% = LBOUND(C, 1): U% = UBOUND(C, 1)
  5906.                          CASE 2:  L% = LBOUND(s, 1): U% = UBOUND(s, 1)
  5907.                          CASE 3:  L% = LBOUND(P$, 1): U% = UBOUND(P$, 1)
  5908.                          CASE 4:  L% = LBOUND(Char, 1): U% = UBOUND(Char, 1)
  5909.                          CASE 5:  L% = LBOUND(B, 1): U% = UBOUND(B, 1)
  5910.                  END SELECT
  5911.  
  5912.                  ' If incorrectly dimensioned then set error:
  5913.                  IF (L% <> 0) OR (U% <> cPalLen) THEN
  5914.                          clSetError cBadPalette
  5915.                          EXIT SUB
  5916.                  END IF
  5917.          NEXT i%
  5918.  
  5919.  END SUB
  5920.  
  5921.  '=== clClearError - Clears ChartErr, the ChartLib error variable
  5922.  '
  5923.  '  Arguments:
  5924.  '     None
  5925.  '
  5926.  '  Return Values:
  5927.  '     Sets ChartErr to 0
  5928.  '
  5929.  '=================================================================
  5930.  SUB clClearError
  5931.  
  5932.          ChartErr = 0
  5933.  
  5934.  END SUB
  5935.  
  5936.  '=== clColorMaskH% - Function to generate a byte with each pixel set to
  5937.  '                  some color - for high-res modes (7,8,9,10)
  5938.  '
  5939.  '  Arguments:
  5940.  '     Bits%    -  Number of bits per pixel in current screen mode
  5941.  '
  5942.  '     Colr%    -  Color to make the mask
  5943.  '
  5944.  '     CMask%() -  One dimensional array to place mask values in
  5945.  '
  5946.  '  Return Values:
  5947.  '     Screen modes 7, 8, 9 and 10 use bit planes.  Rather than using
  5948.  '     adjacent bits in one byte to determine a color, they use bits
  5949.  '     "stacked" on top of each other in different bytes.  This routine
  5950.  '     generates one byte of a particular color by setting the different
  5951.  '     levels of the stack to &H00 and &HFF to represent eight pixels
  5952.  '     of a particular color.
  5953.  '
  5954.  '=================================================================
  5955.  SUB clColorMaskH (Bits%, Colr%, CMask%())
  5956.  
  5957.          ' Copy the color to a local variable:
  5958.          RefColor% = Colr%
  5959.  
  5960.          ' Bits% is the number of bit planes, set a mask for each one:
  5961.          FOR i% = 1 TO Bits%
  5962.  
  5963.                  ' Check rightmost bit in color, if it is set to 1 then this p
  5964.                  ' "on" (it equals &HFF):
  5965.                  IF RefColor% MOD 2 <> 0 THEN
  5966.                          CMask%(i%) = &HFF
  5967.  
  5968.                  ' If the bit is 0, the plane is off (it equals &H0):
  5969.                  ELSE
  5970.                          CMask%(i%) = &H0
  5971.                  END IF
  5972.  
  5973.                  ' Shift the reference color right by one bit:
  5974.                  RefColor% = RefColor% \ 2
  5975.          NEXT i%
  5976.  
  5977.  END SUB
  5978.  
  5979.  '=== clColorMaskL% - Function to generate a byte with each pixel set to
  5980.  '                 some color.
  5981.  '
  5982.  '  Arguments:
  5983.  '     Bits%    -  Number of bits per pixel in current screen mode
  5984.  '
  5985.  '     Colr%    -  Color to make the mask
  5986.  '
  5987.  '  Return Values:
  5988.  '     Returns integer with low byte that contains definitions for
  5989.  '     pixels of specified color.
  5990.  '
  5991.  '=================================================================
  5992.  FUNCTION clColorMaskL% (Bits%, Colr%)
  5993.  
  5994.          ' Initialize the mask to zero:
  5995.          M% = 0
  5996.  
  5997.          ' Multiplying a number by (2 ^ Bits%) will shift it left by "Bits%" b
  5998.          LShift% = 2 ^ Bits%
  5999.  
  6000.          ' Create a byte in which each pixel (of "Bits%" bits) is set to
  6001.          ' Colr%.  This is done by setting the mask to "Colr%" then shifting
  6002.          ' it left by "Bits%" and repeating until the byte is full:
  6003.          FOR i% = 0 TO 7 \ Bits%
  6004.                  M% = M% * LShift% + Colr%
  6005.          NEXT i%
  6006.  
  6007.          ' Return the mask as the value of the function:
  6008.          clColorMaskL% = M% MOD 256
  6009.  
  6010.  END FUNCTION
  6011.  
  6012.  '=== clDrawAxes - Draws the axes for a chart
  6013.  '
  6014.  '  Arguments:
  6015.  '     Cat$(1)  -  One-dimensional array or category names for use in
  6016.  '                 labeling the category axis (ignored if category
  6017.  '                 axis not used)
  6018.  '
  6019.  '  Return Values:
  6020.  '     No return values
  6021.  '
  6022.  '=================================================================
  6023.  SUB clDrawAxes (Cat$())
  6024.  SHARED GE AS ChartEnvironment, GP AS GlobalParams
  6025.  SHARED GFI AS FontInfo
  6026.  SHARED PaletteC%(), PaletteB%()
  6027.  
  6028.          ' Use temporary variables to refer to axis limits:
  6029.          X1 = GE.XAxis.ScaleMin
  6030.          X2 = GE.XAxis.ScaleMax
  6031.          Y1 = GE.YAxis.ScaleMin
  6032.          Y2 = GE.YAxis.ScaleMax
  6033.  
  6034.          ' To draw the tic/grid lines it is necessary to know where the line
  6035.          ' starts and ends.  If tic marks are specified (by setting
  6036.          ' the "labeled" flag in the axis definition) then the
  6037.          ' tic lines start "ticwidth" below or to the left of the X and
  6038.          ' Y axis respectively.  If grid lines are specified then the
  6039.          ' tic/grid line ends at ScaleMax for the respective axis.  The
  6040.          ' case statements below calculate where the tic/grid lines start
  6041.          ' based on the above criteria.
  6042.  
  6043.          ' Check for tic marks first (X Axis):
  6044.          SELECT CASE GE.XAxis.Labeled
  6045.                  CASE cNo:  XTicMinY = Y1
  6046.                  CASE ELSE
  6047.                          XTicMinY = Y1 - cTicSize * (Y2 - Y1)
  6048.                          IF GP.XStagger = cYes THEN
  6049.                                  clSetChartFont GE.XAxis.TicFont
  6050.                                  XTicDropY = GFI.PixHeight * (Y2 - Y1) / (GE.D
  6051.                          ELSE
  6052.                                  XTicDropY = 0
  6053.                          END IF
  6054.          END SELECT
  6055.  
  6056.          ' (Y Axis):
  6057.          SELECT CASE GE.YAxis.Labeled
  6058.                  CASE cNo:  YTicMinX = X1
  6059.                  CASE ELSE: YTicMinX = X1 - cTicSize * (X2 - X1)
  6060.          END SELECT
  6061.  
  6062.          ' Now for the other end of the tic/grid lines check for
  6063.          ' the grid flag (X axis):
  6064.          SELECT CASE GE.XAxis.grid
  6065.                  CASE cNo:  XTicMaxY = Y1
  6066.                  CASE ELSE: XTicMaxY = Y2
  6067.          END SELECT
  6068.  
  6069.          ' (Y Axis):
  6070.          SELECT CASE GE.YAxis.grid
  6071.                  CASE cNo:  YTicMaxX = X1
  6072.                  CASE ELSE: YTicMaxX = X2
  6073.          END SELECT
  6074.  
  6075.          ' Now that the beginning and end of the tic/grid lines has been
  6076.          ' calculated, it is necessary to figure out where they fall along the
  6077.          ' axes.  This depends on the type of axis: category or value.  On a
  6078.          ' category axis the tic/grid lines should fall in the middle of each
  6079.          ' bar set.  This is calculated by adding 1/2 of TicInterval to
  6080.          ' the beginning of the axis.  On a value axis the tic/grid line
  6081.          ' falls at the beginning of the axis.  It is also necessary to know
  6082.          ' the total number of tics per axis.  The following CASE statements
  6083.          ' calculate this.  Once the first tic/grid location on an axis is
  6084.          ' calculated the others can be calculated as they are drawn by adding
  6085.          ' TicInterval each time to the position of the previous tic mark:
  6086.  
  6087.          ' Location of the first (leftmost) tic/grid line on the X axis:
  6088.          TicTotX% = CINT((X2 - X1) / GE.XAxis.TicInterval)
  6089.          SELECT CASE GP.XMode
  6090.                  CASE cCategory: TicX = X1 + GE.XAxis.TicInterval / 2
  6091.                  CASE ELSE
  6092.                          TicX = X1
  6093.                          TicTotX% = TicTotX% + 1
  6094.          END SELECT
  6095.  
  6096.          ' Location of the first (top) tic/grid line on the Y axis:
  6097.          TicTotY% = CINT((Y2 - Y1) / GE.YAxis.TicInterval)
  6098.          SELECT CASE GP.YMode
  6099.                  CASE cCategory: TicY = Y1 + GE.YAxis.TicInterval / 2
  6100.                  CASE ELSE
  6101.                          TicY = Y1
  6102.                          TicTotY% = TicTotY% + 1
  6103.          END SELECT
  6104.  
  6105.          ' Now it's time to draw the axes; first the X then the Y axis.
  6106.          ' There's a small complexity that has to be dealt with first, though.
  6107.          ' The tic/grid lines are specified in "world" coordinates since that
  6108.          ' is easier to calculate but the current VIEW (the DataWindow) would
  6109.          ' clip them since tic marks (and also labels) lie outside of that
  6110.          ' region.  The solution is to extrapolate the DataWindow "world" to t
  6111.          ' ChartWindow region and set our VIEW to the ChartWindow.  This will
  6112.          ' clip labels if they are too long and try to go outside the Chart
  6113.          ' Window but still allow use of world coordinates for specifying
  6114.          ' locations.  To extrapolate the world coordinates to the ChartWindow
  6115.          ' PMAP can be used.  This works since PMAP can take pixel coordinates
  6116.          ' outside of the current VIEW and map them to the appropriate world
  6117.          ' coordinates.  The DataWindow coordinates (calculated in the routine
  6118.          ' clSizeDataWindow) are expressed relative to the ChartWindow so
  6119.          ' it can be somewhat complicated trying to understand what to use wit
  6120.          ' PMAP.  If you draw a picture of it things will appear more straight
  6121.          ' forward.
  6122.  
  6123.          ' To make sure that bars and columns aren't drawn over the axis lines
  6124.          ' temporarily move the left DataWindow border left by one and the bot
  6125.          ' border down by one pixel:
  6126.          GE.DataWindow.X1 = GE.DataWindow.X1 - 1
  6127.          GE.DataWindow.Y2 = GE.DataWindow.Y2 + 1
  6128.  
  6129.          ' Select the DataWindow view and assign the "world" to it:
  6130.          clSelectRelWindow GE.DataWindow
  6131.          WINDOW (X1, Y1)-(X2, Y2)
  6132.          GTextWindow X1, Y1, X2, Y2, cFalse
  6133.  
  6134.          ' Next, use PMAP to extrapolate to ChartWindow:
  6135.          WorldX1 = PMAP(-GE.DataWindow.X1, 2)
  6136.          WorldX2 = PMAP(GP.ChartWid - 1 - GE.DataWindow.X1, 2)
  6137.  
  6138.          WorldY1 = PMAP(GP.ChartHgt - 1 - GE.DataWindow.Y1, 3)
  6139.          WorldY2 = PMAP(-GE.DataWindow.Y1, 3)
  6140.  
  6141.          ' Reset the DataWindow borders back to their original settings:
  6142.          GE.DataWindow.X1 = GE.DataWindow.X1 + 1
  6143.          GE.DataWindow.Y2 = GE.DataWindow.Y2 - 1
  6144.  
  6145.          ' Finally, select the ChartWindow VIEW and apply the extrapolated
  6146.          ' window to it:
  6147.          clSelectChartWindow
  6148.          WINDOW (WorldX1, WorldY1)-(WorldX2, WorldY2)
  6149.          GTextWindow WorldX1, WorldY1, WorldX2, WorldY2, cFalse
  6150.  
  6151.           ' Draw the X and Y axes (one pixel to left and bottom of window):
  6152.          CX% = PaletteC%(clMap2Pal%(GE.XAxis.AxisColor))  ' Color of X axis
  6153.          CY% = PaletteC%(clMap2Pal%(GE.YAxis.AxisColor))  ' Color of Y axis
  6154.  
  6155.          SX% = PaletteB%(clMap2Pal%(GE.XAxis.GridStyle)) ' Line styles; X grid
  6156.          SY% = PaletteB%(clMap2Pal%(GE.YAxis.GridStyle)) ' Line styles; Y grid
  6157.  
  6158.          LINE (X1, Y1)-(X2, Y1), CX%
  6159.          LINE (X1, Y1)-(X1, Y2), CY%
  6160.  
  6161.          ' X-Axis...Draw styled grid line then solid tic mark:
  6162.          TicLoc = TicX
  6163.          Stagger% = cFalse
  6164.          FOR i% = 1 TO TicTotX%
  6165.                  LINE (TicLoc, Y1)-(TicLoc, XTicMaxY), CY%, , SX%
  6166.                  IF Stagger% THEN
  6167.                          LINE (TicLoc, XTicMinY - XTicDropY)-(TicLoc, Y1), CX%
  6168.                          Stagger% = cFalse
  6169.                  ELSE
  6170.                          LINE (TicLoc, XTicMinY)-(TicLoc, Y1), CX%
  6171.                          Stagger% = cTrue
  6172.                  END IF
  6173.                  TicLoc = TicLoc + GE.XAxis.TicInterval
  6174.          NEXT i%
  6175.  
  6176.          ' Y-Axis...Draw styled grid line then solid tic mark:
  6177.          TicLoc = TicY
  6178.          FOR i% = 1 TO TicTotY%
  6179.                  LINE (X1, TicLoc)-(YTicMaxX, TicLoc), CX%, , SY%
  6180.                  LINE (YTicMinX, TicLoc)-(X1, TicLoc), CY%
  6181.                  TicLoc = TicLoc + GE.YAxis.TicInterval
  6182.          NEXT i%
  6183.  
  6184.          ' Label X tic marks and print titles:
  6185.          clLabelXTics GE.XAxis, Cat$(), TicX, TicTotX%, XTicMinY, YBoundry%
  6186.          clTitleXAxis GE.XAxis, GE.DataWindow.X1, GE.DataWindow.X2, YBoundry%
  6187.  
  6188.          ' Label Y tic marks and print titles:
  6189.          clLabelYTics GE.YAxis, Cat$(), YTicMinX, TicY, TicTotY%
  6190.          clTitleYAxis GE.YAxis, GE.DataWindow.Y1, GE.DataWindow.Y2
  6191.  
  6192.  END SUB
  6193.  
  6194.  '=== clDrawBarData - Draws data portion of multi-series bar chart
  6195.  '
  6196.  '  Arguments:
  6197.  '     None
  6198.  '
  6199.  '  Return Values:
  6200.  '     None
  6201.  '
  6202.  '=================================================================
  6203.  SUB clDrawBarData
  6204.  SHARED GE AS ChartEnvironment, GP AS GlobalParams
  6205.  SHARED PaletteC%()
  6206.  SHARED V1()
  6207.  
  6208.          ' Set the VIEW to the DataWindow:
  6209.          clSelectRelWindow GE.DataWindow
  6210.  
  6211.          ' Set the WINDOW to match:
  6212.          WINDOW (GE.XAxis.ScaleMin, GE.YAxis.ScaleMin)-(GE.XAxis.ScaleMax, GE.
  6213.          GTextWindow GE.XAxis.ScaleMin, GE.YAxis.ScaleMin, GE.XAxis.ScaleMax,
  6214.  
  6215.          ' If this is a linear axis then determine where the bars should grow
  6216.          IF GE.XAxis.RangeType = cLinear THEN
  6217.  
  6218.                  ' If the scale minimum and maximum are on opposite sides of z
  6219.                  ' set the bar starting point to zero:
  6220.                  IF GE.XAxis.ScaleMin < 0 AND GE.XAxis.ScaleMax > 0 THEN
  6221.                          BarMin = 0
  6222.  
  6223.                  ' If the axis range is all negative the the bars should grow
  6224.                  ' the right to the left so make the bar starting point the sc
  6225.                  ' maximum:
  6226.                  ELSEIF GE.XAxis.ScaleMin < 0 THEN
  6227.                          BarMin = GE.XAxis.ScaleMax
  6228.  
  6229.                  ' The axis range is all positive so the bar starting point is
  6230.                  ' scale minimum:
  6231.                  ELSE
  6232.                          BarMin = GE.XAxis.ScaleMin
  6233.                  END IF
  6234.  
  6235.          ' The bar starting point for log axes should always be the scale mini
  6236.          ' since only positive numbers are represented on a log axis (even tho
  6237.          ' the log of small numbers is negative):
  6238.          ELSE
  6239.                  BarMin = GE.XAxis.ScaleMin
  6240.          END IF
  6241.  
  6242.          ' Calculate the width of a bar.  Divide by the number of
  6243.          ' series if it's a plain (not stacked) chart:
  6244.          BarWid = GE.YAxis.TicInterval * cBarWid
  6245.          IF GE.ChartStyle = cPlain THEN BarWid = BarWid / GP.NSeries
  6246.  
  6247.          ' Calculate the beginning Y value of first bar then loop drawing
  6248.          ' all the bars:
  6249.          SpaceWid = GE.YAxis.TicInterval * (1 - cBarWid)
  6250.          StartLoc = GE.YAxis.ScaleMax - SpaceWid / 2
  6251.  
  6252.          FOR i% = 1 TO GP.NVals
  6253.  
  6254.                  ' Reset sum variables for positive and negative stacked bars:
  6255.                  RSumPos = 0
  6256.                  RSumNeg = 0
  6257.  
  6258.                  ' Reset the bar starting points:
  6259.                  BarStartPos = BarMin
  6260.                  BarStartNeg = BarMin
  6261.  
  6262.                  ' Reset starting Y location of this bar set:
  6263.                  BarLoc = StartLoc
  6264.  
  6265.                  ' Now, chart the different series for this category:
  6266.                  FOR J% = 1 TO GP.NSeries
  6267.  
  6268.                          ' Get the value to chart from the data array:
  6269.                          V = V1(i%, J%)
  6270.  
  6271.                          ' If the value isn't a missing one then try to chart
  6272.                          IF V <> cMissingValue THEN
  6273.  
  6274.                                  ' If the X-axis has the AutoScale flag set th
  6275.                                  ' the value by the axis' ScaleFactor variable
  6276.                                  IF GE.XAxis.AutoScale = cYes THEN V = V / GE.
  6277.  
  6278.                                  ' If this is a plain chart then calculate the
  6279.                                  ' and draw it:
  6280.                                  IF GE.ChartStyle = cPlain THEN
  6281.  
  6282.                                          BarLoc = StartLoc - (J% - 1) * BarWid
  6283.                                          clRenderBar BarMin, BarLoc, V, BarLoc
  6284.  
  6285.                                  ' If the bars should be stacked then draw eit
  6286.                                  ' negative portion of a bar depending on whet
  6287.                                  ' is positive or negative:
  6288.                                  ELSE
  6289.  
  6290.                                          ' If the value is positive:
  6291.                                          IF V > 0 THEN
  6292.  
  6293.                                                  ' Add the value to the curren
  6294.                                                  ' the bar from the top of the
  6295.                                                  RSumPos = RSumPos + V
  6296.                                                  clRenderBar BarStartPos, BarL
  6297.                                                  BarStartPos = RSumPos
  6298.  
  6299.                                          ' If the value is negative:
  6300.                                          ELSE
  6301.  
  6302.                                                  ' Add the value to the curren
  6303.                                                  ' the bar from the bottom of
  6304.                                                  RSumNeg = RSumNeg + V
  6305.                                                  clRenderBar BarStartNeg, BarL
  6306.                                                  BarStartNeg = RSumNeg
  6307.  
  6308.                                          END IF
  6309.                                  END IF
  6310.                          END IF
  6311.  
  6312.                  NEXT J%
  6313.  
  6314.                  ' Update the bar cluster's starting location:
  6315.                  StartLoc = StartLoc - GE.YAxis.TicInterval
  6316.  
  6317.          NEXT i%
  6318.  
  6319.          ' If BarMin isn't the axis minimum then draw a reference line:
  6320.          IF BarMin <> GE.XAxis.ScaleMin THEN
  6321.                  LINE (BarMin, GE.YAxis.ScaleMin)-(BarMin, GE.YAxis.ScaleMax),
  6322.          END IF
  6323.  
  6324.  END SUB
  6325.  
  6326.  '=== clDrawChartWindow - Draws the Chart window
  6327.  '
  6328.  '  Arguments:
  6329.  '     None
  6330.  '
  6331.  '  Return Values:
  6332.  '     None
  6333.  '
  6334.  '  Remarks:
  6335.  '     This routine erases any previous viewport
  6336.  '
  6337.  '=================================================================
  6338.  SUB clDrawChartWindow
  6339.  SHARED GE AS ChartEnvironment
  6340.  
  6341.          ' Define viewport then render window:
  6342.          clSelectChartWindow
  6343.          clRenderWindow GE.ChartWindow
  6344.  
  6345.  END SUB
  6346.  
  6347.  '=== clDrawColumnData - Draws data portion of MS Column chart
  6348.  '
  6349.  '  Arguments:
  6350.  '     None
  6351.  '
  6352.  '  Return Values:
  6353.  '     None
  6354.  '
  6355.  '=================================================================
  6356.  SUB clDrawColumnData
  6357.  SHARED GP AS GlobalParams, GE AS ChartEnvironment
  6358.  SHARED PaletteC%(), V1()
  6359.  
  6360.          ' First, set the VIEW to DataWindow:
  6361.          clSelectRelWindow GE.DataWindow
  6362.  
  6363.          ' Set the WINDOW to match:
  6364.          WINDOW (GE.XAxis.ScaleMin, GE.YAxis.ScaleMin)-(GE.XAxis.ScaleMax, GE.
  6365.          GTextWindow GE.XAxis.ScaleMin, GE.YAxis.ScaleMin, GE.XAxis.ScaleMax,
  6366.  
  6367.          ' If this is a linear axis then determine where the bars should grow
  6368.          IF GE.YAxis.RangeType = cLinear THEN
  6369.  
  6370.                  ' Draw 0 reference line if the scale minimum and maximum are
  6371.                  ' opposite sides of zero.  Also set the bar starting point to
  6372.                  ' so that bars grow from the zero line:
  6373.                  IF GE.YAxis.ScaleMin < 0 AND GE.YAxis.ScaleMax > 0 THEN
  6374.                          BarMin = 0
  6375.  
  6376.                  ' If the axis range is all negative the the bars should grow
  6377.                  ' the right to the left so make the bar starting point the sc
  6378.                  ' maximum:
  6379.                  ELSEIF GE.YAxis.ScaleMin < 0 THEN
  6380.                          BarMin = GE.YAxis.ScaleMax
  6381.  
  6382.                  ' The axis range is all positive so the bar starting point is
  6383.                  ' scale minimum:
  6384.                  ELSE
  6385.                          BarMin = GE.YAxis.ScaleMin
  6386.                  END IF
  6387.  
  6388.          ' The bar starting point for log axes should always be the scale mini
  6389.          ' since only positive numbers are represented on a log axis (even tho
  6390.          ' the log of small numbers is negative):
  6391.          ELSE
  6392.                  BarMin = GE.YAxis.ScaleMin
  6393.          END IF
  6394.  
  6395.          ' Calculate the width of a bar.  Divide by the number of
  6396.          ' series if it's a plain (not stacked) chart:
  6397.          BarWid = GE.XAxis.TicInterval * cBarWid
  6398.          IF GE.ChartStyle = cPlain THEN BarWid = BarWid / GP.NSeries
  6399.  
  6400.          ' calculate the beginning X value of first bar and loop, drawing all
  6401.          ' the bars:
  6402.          SpaceWid = GE.XAxis.TicInterval * (1 - cBarWid)
  6403.          StartLoc = GE.XAxis.ScaleMin + SpaceWid / 2
  6404.  
  6405.          FOR i% = 1 TO GP.NVals
  6406.  
  6407.                  ' Reset sum variables for positive and negative stacked bars:
  6408.                  RSumPos = 0
  6409.                  RSumNeg = 0
  6410.  
  6411.                  BarStartPos = BarMin
  6412.                  BarStartNeg = BarMin
  6413.  
  6414.                  ' Reset starting Y location of this bar set:
  6415.                  BarLoc = StartLoc
  6416.  
  6417.                  ' Now, go across the rows charting the different series for
  6418.                  ' this category:
  6419.                  FOR J% = 1 TO GP.NSeries
  6420.  
  6421.                          ' Get the value to chart from the data array:
  6422.                          V = V1(i%, J%)
  6423.  
  6424.                          ' If the value isn't a missing one then try to chart
  6425.                          IF V <> cMissingValue THEN
  6426.  
  6427.                                  ' If the Y-axis has the AutoScale flag set th
  6428.                                  ' the value by the axis' ScaleFactor variable
  6429.                                  IF GE.YAxis.AutoScale = cYes THEN V = V / GE.
  6430.  
  6431.                                  ' If this is a plain chart then calculate the
  6432.                                  ' and draw it:
  6433.                                  IF GE.ChartStyle = cPlain THEN
  6434.  
  6435.                                          BarLoc = StartLoc + (J% - 1) * BarWid
  6436.                                          clRenderBar BarLoc, BarMin, BarLoc +
  6437.  
  6438.                                  ' If the bars should be stacked then draw eit
  6439.                                  ' negative portion of a bar depending on whet
  6440.                                  ' is positive or negative:
  6441.                                  ELSE
  6442.  
  6443.                                          ' If the value is positive:
  6444.                                          IF V > 0 THEN
  6445.  
  6446.                                                  ' Add the value to the curren
  6447.                                                  ' the bar from the top of the
  6448.                                                  RSumPos = RSumPos + V
  6449.                                                  clRenderBar BarLoc, BarStartP
  6450.                                                  BarStartPos = RSumPos
  6451.  
  6452.                                          ' If the value is negative:
  6453.                                          ELSE
  6454.  
  6455.                                                  ' Add the value to the curren
  6456.                                                  ' the bar from the bottom of
  6457.                                                  RSumNeg = RSumNeg + V
  6458.                                                  clRenderBar BarLoc, BarStartN
  6459.                                                  BarStartNeg = RSumNeg
  6460.  
  6461.                                          END IF
  6462.                                  END IF
  6463.                          END IF
  6464.  
  6465.                  NEXT J%
  6466.  
  6467.                  ' Update the bar cluster's starting location:
  6468.                  StartLoc = StartLoc + GE.XAxis.TicInterval
  6469.  
  6470.          NEXT i%
  6471.  
  6472.          ' If BarMin isn't the axis minimum then draw a reference line:
  6473.          IF BarMin <> GE.YAxis.ScaleMin THEN
  6474.                  LINE (GE.XAxis.ScaleMin, BarMin)-(GE.XAxis.ScaleMax, BarMin),
  6475.          END IF
  6476.  
  6477.  END SUB
  6478.  
  6479.  '=== clDrawDataWindow - Draws the Data window
  6480.  '
  6481.  '  Arguments:
  6482.  '     None
  6483.  '
  6484.  '  Return Values:
  6485.  '     None
  6486.  '
  6487.  '  Remarks:
  6488.  '     This routine erases any previous viewport or window specification.
  6489.  '
  6490.  '=================================================================
  6491.  SUB clDrawDataWindow
  6492.  SHARED GE AS ChartEnvironment
  6493.  
  6494.          ' Define viewport then render window:
  6495.          clSelectRelWindow GE.DataWindow
  6496.          clRenderWindow GE.DataWindow
  6497.  
  6498.  END SUB
  6499.  
  6500.  '=== clDrawLegend - Draws a legend
  6501.  '
  6502.  '  Arguments:
  6503.  '     SeriesLabel$(1)   - Array of labels for the legend
  6504.  '
  6505.  '     First%            - Label number corresponding to first series
  6506.  '
  6507.  '     Last%             - Label number corresponding to last series
  6508.  '
  6509.  '  Return Values:
  6510.  '     None.
  6511.  '
  6512.  '=================================================================
  6513.  SUB clDrawLegend (SeriesLabel$(), First AS INTEGER, Last AS INTEGER)
  6514.  
  6515.  SHARED GE AS ChartEnvironment, GP AS GlobalParams
  6516.  SHARED PaletteC%(), PaletteP$(), PaletteCh%()
  6517.  SHARED GFI AS FontInfo
  6518.  SHARED LLayout AS LegendLayout
  6519.  
  6520.          ' If legend flag is No then exit:
  6521.          IF GE.Legend.Legend = cNo THEN EXIT SUB
  6522.  
  6523.          ' Select and render the legend window:
  6524.          clSelectRelWindow GE.Legend.LegendWindow
  6525.          clRenderWindow GE.Legend.LegendWindow
  6526.          WINDOW
  6527.          GTextWindow 0, 0, 0, 0, cFalse
  6528.  
  6529.          ' Start with the first label, set the Y position of the first line
  6530.          ' of labels and loop through all the rows in the legend:
  6531.          clSetChartFont GE.Legend.TextFont
  6532.          LabelNum% = First
  6533.          YPos% = LLayout.HorizBorder
  6534.          FOR i% = 1 TO LLayout.NumRow
  6535.  
  6536.                  ' Set position of beginning of row:
  6537.                  XPos% = LLayout.VertBorder
  6538.  
  6539.                  FOR J% = 1 TO LLayout.NumCol
  6540.  
  6541.                          ' Map the label number to a valid palette reference:
  6542.                          MJ% = clMap2Pal%(LabelNum% - First + 1)
  6543.  
  6544.                          ' Depending on ChartType draw either a filled box or
  6545.                          ' plot character used for plotting:
  6546.                          XStep% = LLayout.SymbolSize / GP.Aspect
  6547.                          SELECT CASE GE.ChartType
  6548.  
  6549.                                  CASE cBar, cColumn, cPie:
  6550.                                          LINE (XPos%, YPos%)-STEP(XStep%, LLay
  6551.                                          LINE (XPos%, YPos%)-STEP(XStep%, LLay
  6552.                                          PAINT (XPos% + 1, YPos% + 1), Palette
  6553.                                          LINE (XPos%, YPos%)-STEP(XStep%, LLay
  6554.  
  6555.                                  CASE cLine, cScatter:
  6556.                                          clSetCharColor MJ%
  6557.                                          PlotChr$ = CHR$(PaletteCh%(MJ%))
  6558.                                          clHPrint XPos% + XStep% - GFI.AvgWidt
  6559.  
  6560.                          END SELECT
  6561.  
  6562.                          ' Print the label for this entry in the legend:
  6563.                          clSetCharColor GE.Legend.TextColor
  6564.                          clHPrint XPos% + LLayout.LabelOffset, YPos% - GFI.Lea
  6565.  
  6566.                          ' Increment the label count and check count has finis
  6567.                          LabelNum% = LabelNum% + 1
  6568.                          IF LabelNum% > Last THEN EXIT SUB
  6569.  
  6570.                          ' Move over to the next column:
  6571.                          XPos% = XPos% + LLayout.ColSpacing
  6572.  
  6573.                  NEXT J%
  6574.  
  6575.                  ' Move position to the next row:
  6576.                  YPos% = YPos% + LLayout.RowSpacing
  6577.  
  6578.          NEXT i%
  6579.  
  6580.  END SUB
  6581.  
  6582.  '=== clDrawLineData - Draws data portion line chart
  6583.  '
  6584.  '  Arguments:
  6585.  '     None
  6586.  '
  6587.  '  Return Values:
  6588.  '     None
  6589.  '
  6590.  '=================================================================
  6591.  SUB clDrawLineData
  6592.  SHARED GE AS ChartEnvironment, GP AS GlobalParams
  6593.  SHARED PaletteC%(), PaletteS%(), PaletteCh%()
  6594.  SHARED GFI AS FontInfo
  6595.  SHARED V1()
  6596.  
  6597.          ' First, set the appropriate font and make text horizontal:
  6598.          clSetChartFont GE.DataFont
  6599.          SetGTextDir 0
  6600.  
  6601.          ' Then, set the view to DataWindow:
  6602.          clSelectRelWindow GE.DataWindow
  6603.  
  6604.          ' Set the window to match:
  6605.          WINDOW (GE.XAxis.ScaleMin, GE.YAxis.ScaleMin)-(GE.XAxis.ScaleMax, GE.
  6606.          GTextWindow GE.XAxis.ScaleMin, GE.YAxis.ScaleMin, GE.XAxis.ScaleMax,
  6607.  
  6608.          ' Loop through the series:
  6609.          FOR J% = 1 TO GP.NSeries
  6610.  
  6611.                  ' Map the series number into a valid palette reference:
  6612.                  MJ% = clMap2Pal%(J%)
  6613.  
  6614.                  ' Calculate starting X location of first point and set
  6615.                  ' last value to missing (since this is the first value in the
  6616.                  ' series the last value wasn't there):
  6617.                  StartLoc = GE.XAxis.ScaleMin + GE.XAxis.TicInterval / 2
  6618.                  LastMissing% = cYes
  6619.  
  6620.                  FOR i% = 1 TO GP.NVals
  6621.  
  6622.                          ' Get a value from the data array:
  6623.                          V = V1(i%, J%)
  6624.  
  6625.                          ' If the value is missing, set the LastMissing flag t
  6626.                          ' go to the next value:
  6627.                          IF V = cMissingValue THEN
  6628.                                  LastMissing% = cYes
  6629.  
  6630.                          ' If the value is not missing then try to chart it:
  6631.                          ELSE
  6632.  
  6633.                                  ' Scale the value (and convert it to a log if
  6634.                                  ' Log axis):
  6635.                                  IF GE.YAxis.AutoScale = cYes THEN V = V / GE.
  6636.  
  6637.                                  ' If the style dictates lines and the last po
  6638.                                  ' missing then draw a line between the last p
  6639.                                  IF GE.ChartStyle = cLines AND LastMissing% <>
  6640.                                          LINE -(StartLoc, V), PaletteC%(MJ%),
  6641.                                  END IF
  6642.  
  6643.                                  ' Position and print character:
  6644.                                  CX% = PMAP(StartLoc, 0) - GetGTextLen(CHR$(Pa
  6645.                                  CY% = PMAP(V, 1) - GFI.Ascent / 2
  6646.                                  clSetCharColor MJ%
  6647.                                  clHPrint CX%, CY%, CHR$(PaletteCh%(MJ%))
  6648.  
  6649.                                  PSET (StartLoc, V), POINT(StartLoc, V)
  6650.  
  6651.                                  LastMissing% = cNo
  6652.                          END IF
  6653.  
  6654.                          ' Move to next category position:
  6655.                          StartLoc = StartLoc + GE.XAxis.TicInterval
  6656.                  NEXT i%
  6657.          NEXT J%
  6658.  
  6659.  END SUB
  6660.  
  6661.  '=== clDrawPieData - Draws data part of a pie chart
  6662.  '
  6663.  '  Arguments:
  6664.  '     Value(1)    -  One-dimensional array of data values
  6665.  '
  6666.  '     Expl(1)     -  One-dimensional array of explode flags (1=explode, 0=no)
  6667.  '
  6668.  '     N%          -  The number of data values to plot
  6669.  '  Return Values:
  6670.  '     None
  6671.  '
  6672.  '=================================================================
  6673.  SUB clDrawPieData (value() AS SINGLE, Expl() AS INTEGER, N AS INTEGER)
  6674.  SHARED GE AS ChartEnvironment
  6675.  SHARED GP AS GlobalParams
  6676.  SHARED GFI AS FontInfo
  6677.  SHARED PaletteC%(), PaletteP$()
  6678.  
  6679.          ' Set the font to use for percent labels:
  6680.          clSetChartFont GE.DataFont
  6681.  
  6682.          ' Set up some reference variables:
  6683.          Pi2 = 2 * cPiVal                  ' 2*PI for radians conversions
  6684.          MinAngle = Pi2 / 120              ' Smallest wedge to try to paint
  6685.          A1 = -.0000001                    ' Starting and ending angle (set
  6686.          A2 = A1                           ' to very small negative to get
  6687.  
  6688.  
  6689.          ' Size the pie.
  6690.          ' Choose the point in the middle of the data window for the pie cente
  6691.          WINDOW (0, 0)-(1, 1)
  6692.          X = PMAP(.5, 0)                  ' Distance: left to center
  6693.          Y = PMAP(.5, 1)                  ' Distance: bottom to center
  6694.          WINDOW                           ' Now, use physical coordinates (pix
  6695.          GTextWindow 0, 0, 0, 0, cFalse
  6696.  
  6697.          ' Adjust radii for percent labels if required:
  6698.          clSetChartFont GE.DataFont
  6699.          IF GE.ChartStyle = cPercent THEN
  6700.                  RadiusX = (X - 6 * GFI.AvgWidth) * GP.Aspect
  6701.                  RadiusY = Y - 2 * GFI.PixHeight
  6702.          ELSE
  6703.                  RadiusX = X * GP.Aspect
  6704.                  RadiusY = Y
  6705.          END IF
  6706.  
  6707.          ' Pick the smallest radius (adjusted for screen aspect) then reduce
  6708.          ' it by 10% so the pie isn't too close to the window border:
  6709.          IF RadiusX < RadiusY THEN
  6710.                  Radius = RadiusX
  6711.          ELSE
  6712.                  Radius = RadiusY
  6713.          END IF
  6714.          Radius = (.9 * Radius) / GP.Aspect
  6715.  
  6716.          ' If radius is too small then error:
  6717.          IF Radius <= 0 THEN
  6718.                  clSetError cBadDataWindow
  6719.                  EXIT SUB
  6720.          END IF
  6721.  
  6722.          ' Find the sum of the data values (use double precision Sum variable
  6723.          ' protect against overflow if summing large data values):
  6724.          Sum# = 0
  6725.          FOR i% = 1 TO GP.NSeries
  6726.                  IF value(i%) > 0 THEN Sum# = Sum# + value(i%)
  6727.          NEXT i%
  6728.  
  6729.          ' Loop through drawing and painting the wedges:
  6730.          FOR i% = 1 TO N
  6731.  
  6732.                  ' Map I% to a valid palette reference:
  6733.                  MappedI% = clMap2Pal(i%)
  6734.  
  6735.                  ' Draw wedges for positive values only:
  6736.                  IF value(i%) > 0 THEN
  6737.  
  6738.                          ' Calculate wedge percent and wedge ending angle:
  6739.                          Percent = value(i%) / Sum#
  6740.                          A2 = A1 - Percent * Pi2
  6741.  
  6742.                          ' This locates the angle through the center of the pi
  6743.                          ' calculates X and Y components of the vector headed
  6744.                          ' direction:
  6745.                          Bisect = (A1 + A2) / 2
  6746.                          BisectX = Radius * COS(Bisect)
  6747.                          BisectY = Radius * SIN(Bisect) * GP.Aspect
  6748.  
  6749.                          ' If the piece is exploded then offset it 1/10th of a
  6750.                          ' along the bisecting angle calculated above:
  6751.                          IF Expl(i%) <> 0 THEN
  6752.                                  CX = X + .1 * BisectX
  6753.                                  CY = Y + .1 * BisectY
  6754.                          ELSE
  6755.                                  CX = X
  6756.                                  CY = Y
  6757.                          END IF
  6758.  
  6759.                          ' If the angle is large enough, paint the wedge (if w
  6760.                          ' smaller angles are painted, the "paint" will someti
  6761.                          IF (A1 - A2) > MinAngle THEN
  6762.                                  PX = CX + .8 * BisectX
  6763.                                  PY = CY + .8 * BisectY
  6764.  
  6765.                                  ' Outline the wedge in color 1 and paint it b
  6766.                                  CIRCLE (CX, CY), Radius, 1, A1, A2, GP.Aspect
  6767.                                  PAINT (PX, PY), 0, 1
  6768.                                  ' Paint with the appropriate pattern:
  6769.                                  PAINT (PX, PY), PaletteP$(MappedI%), 1
  6770.                          END IF
  6771.                          ' draw the wedge in the correct color:
  6772.                          CIRCLE (CX, CY), Radius, PaletteC%(MappedI%), A1, A2,
  6773.  
  6774.                          ' Label pie wedge with percent if appropriate:
  6775.                          IF GE.ChartStyle = cPercent THEN
  6776.                                  Label$ = clVal2Str$(Percent * 100, 1, 1) + "%
  6777.                                  LabelX% = CX + BisectX + (GFI.AvgWidth * COS(
  6778.                                  LabelY% = CY + BisectY + (GFI.AvgWidth * SIN(
  6779.  
  6780.                                  ' Adjust label location for the quadrant:
  6781.                                  Quadrant% = FIX((ABS(Bisect / Pi2)) * 4)
  6782.                                  IF Quadrant% = 0 OR Quadrant% = 1 THEN
  6783.                                          LabelY% = LabelY% - GFI.Ascent
  6784.                                  END IF
  6785.                                  IF Quadrant% = 1 OR Quadrant% = 2 THEN
  6786.                                          LabelX% = LabelX% - GetGTextLen(Label
  6787.                                  END IF
  6788.  
  6789.                                  clSetCharColor GE.Legend.TextColor
  6790.                                  clHPrint LabelX%, LabelY%, Label$
  6791.                          END IF
  6792.                  END IF
  6793.  
  6794.                  ' Set the beginning of next wedge to the end of this one:
  6795.                  A1 = A2
  6796.  
  6797.          NEXT i%
  6798.  
  6799.  END SUB
  6800.  
  6801.  '=== clDrawScatterData - Draws data portion of Scatter chart
  6802.  '
  6803.  '  Arguments:
  6804.  '     None
  6805.  '
  6806.  '  Return Values:
  6807.  '     None
  6808.  '
  6809.  '=================================================================
  6810.  SUB clDrawScatterData
  6811.  SHARED GE AS ChartEnvironment, GP AS GlobalParams
  6812.  SHARED PaletteC%(), PaletteS%(), PaletteCh%()
  6813.  SHARED GFI AS FontInfo
  6814.  SHARED V1(), V2()
  6815.  
  6816.          ' Select the chart font and make text output horizontal:
  6817.          clSetChartFont GE.DataFont
  6818.          SetGTextDir 0
  6819.  
  6820.          ' Now, loop through all the points charting them:
  6821.          FOR Series% = 1 TO GP.NSeries
  6822.  
  6823.                  ' Set LastMissing flag to Yes for first point in series:
  6824.                  LastMissing% = cYes
  6825.                  MS% = clMap2Pal%(Series%)
  6826.  
  6827.                  ' Loop through all the points, charting them:
  6828.                  FOR DataPoint% = 1 TO GP.NVals
  6829.  
  6830.                          ' Get the X-value and Y-values from the data arrays:
  6831.                          VX = V1(DataPoint%, Series%)
  6832.                          VY = V2(DataPoint%, Series%)
  6833.  
  6834.                          ' If either of the values to chart is missing set Las
  6835.                          ' flag to Yes to indicate a missing point and go to t
  6836.                          IF VX = cMissingValue OR VY = cMissingValue THEN
  6837.                                  LastMissing% = cYes
  6838.  
  6839.                          ELSE
  6840.  
  6841.                                  ' Otherwise, scale the X and Y values if Auto
  6842.                                  ' their respective axes:
  6843.                                  IF GE.XAxis.AutoScale = cYes THEN VX = VX / G
  6844.                                  IF GE.YAxis.AutoScale = cYes THEN VY = VY / G
  6845.  
  6846.                                  ' If this is a lined chart and the last point
  6847.                                  ' then draw a line from last point to the cur
  6848.                                  IF GE.ChartStyle = cLines AND LastMissing% <>
  6849.                                          LINE -(VX, VY), PaletteC%(MS%), , Pal
  6850.                                  END IF
  6851.  
  6852.                                  ' In any case draw the plot character.  Start
  6853.                                  ' screen coordinates of the character relativ
  6854.                                  ' just charted:
  6855.                                  CX% = PMAP(VX, 0) - GetGTextLen(CHR$(PaletteC
  6856.                                  CY% = PMAP(VY, 1) - GFI.Ascent / 2
  6857.  
  6858.                                  ' Now, set the character color and print it:
  6859.                                  clSetCharColor MS%
  6860.                                  clHPrint CX%, CY%, CHR$(PaletteCh%(MS%))
  6861.  
  6862.                                  ' Finally, reset the graphics cursor, since p
  6863.                                  ' character changed it:
  6864.                                  PSET (VX, VY), POINT(VX, VY)
  6865.  
  6866.                                  LastMissing% = cNo
  6867.                          END IF
  6868.  
  6869.                  NEXT DataPoint%
  6870.          NEXT Series%
  6871.  END SUB
  6872.  
  6873.  '=== clDrawTitles - Draws the main and subtitles on a chart
  6874.  '
  6875.  '  Arguments:
  6876.  '     None
  6877.  '
  6878.  '  Return Values:
  6879.  '     None
  6880.  '
  6881.  '=================================================================
  6882.  SUB clDrawTitles
  6883.  SHARED GE AS ChartEnvironment
  6884.  SHARED TTitleLayout AS TitleLayout
  6885.  
  6886.          ' Bottom of main title line is 1-1/2 character heights from the
  6887.          ' top of the chart window:
  6888.          YPos% = TTitleLayout.Top
  6889.          clPrintTitle GE.MainTitle, YPos%
  6890.  
  6891.          ' Add 1.5 * character height to y position for subtitle line position
  6892.          YPos% = YPos% + TTitleLayout.TitleOne + TTitleLayout.Middle
  6893.          clPrintTitle GE.SubTitle, YPos%
  6894.  
  6895.  END SUB
  6896.  
  6897.  '=== clFilter - Filters input data into dynamic working data array
  6898.  '
  6899.  '  Arguments:
  6900.  '     Axis     -  An AxisType variable
  6901.  '
  6902.  '     AxisMode%-  Mode for this axis
  6903.  '
  6904.  '     D1(1)    -  One-dimensional array of input data
  6905.  '
  6906.  '     D2(2)    -  Two-dimensional array for filtered data
  6907.  '
  6908.  '     N%       -  The number of values to transfer
  6909.  '
  6910.  '  Return Values:
  6911.  '     Alters values in D2()
  6912.  '
  6913.  '=================================================================
  6914.  SUB clFilter (Axis AS AxisType, AxisMode%, D1(), D2(), N%)
  6915.  
  6916.          ' If the axis is a category one then exit:
  6917.          IF AxisMode% = cCategory THEN EXIT SUB
  6918.  
  6919.          ' Transfer the data from the input data array to the working data
  6920.          ' array:
  6921.          FOR i% = 1 TO N%
  6922.                  D2(i%, 1) = D1(i%)
  6923.          NEXT i%
  6924.  
  6925.          ' Call FilterMS to go through the data again scaling it and taking
  6926.          ' logs depending on the settings for this axis:
  6927.          clFilterMS Axis, AxisMode%, D2(), D2(), N%, 1, 1
  6928.  
  6929.  END SUB
  6930.  
  6931.  '=== clFilterMS - Filters two-dimensional input data into the dynamic working
  6932.  '               data array
  6933.  '
  6934.  '  Arguments:
  6935.  '     Axis     -  An AxisType variable
  6936.  '
  6937.  '     AxisMode%-  Axis mode for the axis
  6938.  '
  6939.  '     D1(2)    -  Two-dimensional array of input data
  6940.  '
  6941.  '     D2(2)    -  Two-dimensional array for filtered data
  6942.  '
  6943.  '     N%       -  The number of values to transfer
  6944.  '
  6945.  '     First%   -  First data series to filter
  6946.  '
  6947.  '     Last%    -  Last data series to filter
  6948.  '
  6949.  '  Return Values:
  6950.  '     Alters values in D2()
  6951.  '
  6952.  '=================================================================
  6953.  SUB clFilterMS (Axis AS AxisType, AxisMode%, D1(), D2(), N%, First%, Last%)
  6954.  
  6955.          ' If the axis is a category axis then exit:
  6956.          IF AxisMode% = cCategory THEN EXIT SUB
  6957.  
  6958.          ' If this isn't an autoscale axis, use the scale factor from the
  6959.          ' environment.  If it is an autoscale axis don't scale at all now
  6960.          ' it will be done when the data is drawn on the screen:
  6961.          IF Axis.AutoScale = cNo THEN
  6962.                  ScaleFactor = Axis.ScaleFactor
  6963.          ELSE
  6964.                  ScaleFactor = 1
  6965.          END IF
  6966.  
  6967.          ' If this a log axis calculate the log base:
  6968.          IF AxisMode% = cLog THEN LogRef = LOG(Axis.LogBase)
  6969.  
  6970.          ' Loop through the data series:
  6971.          FOR J% = First% TO Last%
  6972.  
  6973.                  ' Loop through the values within the series:
  6974.                  FOR i% = 1 TO N%
  6975.  
  6976.                          ' Get a data value and if it isn't missing, then scal
  6977.                          V = D1(i%, J%)
  6978.                          IF V <> cMissingValue THEN V = V / ScaleFactor
  6979.  
  6980.                          ' If the axis is a log axis, then if the value is gre
  6981.                          ' it is safe to take it's log.  Otherwise, set the da
  6982.                          ' missing:
  6983.                          IF Axis.RangeType = cLog THEN
  6984.                                   IF V > 0 THEN
  6985.                                          V = LOG(V) / LogRef
  6986.                                  ELSE
  6987.                                          V = cMissingValue
  6988.                                  END IF
  6989.                          END IF
  6990.  
  6991.                          ' Place the value in the output data array:
  6992.                          D2(i%, J% - First% + 1) = V
  6993.  
  6994.                  NEXT i%
  6995.  
  6996.          NEXT J%
  6997.  
  6998.  END SUB
  6999.  
  7000.  '=== clFlagSystem - Sets GP.SysFlag to cYes
  7001.  '
  7002.  '  Arguments:
  7003.  '     None
  7004.  '
  7005.  '  Return Values:
  7006.  '     Alters the value of GP.SysFlag
  7007.  '
  7008.  '=================================================================
  7009.  SUB clFlagSystem
  7010.  SHARED GP AS GlobalParams
  7011.  
  7012.          GP.SysFlag = cYes
  7013.  
  7014.  END SUB
  7015.  
  7016.  '=== clFormatTics - Figures out tic label format and TicDecimals.
  7017.  '
  7018.  '  Arguments:
  7019.  '     Axis     -  AxisType variable for which to format tics.
  7020.  '
  7021.  '  Return Values:
  7022.  '     The TicFormat and Decimals elements may be changed for an axis
  7023.  '     if AutoTic is cYes.
  7024.  '
  7025.  '=================================================================
  7026.  SUB clFormatTics (Axis AS AxisType)
  7027.  
  7028.          ' If AutoScale isn't Yes then exit
  7029.          IF Axis.AutoScale <> cYes THEN EXIT SUB
  7030.  
  7031.          ' If the size of the largest value is bigger than seven decimal
  7032.          ' places then set TicFormat to exponential.  Otherwise, set it
  7033.          ' to normal:
  7034.          IF ABS(Axis.ScaleMin) >= 10 ^ 8 OR ABS(Axis.ScaleMax) >= 10 ^ 8 THEN
  7035.                  Axis.TicFormat = cExpFormat
  7036.          ELSE
  7037.                  Axis.TicFormat = cNormFormat
  7038.          END IF
  7039.  
  7040.          ' Pick the largest of the scale max and min (in absolute value) and
  7041.          ' use that to decide how many decimals to use when displaying the tic
  7042.          ' labels:
  7043.          Range = ABS(Axis.ScaleMax)
  7044.          IF ABS(Axis.ScaleMin) > Range THEN Range = ABS(Axis.ScaleMin)
  7045.          IF Range < 10 THEN
  7046.                  TicResolution = -INT(-ABS(LOG(Range) / LOG(10!))) + 1
  7047.                  IF TicResolution > 9 THEN TicResolution = 9
  7048.                  Axis.TicDecimals = TicResolution
  7049.          ELSE
  7050.                  Axis.TicDecimals = 0
  7051.          END IF
  7052.  
  7053.  END SUB
  7054.  
  7055.  '=== clGetStyle - Returns a predefined line-style definition
  7056.  '
  7057.  '  Arguments:
  7058.  '     StyleNum%   -  A number identifying the entry to return
  7059.  '
  7060.  '  Return Values:
  7061.  '     Returns the line-style for the specified style number
  7062.  '
  7063.  '=================================================================
  7064.  FUNCTION clGetStyle% (StyleNum%)
  7065.  
  7066.          SELECT CASE StyleNum%
  7067.                  CASE 1: Style% = &HFFFF
  7068.                  CASE 2: Style% = &HF0F0
  7069.                  CASE 3: Style% = &HF060
  7070.                  CASE 4: Style% = &HCCCC
  7071.                  CASE 5: Style% = &HC8C8
  7072.                  CASE 6: Style% = &HEEEE
  7073.                  CASE 7: Style% = &HEAEA
  7074.                  CASE 8: Style% = &HF6DE
  7075.                  CASE 9: Style% = &HF6F6
  7076.                  CASE 10: Style% = &HF56A
  7077.                  CASE 11: Style% = &HCECE
  7078.                  CASE 12: Style% = &HA8A8
  7079.                  CASE 13: Style% = &HAAAA
  7080.                  CASE 14: Style% = &HE4E4
  7081.                  CASE 15: Style% = &HC88C
  7082.          END SELECT
  7083.          clGetStyle% = Style%
  7084.  
  7085.  END FUNCTION
  7086.  
  7087.  '=== clHPrint - Prints text Horizontally on the screen
  7088.  '
  7089.  '  Arguments:
  7090.  '     X     -  X position for the lower left of the first character to be
  7091.  '              printed (in absolute screen coordinates)
  7092.  '
  7093.  '     Y     -  Y position for the lower left of the first character to be
  7094.  '              printed (in absolute screen coordinates)
  7095.  '
  7096.  '     Txt$  -  Text to print
  7097.  '
  7098.  '  Return Values:
  7099.  '     None
  7100.  '
  7101.  '=================================================================
  7102.  SUB clHPrint (X%, Y%, Txt$)
  7103.  
  7104.          ' Map the input coordinates relative to the current viewport:
  7105.          X = PMAP(X%, 2)
  7106.          Y = PMAP(Y%, 3)
  7107.  
  7108.          ' Output the text horizontally:
  7109.          SetGTextDir 0
  7110.          TextLen% = OutGText(X, Y, Txt$)
  7111.  
  7112.  END SUB
  7113.  
  7114.  '=== clInitChart - Initializes the charting library.
  7115.  '
  7116.  '  Arguments:
  7117.  '     None
  7118.  '
  7119.  '  Return Values:
  7120.  '     None
  7121.  '
  7122.  '  Remarks:
  7123.  '     This routine initializes some default data structures and is
  7124.  '     called automatically by charting routines if the variable
  7125.  '     GP.Initialized is cNo (or zero).
  7126.  '
  7127.  '=================================================================
  7128.  SUB clInitChart
  7129.  SHARED StdChars%(), GP AS GlobalParams
  7130.  
  7131.          ' Clear any previous errors
  7132.          clClearError
  7133.  
  7134.          ON ERROR GOTO UnexpectedErr
  7135.  
  7136.          ' Initialize PaletteSet to no so palettes will be initialized properl
  7137.          ' when ChartScreen is called:
  7138.          GP.PaletteSet = cNo
  7139.  
  7140.          ' Set up the list of plotting characters:
  7141.          PlotChars$ = "*ox=+/:&#@%![$^"
  7142.          StdChars%(0) = 0
  7143.          FOR i% = 1 TO cPalLen
  7144.                  StdChars%(i%) = ASC(MID$(PlotChars$, i%, 1))
  7145.          NEXT i%
  7146.  
  7147.          ' Initialize standard structures for title, axis, window and legend:
  7148.          clInitStdStruc
  7149.  
  7150.          GP.Initialized = cYes
  7151.  
  7152.  END SUB
  7153.  
  7154.  '=== clInitStdStruc - Initializes structures for standard titles, axes, etc.
  7155.  '
  7156.  '  Arguments:
  7157.  '     None
  7158.  '
  7159.  '  Return Values:
  7160.  '     None
  7161.  '
  7162.  '=================================================================
  7163.  SUB clInitStdStruc
  7164.  SHARED DAxis AS AxisType, DWindow AS RegionType
  7165.  SHARED DLegend AS LegendType, DTitle AS TitleType
  7166.  
  7167.  ' Set up default components of the default chart
  7168.  ' environment; start with default title:
  7169.  
  7170.  ' Default title definition:
  7171.  DTitle.Title = ""                ' Title text is blank
  7172.  DTitle.TitleFont = 1             ' Title font is first one
  7173.  DTitle.TitleColor = 1            ' Title color is white
  7174.  DTitle.Justify = cCenter         ' Center justified
  7175.  
  7176.  ' Default axis definition:
  7177.  DAxis.grid = cNo                 ' No grid
  7178.  DAxis.GridStyle = 1              ' Solid lines for grid
  7179.  DAxis.AxisTitle = DTitle         ' Use above to initialize axis title
  7180.  DAxis.AxisColor = 1              ' Axis color is white
  7181.  DAxis.Labeled = cYes             ' Label and tic axis
  7182.  DAxis.RangeType = cLinear        ' Linear axis
  7183.  DAxis.LogBase = 10               ' Logs to base 10
  7184.  DAxis.AutoScale = cYes           ' Automatically scale numbers if needed
  7185.  DAxis.ScaleTitle = DTitle        ' Scale title
  7186.  DAxis.TicFont = 1                ' Tic font is first one
  7187.  DAxis.TicDecimals = 0            ' No decimals
  7188.  
  7189.  ' Default window definition:
  7190.  DWindow.Background = 0           ' Black background
  7191.  DWindow.Border = cNo             ' Window will have no border
  7192.  DWindow.BorderColor = 1          ' Make the borders white
  7193.  DWindow.BorderStyle = 1          ' Solid-line borders
  7194.  
  7195.  ' Default legend definition:
  7196.  DLegend.Legend = cYes            ' Draw a legend if multi-series chart
  7197.  DLegend.Place = cRight           ' On the right side
  7198.  DLegend.TextColor = 1            ' Legend text is white on black
  7199.  DLegend.TextFont = 1             ' Legend text font is first one
  7200.  DLegend.AutoSize = cYes          ' Figure out size automatically
  7201.  DLegend.LegendWindow = DWindow   ' Use the default window specification
  7202.  
  7203.  END SUB
  7204.  
  7205.  '=== clLabelXTics - Labels tic marks for X axis
  7206.  '
  7207.  '  Arguments:
  7208.  '     Axis     -  An AxisType variable containing axis specification
  7209.  '
  7210.  '     Cat$(1)  -  One-dimensional array of category labels.  Ignored
  7211.  '                 if axis not category axis
  7212.  '
  7213.  '     TicX     -  X coordinate of first tic mark
  7214.  '
  7215.  '     TicY     -  Y coordinate of tic tip (portion away from axis)
  7216.  '
  7217.  '     YBoundry% -  Y coordinate of bottom of tic labels
  7218.  '
  7219.  '  Return Values:
  7220.  '     None
  7221.  '
  7222.  '=================================================================
  7223.  SUB clLabelXTics (Axis AS AxisType, Cat$(), TicX, TicTotX%, TicY, YBoundry%)
  7224.  SHARED GFI AS FontInfo
  7225.  SHARED GP AS GlobalParams
  7226.  SHARED GE AS ChartEnvironment
  7227.  
  7228.          ' If this axis isn't supposed to be labeled then exit:
  7229.          IF Axis.Labeled <> cYes THEN EXIT SUB
  7230.  
  7231.          ' Set the appropriate color, font, and orientation for tic labels:
  7232.          clSetCharColor Axis.AxisColor
  7233.          clSetChartFont Axis.TicFont
  7234.          SetGTextDir 0
  7235.  
  7236.          ' The Y coordinate of the labels will be a constant .5 character
  7237.          ' heights below the end of the tic marks (TicY):
  7238.          Y% = PMAP(TicY, 1) + (GFI.Ascent - GFI.Leading) / 2
  7239.          IF GP.XStagger = cYes THEN
  7240.                  YDrop% = (3 * GFI.Ascent - GFI.Leading) / 2
  7241.          ELSE
  7242.                  YDrop% = 0
  7243.          END IF
  7244.          YBoundry% = Y% + YDrop% + GFI.PixHeight
  7245.  
  7246.          ' Loop through and write labels
  7247.          TX = TicX
  7248.          CatNum% = 1
  7249.          Stagger% = cFalse
  7250.          FOR i% = 1 TO TicTotX%
  7251.  
  7252.                  ' The label depends on axis mode (category, value):
  7253.                  SELECT CASE GP.XMode
  7254.                          CASE cCategory: Txt$ = Cat$(CatNum%)
  7255.                          CASE ELSE:      Txt$ = clVal2Str$(TX, Axis.TicDecimal
  7256.                  END SELECT
  7257.                  TxtLen% = GetGTextLen(Txt$)
  7258.                  IF GP.XMode = cCategory THEN
  7259.                          MaxLen% = 2 * (GE.DataWindow.X2 - GE.DataWindow.X1) /
  7260.                          IF MaxLen% < 0 THEN MaxLen% = 0
  7261.                          DO UNTIL TxtLen% <= MaxLen%
  7262.                                  Txt$ = LEFT$(Txt$, LEN(Txt$) - 1)
  7263.                                  TxtLen% = GetGTextLen(Txt$)
  7264.                          LOOP
  7265.                  END IF
  7266.  
  7267.                  ' Center the label under the tic mark and print it:
  7268.                  X% = PMAP(TX, 0) - (TxtLen%) / 2
  7269.  
  7270.                  IF Stagger% THEN
  7271.                          clHPrint X%, Y% + YDrop%, Txt$
  7272.                          Stagger% = cFalse
  7273.                  ELSE
  7274.                          clHPrint X%, Y%, Txt$
  7275.                          Stagger% = cTrue
  7276.                  END IF
  7277.  
  7278.                  ' Move to the next tic mark:
  7279.                  TX = TX + Axis.TicInterval
  7280.                  CatNum% = CatNum% + 1
  7281.          NEXT i%
  7282.  
  7283.  END SUB
  7284.  
  7285.  '=== clLabelYTics - Labels tic marks and draws Y axis title
  7286.  '
  7287.  '  Arguments:
  7288.  '     Axis     -  An AxisType variable containing axis specification
  7289.  '
  7290.  '     Cat$(1)  -  One-dimensional array of category labels.  Ignored
  7291.  '                 if axis not category axis
  7292.  '
  7293.  '     TicX     -  X coordinate of first tic's tip (away from axis)
  7294.  '
  7295.  '     TicY     -  Y coordinate of first tic
  7296.  '
  7297.  '  Return Values:
  7298.  '     None
  7299.  '
  7300.  '=================================================================
  7301.  SUB clLabelYTics (Axis AS AxisType, Cat$(), TicX, TicY, TicTotY%)
  7302.  SHARED GFI AS FontInfo
  7303.  SHARED GP AS GlobalParams
  7304.  
  7305.          ' If axis isn't supposed to be labeled then exit:
  7306.          IF Axis.Labeled <> cYes THEN EXIT SUB
  7307.  
  7308.          ' Set the appropriate color, font, and orientation for tic labels:
  7309.          clSetCharColor Axis.AxisColor
  7310.          clSetChartFont Axis.TicFont
  7311.          SetGTextDir 0
  7312.  
  7313.          ' Loop through and write labels
  7314.          TY = TicY
  7315.          CatNum% = 1
  7316.          FOR i% = 1 TO TicTotY%
  7317.  
  7318.                  ' The label depends on axis mode (category, value):
  7319.                  SELECT CASE GP.YMode
  7320.                          CASE cCategory: Txt$ = Cat$(GP.NVals - CatNum% + 1)
  7321.                          CASE ELSE:      Txt$ = clVal2Str$(TY, Axis.TicDecimal
  7322.                  END SELECT
  7323.                  TxtLen% = GetGTextLen(Txt$)
  7324.  
  7325.                  ' Space the label 1/2 character width to the left of the tic
  7326.                  ' mark and center it vertically on the tic mark (round vertic
  7327.                  ' location to the next highest integer):
  7328.                  X% = PMAP(TicX, 0) - TxtLen% - (.5 * GFI.MaxWidth)
  7329.                  Y% = -INT(-(PMAP(TY, 1) - (GFI.Ascent + GFI.Leading) / 2))
  7330.  
  7331.                  ' Print the label:
  7332.                  clHPrint X%, Y%, Txt$
  7333.  
  7334.                  ' Go to the next tic mark:
  7335.                  TY = TY + Axis.TicInterval
  7336.                  CatNum% = CatNum% + 1
  7337.          NEXT i%
  7338.  
  7339.  END SUB
  7340.  
  7341.  '=== clLayoutLegend - Calculates size of the legend
  7342.  '
  7343.  '  Arguments:
  7344.  '     SeriesLabel$(1) - The labels used in the legend
  7345.  '
  7346.  '     First%   - The first series (label) to process
  7347.  '
  7348.  '     Last%    - The last series (label) to process
  7349.  '
  7350.  '  Return Values:
  7351.  '     The coordinates in the legend window portion of Env are altered
  7352.  '
  7353.  '  Remarks:
  7354.  '     Sizing the legend window requires finding out where it goes (right
  7355.  '     or bottom) and determining how big the labels are and how big
  7356.  '     the legend needs to be to hold them.
  7357.  '
  7358.  '=================================================================
  7359.  SUB clLayoutLegend (SeriesLabel$(), First%, Last%)
  7360.  SHARED GE AS ChartEnvironment, GP AS GlobalParams
  7361.  SHARED GFI AS FontInfo
  7362.  SHARED LLayout AS LegendLayout
  7363.  SHARED TTitleLayout AS TitleLayout
  7364.  DIM W AS RegionType
  7365.  
  7366.          ' If "no legend" is specified, then exit:
  7367.          IF GE.Legend.Legend = cNo THEN EXIT SUB
  7368.  
  7369.          ' This may be an auto legend or not, but, in either case we're
  7370.          ' going to need the following information:
  7371.          clSetChartFont GE.Legend.TextFont
  7372.  
  7373.          LLayout.SymbolSize = GFI.Ascent - GFI.Leading - 1
  7374.          LLayout.HorizBorder = GFI.Ascent
  7375.          LLayout.VertBorder = GFI.AvgWidth
  7376.          LLayout.RowSpacing = 1.75 * (LLayout.SymbolSize + 1)
  7377.          LLayout.LabelOffset = LLayout.SymbolSize / GP.Aspect + GFI.AvgWidth
  7378.  
  7379.          'RowLeading% = LLayout.RowSpacing - LLayout.SymbolSize
  7380.          RowLeading% = .75 * LLayout.SymbolSize + 1.75
  7381.  
  7382.          ColWid% = clMaxStrLen(SeriesLabel$(), First%, Last%) + LLayout.LabelO
  7383.          LLayout.ColSpacing = ColWid% + GFI.AvgWidth
  7384.  
  7385.          ' If this isn't an autosize legend:
  7386.          IF GE.Legend.AutoSize = cNo THEN
  7387.  
  7388.                  ' Check the legend coordinates supplied by the user to make
  7389.                  ' sure that they are valid.  If they are, exit:
  7390.                  W = GE.Legend.LegendWindow
  7391.                  LWid% = W.X2 - W.X1
  7392.                  LHgt% = W.Y2 - W.Y1
  7393.                  IF LWid% > 0 AND LHgt% > 0 THEN
  7394.  
  7395.                          ' Calculate the number of columns and rows of labels
  7396.                          ' fit in the legend:
  7397.                          NumCol% = INT((LWid% - LLayout.VertBorder) / (LLayout
  7398.                          IF NumCol% <= 0 THEN NumCol% = 1
  7399.                          IF NumCol% > GP.NSeries THEN NumCol% = GP.NSeries
  7400.                          NumRow% = -INT(-GP.NSeries / NumCol%)
  7401.                          LLayout.NumRow = NumRow%
  7402.                          LLayout.NumCol = NumCol%
  7403.  
  7404.                          ' Re-calculate the column and row spacing:
  7405.                          LLayout.ColSpacing = INT((LWid% - LLayout.VertBorder)
  7406.                          LLayout.RowSpacing = INT((LHgt% - 2 * LLayout.HorizBo
  7407.  
  7408.                          EXIT SUB
  7409.  
  7410.                  ' If invalid legend coordinates are discovered set an error a
  7411.                  ' go on to calculate new ones:
  7412.                  ELSE
  7413.  
  7414.                    clSetError cBadLegendWindow
  7415.  
  7416.                  END IF
  7417.          END IF
  7418.  
  7419.          ' Do remaining calculations according to the legend placement specifi
  7420.          ' (right, bottom, overlay):
  7421.          SELECT CASE GE.Legend.Place
  7422.  
  7423.                  CASE cRight, cOverlay:
  7424.  
  7425.                          ' Leave room at top for chart titles:
  7426.                          Top% = TTitleLayout.TotalSize
  7427.  
  7428.                          ' Figure out the maximum number of legend rows that w
  7429.                          ' fit in the amount of space you have left for the le
  7430.                          ' height.  Then, see how many columns are needed.  On
  7431.                          ' the number of columns is set refigure how many rows
  7432.                          ' required:
  7433.                          NumRow% = INT((GP.ChartHgt - Top% - 2 * LLayout.Horiz
  7434.                          IF NumRow% > GP.NSeries THEN NumRow% = GP.NSeries
  7435.                          NumCol% = -INT(-GP.NSeries / NumRow%)
  7436.                          NumRow% = -INT(-GP.NSeries / NumCol%)
  7437.  
  7438.                          ' Set the width and height:
  7439.                          LWid% = NumCol% * LLayout.ColSpacing - GFI.AvgWidth +
  7440.                          LHgt% = (NumRow% * LLayout.RowSpacing - RowLeading% +
  7441.  
  7442.                          ' Place the legend one character width from right and
  7443.                          ' what will be the top of the data window:
  7444.                          LLft% = GP.ChartWid - 1 - LWid% - GFI.AvgWidth
  7445.                          LTop% = Top%
  7446.  
  7447.                  CASE cBottom:
  7448.  
  7449.                          ' The number of label columns that will fit (using th
  7450.                          ' procedure as above except figure columns first):
  7451.                          NumCol% = INT((GP.ChartWid - 2 * LLayout.HorizBorder)
  7452.                          IF NumCol% > GP.NSeries THEN NumCol% = GP.NSeries
  7453.                          NumRow% = -INT(-GP.NSeries / NumCol%)
  7454.                          NumCol% = -INT(-GP.NSeries / NumRow%)
  7455.  
  7456.                          ' Set the width and height:
  7457.                          LWid% = NumCol% * LLayout.ColSpacing - GFI.AvgWidth +
  7458.                          LHgt% = (NumRow% * LLayout.RowSpacing - RowLeading% +
  7459.  
  7460.                          ' Center the legend horizontally one character from t
  7461.                          LLft% = (GP.ChartWid - 1 - LWid%) / 2
  7462.                          LTop% = GP.ChartHgt - 1 - LHgt% - GFI.Ascent
  7463.  
  7464.          END SELECT
  7465.  
  7466.          ' Record legend columns and rows:
  7467.          LLayout.NumRow = NumRow%
  7468.          LLayout.NumCol = NumCol%
  7469.  
  7470.          ' Finally, place the legend coordinates in GE:
  7471.          GE.Legend.LegendWindow.X1 = LLft%
  7472.          GE.Legend.LegendWindow.Y1 = LTop%
  7473.          GE.Legend.LegendWindow.X2 = LLft% + LWid%
  7474.          GE.Legend.LegendWindow.Y2 = LTop% + LHgt%
  7475.  
  7476.          ' If, after all this, the legend window is invalid, set error:
  7477.          IF LLft% < 0 OR LTop% < 0 OR LWid% <= 0 OR LHgt% <= 0 THEN
  7478.                  clSetError cBadLegendWindow
  7479.          END IF
  7480.  
  7481.  END SUB
  7482.  
  7483.  '=== clLayoutTitle - Figures out title layouts for Top, X-axis and
  7484.  '                      Y-axis titles
  7485.  '
  7486.  '  Arguments:
  7487.  '     TL    -  Layout variable into which to place titles
  7488.  '
  7489.  '     T1    -  First title
  7490.  '
  7491.  '     T2    -  Second Title
  7492.  '
  7493.  '  Return Values:
  7494.  '     none
  7495.  '
  7496.  '=================================================================
  7497.  SUB clLayoutTitle (TL AS TitleLayout, T1 AS TitleType, T2 AS TitleType)
  7498.  SHARED GFI AS FontInfo
  7499.  
  7500.          ' Set the title heights initially to 0:
  7501.          TL.TitleOne = 0
  7502.          TL.TitleTwo = 0
  7503.  
  7504.          ' If the first title is set then get its height:
  7505.          Total% = 0
  7506.          IF LTRIM$(T1.Title) <> "" THEN
  7507.                  clSetChartFont T1.TitleFont
  7508.                  TL.TitleOne = GFI.PixHeight
  7509.                  Total% = Total% + 1
  7510.          END IF
  7511.  
  7512.          ' If the second title is set then get it's height:
  7513.          IF LTRIM$(T2.Title) <> "" THEN
  7514.                  clSetChartFont T2.TitleFont
  7515.                  TL.TitleTwo = GFI.PixHeight
  7516.                  Lead2% = GFI.Leading
  7517.                  Total% = Total% + 1
  7518.          END IF
  7519.  
  7520.          ' Set the "leading" values for label spacing depending on how many
  7521.          ' of the titles were non-blank:
  7522.          TotalHeight% = TL.TitleOne + TL.TitleTwo
  7523.          SELECT CASE Total%
  7524.                  CASE 0:
  7525.                          TL.Top = 8
  7526.                          TL.Middle = 0
  7527.                          TL.Bottom = 4
  7528.  
  7529.                  CASE 1:
  7530.                          TL.Top = 8 + TotalHeight% / 8
  7531.                          TL.Middle = 0
  7532.                          TL.Bottom = TL.Top
  7533.  
  7534.                  CASE 2:
  7535.                          TL.Top = 8 + TotalHeight% / 8
  7536.                          TL.Middle = 0: IF Lead2% = 0 THEN TL.Middle = TL.Titl
  7537.                          TL.Bottom = TL.Top
  7538.          END SELECT
  7539.  
  7540.          TL.TotalSize = TL.Top + TL.TitleOne + TL.Middle + TL.TitleTwo + TL.Bo
  7541.  
  7542.  END SUB
  7543.  
  7544.  '=== clMap2Attrib% - Maps an integer to a screen attribute for current
  7545.  '                    screen mode
  7546.  '
  7547.  '  Arguments:
  7548.  '     N% - The number to map
  7549.  '
  7550.  '  Return Values:
  7551.  '     The function returns:
  7552.  '        0 is mapped to 0, all other numbers are mapped to the range
  7553.  '        1 to GP.MaxColor
  7554.  '
  7555.  '=================================================================
  7556.  FUNCTION clMap2Attrib% (N%)
  7557.  SHARED GP AS GlobalParams
  7558.  
  7559.          AbsN% = ABS(N%)
  7560.          IF AbsN% = 0 THEN
  7561.                  clMap2Attrib% = AbsN%
  7562.          ELSE
  7563.                  clMap2Attrib% = (AbsN% - 1) MOD GP.MaxColor + 1
  7564.          END IF
  7565.  
  7566.  END FUNCTION
  7567.  
  7568.  '=== clMap2Pal% - Maps an integer into a palette reference
  7569.  '
  7570.  '  Arguments:
  7571.  '     N% - The number to map
  7572.  '
  7573.  '  Return Values:
  7574.  '     The function returns (N%-1) MOD cPalLen + 1
  7575.  '
  7576.  '  Remarks:
  7577.  '     This FUNCTION is used in almost every reference to a palette to ensure
  7578.  '     that an invalid number doesn't cause a reference outside of a palette
  7579.  '     array (and thus crash the library).  This FUNCTION maps the first
  7580.  '     cPalLen values to themselves. Numbers above cPalLen are mapped to
  7581.  '     the values 2..cPalLen.
  7582.  '
  7583.  '=================================================================
  7584.  FUNCTION clMap2Pal% (N%)
  7585.  
  7586.          AbsN% = ABS(N%)
  7587.          IF AbsN% > cPalLen THEN
  7588.                  clMap2Pal% = (AbsN% - 2) MOD (cPalLen - 1) + 2
  7589.          ELSE
  7590.                  clMap2Pal% = AbsN%
  7591.          END IF
  7592.  
  7593.  END FUNCTION
  7594.  
  7595.  '=== clMaxStrLen% - Finds the length of the longest string in a list
  7596.  '
  7597.  '  Arguments:
  7598.  '     Txt$(1)  -  One-dimensional array of strings to search
  7599.  '
  7600.  '     First%   -  First string to consider
  7601.  '
  7602.  '     Last%    -  Last string to consider
  7603.  '
  7604.  '  Return Values:
  7605.  '     This FUNCTION returns the length of the longest string
  7606.  '
  7607.  '=================================================================
  7608.  FUNCTION clMaxStrLen% (Txt$(), First%, Last%)
  7609.  
  7610.          ' Set Max to 0 then loop through each label updating Max if the
  7611.          ' label is longer:
  7612.          Max% = 0
  7613.          FOR Row% = First% TO Last%
  7614.                  L% = GetGTextLen(Txt$(Row%))
  7615.                  IF L% > Max% THEN Max% = L%
  7616.          NEXT Row%
  7617.  
  7618.          ' Return Max as the value of the FUNCTION:
  7619.          clMaxStrLen% = Max%
  7620.  
  7621.  END FUNCTION
  7622.  
  7623.  '=== clMaxVal - Returns the maximum of two numbers
  7624.  '
  7625.  '  Arguments:
  7626.  '     A  -  The first number
  7627.  '
  7628.  '     B  -  The second number
  7629.  '
  7630.  '  Return Values:
  7631.  '     The function returns the maximum of the two values
  7632.  '
  7633.  '=================================================================
  7634.  FUNCTION clMaxVal (A, B)
  7635.  
  7636.          IF A > B THEN clMaxVal = A ELSE clMaxVal = B
  7637.  
  7638.  END FUNCTION
  7639.  
  7640.  '=== clPrintTitle - Prints title correctly justified and colored
  7641.  '
  7642.  '  Arguments:
  7643.  '     TitleVar - A TitleType variable containing specifications for the
  7644.  '                title to be printed
  7645.  '
  7646.  '     Y%       - Vertical position in window for bottom of line
  7647.  '
  7648.  '  Return Values:
  7649.  '     None
  7650.  '
  7651.  '=================================================================
  7652.  SUB clPrintTitle (TitleVar AS TitleType, Y%)
  7653.  SHARED GFI AS FontInfo, GP AS GlobalParams
  7654.  
  7655.          ' Calculate width of the title text:
  7656.          clSetChartFont TitleVar.TitleFont
  7657.  
  7658.          Txt$ = RTRIM$(TitleVar.Title)
  7659.          TxtLen% = GetGTextLen(Txt$)
  7660.          IF TxtLen% = 0 THEN EXIT SUB
  7661.  
  7662.          ' Calculate horizontal position depending on justification style
  7663.          SELECT CASE TitleVar.Justify
  7664.  
  7665.                  CASE cCenter: X% = (GP.ChartWid - 1 - (TxtLen%)) / 2
  7666.                  CASE cRight:  X% = GP.ChartWid - 1 - TxtLen% - GFI.AvgWidth
  7667.                  CASE ELSE:    X% = GFI.AvgWidth
  7668.  
  7669.          END SELECT
  7670.  
  7671.          ' Set color of text and print it:
  7672.          clSetCharColor TitleVar.TitleColor
  7673.          clHPrint X%, Y%, Txt$
  7674.  
  7675.  END SUB
  7676.  
  7677.  '=== clRenderBar - Renders a bar for a bar or column chart
  7678.  '
  7679.  '  Arguments:
  7680.  '     X1    -  Left side of bar (in data world coordinates)
  7681.  '
  7682.  '     Y1    -  Top of bar (in data world coordinates)
  7683.  '
  7684.  '     X2    -  Right side of bar (in data world coordinates)
  7685.  '
  7686.  '     Y2    -  Bottom of bar (in data world coordinates)
  7687.  '
  7688.  '     C%    -  Palette entry number to use for border color and fill pattern
  7689.  '
  7690.  '  Return Values:
  7691.  '     None
  7692.  '
  7693.  '=================================================================
  7694.  SUB clRenderBar (X1, Y1, X2, Y2, C%)
  7695.  SHARED PaletteC%(), PaletteP$()
  7696.  
  7697.          ' First clear out space for the bar by drawing a bar in black:
  7698.          LINE (X1, Y1)-(X2, Y2), 0, BF
  7699.  
  7700.          ' Put a border around the bar and fill with pattern:
  7701.          MC% = clMap2Pal%(C%)
  7702.  
  7703.          LINE (X1, Y1)-(X2, Y2), 1, B
  7704.          PAINT ((X1 + X2) / 2, (Y1 + Y2) / 2), PaletteP$(MC%), 1
  7705.          LINE (X1, Y1)-(X2, Y2), PaletteC%(MC%), B
  7706.  
  7707.  END SUB
  7708.  
  7709.  '=== clRenderWindow - Renders a window on the screen
  7710.  '
  7711.  '  Arguments:
  7712.  '     W - A RegionType variable
  7713.  '
  7714.  '  Return Values:
  7715.  '     None
  7716.  '
  7717.  '  Remarks:
  7718.  '     This routine assumes that the viewport is set to the borders of
  7719.  '     the window to be rendered
  7720.  '
  7721.  '=================================================================
  7722.  SUB clRenderWindow (W AS RegionType)
  7723.  SHARED PaletteC%(), PaletteB%()
  7724.  
  7725.          ' Set window since the size of the viewport is unknown and draw
  7726.          ' a filled box of the background color specified by the window
  7727.          ' definition:
  7728.          WINDOW (0, 0)-(1, 1)
  7729.          LINE (0, 0)-(1, 1), PaletteC%(clMap2Pal%(W.Background)), BF
  7730.  
  7731.          ' Draw a border if specified:
  7732.          IF W.Border = cYes THEN
  7733.                  LINE (0, 0)-(1, 1), PaletteC%(clMap2Pal%(W.BorderColor)), B,
  7734.          END IF
  7735.  
  7736.  END SUB
  7737.  
  7738.  '=== clScaleAxis - Calculates minimum, maximum and scale factor for an axis
  7739.  '
  7740.  '  Arguments:
  7741.  '     A        - An AxisType variable
  7742.  '
  7743.  '     AxisMode%- cCategory or cValue
  7744.  '
  7745.  '     D1(2)    - Two-dimensional array of values to be scaled
  7746.  '
  7747.  '  Return Values:
  7748.  '     ScaleMin, ScaleMax, ScaleFactor, and ScaleTitle elements in
  7749.  '     axis variable will be altered if it is a category axis or
  7750.  '     AutoScale is Yes.
  7751.  '
  7752.  '=================================================================
  7753.  SUB clScaleAxis (Axis AS AxisType, AxisMode%, D1())
  7754.  SHARED GE AS ChartEnvironment, GP AS GlobalParams
  7755.  
  7756.          ' If this is a category axis then ignore all the flags and force
  7757.          ' scale parameters to those needed by charting routines:
  7758.          IF AxisMode% = cCategory THEN
  7759.                  Axis.ScaleMin = 0
  7760.                  Axis.ScaleMax = 1
  7761.                  Axis.ScaleFactor = 1
  7762.                  Axis.ScaleTitle.Title = ""
  7763.                  EXIT SUB
  7764.          END IF
  7765.  
  7766.          ' If AutoScale isn't Yes then exit:
  7767.          IF Axis.AutoScale <> cYes THEN EXIT SUB
  7768.  
  7769.          ' AutoScale was specified, calculate the different scale variables
  7770.          ' Set maximum and minimum to defaults.
  7771.  
  7772.          ' Initialize the value- and row-minimum and maximum values to zero:
  7773.          VMin = 0
  7774.          VMax = 0
  7775.  
  7776.          RMin = 0
  7777.          RMax = 0
  7778.  
  7779.          ' Compare data values for minimum and maximum:
  7780.          FOR Row% = 1 TO GP.NVals
  7781.  
  7782.                  ' Initialize positive and negative sum variables:
  7783.                  RSumPos = 0
  7784.                  RSumNeg = 0
  7785.  
  7786.                  ' Evaluate the value from this row in each series:
  7787.                  FOR Column% = 1 TO GP.NSeries
  7788.  
  7789.                          ' Get the value from the data array:
  7790.                          V = D1(Row%, Column%)
  7791.  
  7792.                          ' Process values that aren't missing only:
  7793.                          IF V <> cMissingValue THEN
  7794.  
  7795.                                  ' Add positive values to positive sum and neg
  7796.                                  ' negative sum:
  7797.                                  IF V > 0 THEN RSumPos = RSumPos + V
  7798.                                  IF V < 0 THEN RSumNeg = RSumNeg + V
  7799.  
  7800.                                  ' Compare the value against current maximum a
  7801.                                  ' replace them if appropriate:
  7802.                                  IF V < VMin THEN VMin = V
  7803.                                  IF V > VMax THEN VMax = V
  7804.  
  7805.                          END IF
  7806.  
  7807.                  NEXT Column%
  7808.  
  7809.                  ' Compare the positive and negative sums for this row with th
  7810.                  ' current row maximum and minimum and replace them if appropr
  7811.                  IF RSumNeg < RMin THEN RMin = RSumNeg
  7812.                  IF RSumPos > RMax THEN RMax = RSumPos
  7813.  
  7814.          NEXT Row%
  7815.  
  7816.          ' If the chart style is one, meaning that the data isn't stacked for
  7817.          ' bar and column charts, or it is a line or scatter chart then the sc
  7818.          ' minimum and maximum are the minimum and maximum values found.
  7819.          ' Each value is adjusted so the data is not drawn on or beyond the
  7820.          ' border of the data window:
  7821.          IF GE.ChartStyle = 1 OR GE.ChartType = cLine OR GE.ChartType = cScatt
  7822.                  IF VMin < 0 THEN
  7823.                          Axis.ScaleMin = VMin - .01 * (VMax - VMin)
  7824.                  END IF
  7825.                  IF VMax > 0 THEN
  7826.                          Axis.ScaleMax = VMax + .01 * (VMax - VMin)
  7827.                  END IF
  7828.  
  7829.          ' Otherwise, the scale minimum and maximum are the minimum and maximu
  7830.          ' sums of the data for each row:
  7831.          ELSE
  7832.                  IF RMin < 0 THEN
  7833.                          Axis.ScaleMin = RMin - .01 * (RMax - RMin)
  7834.                  END IF
  7835.                  IF RMax > 0 THEN
  7836.                          Axis.ScaleMax = RMax + .01 * (RMax - RMin)
  7837.                  END IF
  7838.          END IF
  7839.  
  7840.          ' If no data then force range to be non-zero:
  7841.          IF Axis.ScaleMin = Axis.ScaleMax THEN Axis.ScaleMax = 1
  7842.  
  7843.          ' Adjust the scale limits by ScaleFactor if required:
  7844.          clAdjustScale Axis
  7845.  
  7846.  END SUB
  7847.  
  7848.  '=== clSelectChartFont - Selects a font to use and gets info about it
  7849.  '
  7850.  '  Arguments:
  7851.  '     N%    -  Font number to use
  7852.  '
  7853.  '  Return Values:
  7854.  '     none
  7855.  '
  7856.  '=================================================================
  7857.  SUB clSelectChartFont (N%)
  7858.  SHARED GFI AS FontInfo
  7859.  
  7860.          ' Select the font and get information about it:
  7861.          SelectFont N%
  7862.          GetFontInfo GFI
  7863.  END SUB
  7864.  
  7865.  '=== clSelectChartWindow - Sets viewport to chart window
  7866.  '
  7867.  '  Arguments:
  7868.  '     Env         - A ChartEnvironment variable
  7869.  '
  7870.  '  Return Values:
  7871.  '     None
  7872.  '
  7873.  '  Remarks:
  7874.  '     This routine erases any previous viewport
  7875.  '
  7876.  '=================================================================
  7877.  SUB clSelectChartWindow
  7878.  SHARED GP AS GlobalParams
  7879.  
  7880.          ' Set viewport to chart window:
  7881.          VIEW (GP.CwX1, GP.CwY1)-(GP.CwX2, GP.CwY2)
  7882.  
  7883.  END SUB
  7884.  
  7885.  '=== clSelectRelWindow - Sets viewport to window relative to chart window
  7886.  '
  7887.  '  Arguments:
  7888.  '     Env   - A ChartEnvironment variable
  7889.  '
  7890.  '     W     - RegionType variable of window to set
  7891.  '
  7892.  '  Return Values:
  7893.  '     None
  7894.  '
  7895.  '  Remarks:
  7896.  '     This routine erases any previous viewport
  7897.  '
  7898.  '=================================================================
  7899.  SUB clSelectRelWindow (W AS RegionType)
  7900.  SHARED GP AS GlobalParams
  7901.  
  7902.          ' New viewport is defined relative to the current one:
  7903.          VIEW (GP.CwX1 + W.X1, GP.CwY1 + W.Y1)-(GP.CwX1 + W.X2, GP.CwY1 + W.Y2
  7904.  
  7905.  END SUB
  7906.  
  7907.  '=== clSetAxisModes - Sets axis modes for X- and Y-axis according to
  7908.  '                   ChartType
  7909.  '
  7910.  '  Arguments:
  7911.  '     None
  7912.  '
  7913.  '  Return Values:
  7914.  '     Alters XAxis and YAxis axis modes
  7915.  '
  7916.  '=================================================================
  7917.  SUB clSetAxisModes
  7918.  SHARED GE AS ChartEnvironment
  7919.  SHARED GP AS GlobalParams
  7920.  
  7921.          SELECT CASE GE.ChartType
  7922.  
  7923.                  CASE cBar:
  7924.                          GP.XMode = cValue
  7925.                          GP.YMode = cCategory
  7926.  
  7927.                  CASE cColumn, cLine:
  7928.                          GP.XMode = cCategory
  7929.                          GP.YMode = cValue
  7930.  
  7931.                  CASE cScatter:
  7932.                          GP.XMode = cValue
  7933.                          GP.YMode = cValue
  7934.  
  7935.                  CASE cPie:
  7936.                          GP.XMode = cCategory
  7937.                          GP.YMode = cCategory
  7938.  
  7939.          END SELECT
  7940.  
  7941.  END SUB
  7942.  
  7943.  '=== clSetCharColor - Sets color for DRAW characters
  7944.  '
  7945.  '  Arguments:
  7946.  '     N%    -  Color number
  7947.  '
  7948.  '  Return Values:
  7949.  '     None
  7950.  '
  7951.  '=================================================================
  7952.  SUB clSetCharColor (N%)
  7953.  SHARED PaletteC%()
  7954.  
  7955.          ' Check for valid color number then set color if correct:
  7956.          SetGTextColor PaletteC%(clMap2Pal%(N%))
  7957.  
  7958.  END SUB
  7959.  
  7960.  '=== clSetChartFont - Selects the specified font
  7961.  '
  7962.  '  Arguments:
  7963.  '     N%    -  Number of loaded font to select
  7964.  '
  7965.  '  Return Values:
  7966.  '     none
  7967.  '
  7968.  '=================================================================
  7969.  SUB clSetChartFont (N AS INTEGER)
  7970.  SHARED GFI AS FontInfo
  7971.  
  7972.          ' Select font and get information on it:
  7973.          SelectFont N%
  7974.          GetFontInfo GFI
  7975.  
  7976.  END SUB
  7977.  
  7978.  '=== clSetError - Sets the ChartLib error variable
  7979.  '
  7980.  '  Arguments:
  7981.  '     ErrNo    - The error number to set
  7982.  '
  7983.  '  Return Values:
  7984.  '     Sets ChartErr to ErrNo
  7985.  '
  7986.  '=================================================================
  7987.  SUB clSetError (ErrNo AS INTEGER)
  7988.  
  7989.          ChartErr = ErrNo
  7990.  
  7991.  END SUB
  7992.  
  7993.  '=== clSetGlobalParams - Sets some global parameters that other routines use
  7994.  '
  7995.  '  Arguments:
  7996.  '     None
  7997.  '
  7998.  '  Return Values:
  7999.  '     GP.ValLenX and GP.ValLenY are altered
  8000.  '
  8001.  '=================================================================
  8002.  SUB clSetGlobalParams
  8003.  SHARED GP AS GlobalParams, GE AS ChartEnvironment
  8004.  
  8005.          ' Figure out longest label on X axis:
  8006.          clSetChartFont GE.XAxis.TicFont
  8007.          SF = GE.XAxis.ScaleMin
  8008.          Len1 = GetGTextLen(clVal2Str$(SF, GE.XAxis.TicDecimals, GE.XAxis.TicF
  8009.          SF = GE.XAxis.ScaleMax
  8010.          Len2 = GetGTextLen(clVal2Str$(SF, GE.XAxis.TicDecimals, GE.XAxis.TicF
  8011.          GP.ValLenX = clMaxVal(Len1, Len2)
  8012.  
  8013.          ' Figure out longest label on Y axis:
  8014.          clSetChartFont GE.YAxis.TicFont
  8015.          SF = GE.YAxis.ScaleMin
  8016.          Len1 = GetGTextLen(clVal2Str$(SF, GE.YAxis.TicDecimals, GE.YAxis.TicF
  8017.          SF = GE.YAxis.ScaleMax
  8018.          Len2 = GetGTextLen(clVal2Str$(SF, GE.YAxis.TicDecimals, GE.YAxis.TicF
  8019.          GP.ValLenY = clMaxVal(Len1, Len2)
  8020.  
  8021.  END SUB
  8022.  
  8023.  '=== clSizeDataWindow - Calculates general data window size
  8024.  '
  8025.  '  Arguments:
  8026.  '     Cat$(1)  - One-dimensional array of category labels (only
  8027.  '                used if one of the axes is a category one)
  8028.  '
  8029.  '  Return Values:
  8030.  '     The X1, Y1, X2, Y2 elements of the GE variable will be
  8031.  '     set to the data window coordinates
  8032.  '
  8033.  '=================================================================
  8034.  SUB clSizeDataWindow (Cat$())
  8035.  SHARED GE AS ChartEnvironment
  8036.  SHARED GP AS GlobalParams
  8037.  SHARED GFI AS FontInfo
  8038.  SHARED TTitleLayout AS TitleLayout
  8039.  SHARED XTitleLayout AS TitleLayout
  8040.  SHARED YTitleLayout AS TitleLayout
  8041.  
  8042.          ' *** TOP
  8043.          ' Adjust the top of the data window:
  8044.          DTop% = TTitleLayout.TotalSize
  8045.  
  8046.          ' *** LEFT
  8047.          ' Do left side:
  8048.          DLeft% = YTitleLayout.TotalSize
  8049.  
  8050.          ' Add room for axis labels if the axis is labeled and not a pie chart
  8051.          IF GE.ChartType <> cPie THEN
  8052.                  IF GE.YAxis.Labeled = cYes THEN
  8053.  
  8054.                          ' Get the correct font:
  8055.                          clSetChartFont GE.YAxis.TicFont
  8056.  
  8057.                          ' If it is a category axis then add longest category
  8058.                          IF GP.YMode = cCategory THEN
  8059.                                  DLeft% = DLeft% + clMaxStrLen%(Cat$(), 1, GP.
  8060.  
  8061.                          ' If it a value axis just add characters for label (p
  8062.                          ' spacing):
  8063.                          ELSE
  8064.                                  DLeft% = DLeft% + GP.ValLenY + (.5 * GFI.MaxW
  8065.                          END IF
  8066.  
  8067.                  ELSEIF GP.XMode = cValue AND GE.XAxis.Labeled = cYes THEN
  8068.  
  8069.                          ' Then space over 1/2 of the leftmost label on the X
  8070.                          ' a value axis; if it's a category axis assume the la
  8071.                          ' correct:
  8072.                          DLeft% = DLeft% + GP.ValLenX \ 2
  8073.                  END IF
  8074.          END IF
  8075.  
  8076.          ' *** RIGHT
  8077.          ' For the right, space over 8 pixels from the right:
  8078.          DRight% = 12
  8079.  
  8080.          ' Then space over 1/2 of the rightmost label on the X Axis if it's
  8081.          ' a value axis; if it's a category axis assume the label will be
  8082.          ' correct:
  8083.          IF GP.XMode = cValue AND GE.XAxis.Labeled = cYes THEN
  8084.                  DRight% = DRight% + (GP.ValLenX) \ 2
  8085.          END IF
  8086.  
  8087.          DRight% = GP.ChartWid - DRight%
  8088.  
  8089.          ' *** YTIC MARKS
  8090.          ' Finally, adjust the window coordinates for tic marks (if it's not a
  8091.          ' pie chart):
  8092.          IF GE.ChartType <> cPie THEN
  8093.                  IF GE.YAxis.Labeled = cYes THEN
  8094.                          DLeft% = DRight% - (DRight% - DLeft%) / (1 + cTicSize
  8095.                  END IF
  8096.          END IF
  8097.  
  8098.          ' *** LEGEND
  8099.          ' Account for the legend if its on the right:
  8100.          IF GE.Legend.Legend = cYes AND GP.MSeries = cYes THEN
  8101.                  IF GE.Legend.Place = cRight THEN
  8102.                          A% = GE.Legend.LegendWindow.X1
  8103.                          DRight% = DRight% - ABS(GP.ChartWid - A%)
  8104.                  END IF
  8105.          END IF
  8106.  
  8107.          ' Now we have DLeft%, DRight% we can check if the labels fit on the
  8108.          ' X axis or if we need to put them on two rows:
  8109.          GP.XStagger = cFalse
  8110.          IF GP.XMode = cCategory AND GE.ChartType <> cPie THEN
  8111.                  clSetChartFont GE.XAxis.TicFont
  8112.                  TicInterval% = (DRight% - DLeft%) \ GP.NVals
  8113.                  IF clMaxStrLen%(Cat$(), 1, GP.NVals) + .5 * GFI.MaxWidth > Ti
  8114.                          GP.XStagger = cTrue
  8115.                  END IF
  8116.          END IF
  8117.  
  8118.          ' If we do have to stagger, check if there is enough space to the
  8119.          ' left and right for long categories.  Make adjustments as necessary:
  8120.          IF GP.XStagger THEN
  8121.                  LenLeft% = GetGTextLen%(Cat$(1)) + GFI.AvgWidth
  8122.                  LenRight% = GetGTextLen%(Cat$(GP.NVals)) + GFI.AvgWidth
  8123.                  SizeRight% = cTrue
  8124.                  SizeLeft% = cTrue
  8125.                  OldRight% = DRight%
  8126.                  OldLeft% = DLeft%
  8127.                  DO WHILE SizeRight% OR SizeLeft%
  8128.                          IF LenRight% - TicInterval% > 2 * (GP.ChartWid - DRig
  8129.                                  SizeRight% = cTrue
  8130.                          ELSE
  8131.                                  SizeRight% = cFalse
  8132.                          END IF
  8133.                          IF SizeRight% THEN
  8134.                                  TicInterval% = (2 * (GP.ChartWid - DLeft%) -
  8135.                                  IF LenRight% > 2 * TicInterval% THEN
  8136.                                          TicInterval% = (GP.ChartWid - DLeft%)
  8137.                                  END IF
  8138.                                  DRight% = DLeft% + GP.NVals * TicInterval%
  8139.                          END IF
  8140.                          IF LenLeft% - TicInterval% > 2 * DLeft% AND 2 * DLeft
  8141.                                  SizeLeft% = cTrue
  8142.                          ELSE
  8143.                                  SizeLeft% = cFalse
  8144.                          END IF
  8145.                          IF SizeLeft% THEN
  8146.                                  TicInterval% = (2 * DRight% - LenLeft%) \ (2
  8147.                                  IF LenLeft% > 2 * TicInterval% THEN
  8148.                                          TicInterval% = DRight% / (GP.NVals +
  8149.                                  END IF
  8150.                                  DLeft% = DRight% - GP.NVals * TicInterval%
  8151.                          END IF
  8152.  
  8153.                          ' Make sure we haven't gone too far on either side:
  8154.                          IF DRight% > OldRight% THEN
  8155.                                  DRight% = OldRight%
  8156.                          END IF
  8157.                          IF DLeft% < OldLeft% THEN
  8158.                                  DLeft% = OldLeft%
  8159.                          END IF
  8160.  
  8161.                          ' Check if there has been a change, if not, we are do
  8162.                          IF ABS(ChangeRight% - DRight%) + ABS(ChangeLeft% - DL
  8163.                                  EXIT DO
  8164.                          ELSE
  8165.                                  ChangeRight% = DRight%
  8166.                                  ChangeLeft% = DLeft%
  8167.                          END IF
  8168.                  LOOP
  8169.          END IF
  8170.  
  8171.          ' *** BOTTOM
  8172.          DBot% = XTitleLayout.TotalSize
  8173.  
  8174.          ' If axis is labeled (and not a pie chart), add row for tic
  8175.          ' labels + 1/2 row spacing:
  8176.          IF GE.XAxis.Labeled = cYes AND GE.ChartType <> cPie THEN
  8177.                  IF GP.XStagger = cTrue THEN
  8178.                          DBot% = DBot% + 3 * GFI.PixHeight
  8179.                  ELSE
  8180.                          DBot% = DBot% + 1.5 * GFI.PixHeight
  8181.                  END IF
  8182.          END IF
  8183.  
  8184.          ' Make the setting relative to the chart window:
  8185.          DBot% = GP.ChartHgt - 1 - DBot%
  8186.  
  8187.  
  8188.          ' *** XTIC MARKS
  8189.          ' Finally, adjust the window coordinates for tic marks (if it's not a
  8190.          ' pie chart):
  8191.          IF GE.ChartType <> cPie THEN
  8192.                  IF GE.XAxis.Labeled = cYes THEN
  8193.                          DBot% = DTop% + (DBot% - DTop%) / (1 + cTicSize)
  8194.                  END IF
  8195.  
  8196.          END IF
  8197.  
  8198.          ' *** LEGEND
  8199.          ' Account for the legend if its on the bottom:
  8200.          IF GE.Legend.Legend = cYes AND GP.MSeries = cYes THEN
  8201.                  IF GE.Legend.Place = cBottom THEN
  8202.                          A% = GE.Legend.LegendWindow.Y1
  8203.                          DBot% = DBot% - ABS(GP.ChartHgt - A%)
  8204.                  END IF
  8205.          END IF
  8206.  
  8207.          ' Install values in the DataWindow definition:
  8208.          GE.DataWindow.X1 = DLeft%
  8209.          GE.DataWindow.X2 = DRight%
  8210.          GE.DataWindow.Y1 = DTop%
  8211.          GE.DataWindow.Y2 = DBot%
  8212.  
  8213.          ' If the window is invalid then set error:
  8214.          IF DLeft% >= DRight% OR DTop% >= DBot% THEN
  8215.                  clSetError cBadDataWindow
  8216.          END IF
  8217.  
  8218.  END SUB
  8219.  
  8220.  '=== clSpaceTics - Calculates TicInterval
  8221.  '
  8222.  '  Arguments:
  8223.  '     None
  8224.  '
  8225.  '  Return Values:
  8226.  '     The TicInterval will be altered
  8227.  '
  8228.  '  Remarks:
  8229.  '     The TicInterval is the distance between tic marks in WORLD
  8230.  '     coordinates (i.e. the coordinates your data are in)
  8231.  '
  8232.  '=================================================================
  8233.  SUB clSpaceTics
  8234.  SHARED GE AS ChartEnvironment, GP AS GlobalParams
  8235.  SHARED GFI AS FontInfo
  8236.  
  8237.          ' X-Axis:
  8238.          ' Calculate the length of the axis and of the longest tic label.  The
  8239.          ' use that information to calculate the number of tics that will fit:
  8240.          clSetChartFont GE.XAxis.TicFont
  8241.          AxisLen% = GE.DataWindow.X2 - GE.DataWindow.X1 + 1
  8242.          TicWid% = GP.ValLenX + GFI.MaxWidth
  8243.          clSpaceTicsA GE.XAxis, GP.XMode, AxisLen%, TicWid%
  8244.  
  8245.          ' Y-Axis:
  8246.          ' Same procedure as above:
  8247.          clSetChartFont GE.YAxis.TicFont
  8248.          AxisLen% = GE.DataWindow.Y2 - GE.DataWindow.Y1 + 1
  8249.          TicWid% = 2 * GFI.Ascent
  8250.          clSpaceTicsA GE.YAxis, GP.YMode, AxisLen%, TicWid%
  8251.  
  8252.  END SUB
  8253.  
  8254.  '=== clSpaceTicsA - Figures out TicInterval for an axis
  8255.  '
  8256.  '  Arguments:
  8257.  '     Axis     -  An AxisType variable to space tics for
  8258.  '
  8259.  '     AxisMode%-  cCategory or cValue
  8260.  '
  8261.  '     AxisLen% -  Length of the axis in pixels
  8262.  '
  8263.  '  Return Values:
  8264.  '     The TicInterval value may be changed for an axis
  8265.  '
  8266.  '  Remarks:
  8267.  '     The TicInterval is the distance between tic marks in adjusted world
  8268.  '     coordinates (i.e. the coordinates your data are in scaled by
  8269.  '     ScaleFactor and adjusted by LogBase if it is a log axis).
  8270.  '
  8271.  '=================================================================
  8272.  SUB clSpaceTicsA (Axis AS AxisType, AxisMode%, AxisLen%, TicWid%)
  8273.  SHARED GP AS GlobalParams
  8274.  
  8275.          ' If this is a category axis the tic interval is 1
  8276.          ' divided by the number-of-categories:
  8277.          IF AxisMode% = cCategory THEN
  8278.                  Axis.TicInterval = 1 / GP.NVals
  8279.                  EXIT SUB
  8280.          END IF
  8281.  
  8282.          ' Otherwise, if we're supposed to scale this axis then the tic interv
  8283.          ' depends on how many will fit and some aesthetic considerations:
  8284.          IF Axis.AutoScale = cYes THEN
  8285.  
  8286.                  ' Figure which is bigger in absolute value between scale maxi
  8287.                  ' and minimum:
  8288.                  MaxRange = ABS(Axis.ScaleMax)
  8289.                  IF ABS(Axis.ScaleMin) > MaxRange THEN MaxRange = ABS(Axis.Sca
  8290.  
  8291.                  ' Calculate the maximum number of tic marks that will fit:
  8292.                  MaxTics% = INT(AxisLen% / TicWid%)
  8293.  
  8294.                  ' If the maximum number of tics is one or less set the tic
  8295.                  ' interval to the axis range and the number of tics to one:
  8296.                  IF MaxTics% <= 1 THEN
  8297.                          NumTics% = 1
  8298.                          TicInterval = Axis.ScaleMax - Axis.ScaleMin
  8299.  
  8300.                  ELSE
  8301.                          ' Guess that the tic interval is equal to 1/10th of t
  8302.                          ' of magnitude of the largest of the scale max or min
  8303.                          TicInterval = .1 * 10 ^ INT(LOG(MaxRange) / LOG(10!))
  8304.  
  8305.                          ' If this doesn't result in too many tic marks then O
  8306.                          ' multiply the tic interval by 2 and 5 alternatively
  8307.                          ' number of tic marks falls into the acceptable range
  8308.                          NextStep% = 2
  8309.                          ScaleRange = Axis.ScaleMax - Axis.ScaleMin
  8310.                          DO
  8311.                                  NumTics% = -INT(-ScaleRange / TicInterval)
  8312.                                  IF (NumTics% <= MaxTics%) THEN EXIT DO
  8313.                                  TicInterval = TicInterval * NextStep%
  8314.                                  NextStep% = 7 - NextStep%
  8315.                          LOOP UNTIL NumTics% <= MaxTics%
  8316.                  END IF
  8317.  
  8318.                  ' Set Axis.TicInterval and adjust scale maximum and minimum:
  8319.                  Axis.TicInterval = TicInterval
  8320.                  IF ABS(TicInterval) < 1 THEN
  8321.                          Axis.TicDecimals = -INT(-ABS(LOG(1.1 * TicInterval) /
  8322.                  END IF
  8323.  
  8324.                  Axis.ScaleMax = -INT(-Axis.ScaleMax / TicInterval) * TicInter
  8325.                  Axis.ScaleMin = INT(Axis.ScaleMin / TicInterval) * TicInterva
  8326.          END IF
  8327.  
  8328.  END SUB
  8329.  
  8330.  '=== clTitleXAxis - Draws titles on X axis (AxisTitle and ScaleTitle)
  8331.  '
  8332.  '  Arguments:
  8333.  '     Axis  -  AxisType variable describing axis
  8334.  '
  8335.  '     X1%   -  Left of DataWindow
  8336.  '
  8337.  '     X2%   -  Right of DataWindow
  8338.  '
  8339.  '     YBoundry%   -  Top boundry of title block
  8340.  '
  8341.  '=================================================================
  8342.  SUB clTitleXAxis (Axis AS AxisType, X1%, X2%, YBoundry%)
  8343.  SHARED GFI AS FontInfo
  8344.  SHARED XTitleLayout AS TitleLayout
  8345.  
  8346.          CH% = GFI.PixHeight
  8347.          CW% = GFI.MaxWidth
  8348.  
  8349.          ' Set position of first title:
  8350.          Y% = YBoundry% + XTitleLayout.Top
  8351.  
  8352.          ' Loop through the two titles (AxisTitle and ScaleTitle), printing
  8353.          ' them if they aren't blank:
  8354.          FOR i% = 1 TO 2
  8355.  
  8356.                  ' Get the test, color, and justification for the title to be
  8357.                  SELECT CASE i%
  8358.  
  8359.                          CASE 1:  ' AxisTitle
  8360.                                  Txt$ = Axis.AxisTitle.Title
  8361.                                  C% = Axis.AxisTitle.TitleColor
  8362.                                  J% = Axis.AxisTitle.Justify
  8363.                                  F% = Axis.AxisTitle.TitleFont
  8364.                                  Lead% = XTitleLayout.Middle
  8365.  
  8366.                          CASE 2:  ' ScaleTitle
  8367.                                  Txt$ = Axis.ScaleTitle.Title
  8368.                                  C% = Axis.ScaleTitle.TitleColor
  8369.                                  J% = Axis.ScaleTitle.Justify
  8370.                                  F% = Axis.ScaleTitle.TitleFont
  8371.                                  Lead% = XTitleLayout.Bottom
  8372.  
  8373.                  END SELECT
  8374.                  clSetChartFont F%
  8375.                  Txt$ = RTRIM$(Txt$)
  8376.                  TxtLen% = GetGTextLen(Txt$)
  8377.  
  8378.                  ' If the title isn't all blank:
  8379.                  IF TxtLen% <> 0 THEN
  8380.  
  8381.                          ' Set the title's color:
  8382.                          clSetCharColor C%
  8383.  
  8384.                          ' Calculate x position of title's first character dep
  8385.                          ' the justification flag:
  8386.                          SELECT CASE J%
  8387.                                  CASE cLeft:   X% = X1%
  8388.                                  CASE cCenter: X% = ((X1% + X2%) - TxtLen%) /
  8389.                                  CASE ELSE:    X% = X2% - TxtLen%
  8390.                          END SELECT
  8391.  
  8392.                          ' Write out the text:
  8393.                          clHPrint X%, Y%, Txt$
  8394.  
  8395.                          ' Move down to the next title position:
  8396.                          Y% = Y% + GFI.PixHeight + XTitleLayout.Middle
  8397.  
  8398.                  END IF
  8399.  
  8400.          NEXT i%
  8401.  
  8402.  END SUB
  8403.  
  8404.  '=== clTitleYAxis - Draws titles on Y axis (AxisTitle and ScaleTitle)
  8405.  '
  8406.  '  Arguments:
  8407.  '     Axis  -  AxisType variable describing axis
  8408.  '
  8409.  '     Y1%   -  Top of DataWindow
  8410.  '
  8411.  '     Y2%   -  Bottom of DataWindow
  8412.  '
  8413.  '  Return Values:
  8414.  '
  8415.  '=================================================================
  8416.  SUB clTitleYAxis (Axis AS AxisType, Y1%, Y2%) STATIC
  8417.  SHARED GFI AS FontInfo
  8418.  SHARED YTitleLayout AS TitleLayout
  8419.  
  8420.  
  8421.          ' Set position for first title:
  8422.          X% = YTitleLayout.Top
  8423.  
  8424.          ' Loop through the two titles (AxisTitle and ScaleTitle), printing
  8425.          ' them if they aren't blank:
  8426.          FOR i% = 1 TO 2
  8427.  
  8428.                  ' Get the test, color, and justification for the title to be
  8429.                  SELECT CASE i%
  8430.  
  8431.                          CASE 1:  ' AxisTitle
  8432.                                  Txt$ = Axis.AxisTitle.Title
  8433.                                  C% = Axis.AxisTitle.TitleColor
  8434.                                  J% = Axis.AxisTitle.Justify
  8435.                                  F% = Axis.AxisTitle.TitleFont
  8436.                                  Lead% = YTitleLayout.TitleOne + YTitleLayout.
  8437.  
  8438.                          CASE 2:  ' ScaleTitle
  8439.                                  Txt$ = Axis.ScaleTitle.Title
  8440.                                  C% = Axis.ScaleTitle.TitleColor
  8441.                                  J% = Axis.ScaleTitle.Justify
  8442.                                  F% = Axis.ScaleTitle.TitleFont
  8443.                                  Lead% = 0
  8444.  
  8445.                  END SELECT
  8446.                  clSetChartFont F%
  8447.                  Txt$ = RTRIM$(Txt$)
  8448.                  TxtLen% = GetGTextLen(Txt$)
  8449.  
  8450.                  IF TxtLen% <> 0 THEN
  8451.  
  8452.                          ' Set title's color:
  8453.                          clSetCharColor C%
  8454.  
  8455.                          ' Calculate y position of title's first character dep
  8456.                          ' the justification flag:
  8457.                          SELECT CASE J%
  8458.                                  CASE cLeft:   Y% = Y2%
  8459.                                  CASE cCenter: Y% = ((Y1% + Y2%) + TxtLen%) /
  8460.                                  CASE ELSE:    Y% = Y1% + (TxtLen% - 1)
  8461.                          END SELECT
  8462.  
  8463.                          ' Write out the text:
  8464.                          clVPrint X%, Y%, Txt$
  8465.  
  8466.                          ' Move to next title position:
  8467.                          X% = X% + Lead%
  8468.  
  8469.                  END IF
  8470.  
  8471.          NEXT i%
  8472.  
  8473.  END SUB
  8474.  
  8475.  '=== clUnFlagSystem - Sets GP.SysFlag to cNo
  8476.  '
  8477.  '  Arguments:
  8478.  '     None
  8479.  '
  8480.  '  Return Values:
  8481.  '     Alters the value of GP.SysFlag
  8482.  '
  8483.  '=================================================================
  8484.  SUB clUnFlagSystem
  8485.  SHARED GP AS GlobalParams
  8486.  
  8487.          GP.SysFlag = cNo
  8488.  
  8489.  END SUB
  8490.  
  8491.  '=== clVal2Str$ - Converts a single precision value to a string
  8492.  '
  8493.  '  Arguments:
  8494.  '     X        -  The value to convert
  8495.  '
  8496.  '     Places%  -  The number of places after the decimal to produce
  8497.  '
  8498.  '     Format%  -  1 For normal, other than 1 for exponential
  8499.  '
  8500.  '  Return Values:
  8501.  '     Returns a string representation of the input number
  8502.  '
  8503.  '=================================================================
  8504.  FUNCTION clVal2Str$ (X, Places%, Format%)
  8505.  
  8506.          ' Make a local copy of the value:
  8507.          XX = ABS(X)
  8508.  
  8509.          ' Force format to exponential if that is specified or number is
  8510.          ' bigger than a long integer will hold (2^31-1):
  8511.          IF Format% <> cNormFormat OR XX >= 2 ^ 31 THEN
  8512.  
  8513.                  ' For exponential format calculate the exponent that will mak
  8514.                  ' one decimal to left of decimal place.  This is done by trun
  8515.                  ' the log (base 10) of XX:
  8516.                  IF XX = 0 THEN ExpX = 0 ELSE ExpX = INT(LOG(XX) / LOG(10))
  8517.                  XX = XX / (10 ^ ExpX)
  8518.  
  8519.                  ' If no decimals are specified then a number of 9.5x will be
  8520.                  ' rounded up to 10 leaving two places to left of decimal so c
  8521.                  ' for that and if that occurs divide number by 10 and add 1 t
  8522.                  IF Places% <= 0 AND CLNG(XX) > 9 THEN
  8523.                          XX = XX / 10
  8524.                          ExpX = ExpX + 1
  8525.                  END IF
  8526.  
  8527.          END IF
  8528.  
  8529.          ' If no decimal places are specified then generate a rounded integer:
  8530.          IF Places% <= 0 THEN
  8531.                  ValStr$ = LTRIM$(STR$(CLNG(XX)))
  8532.  
  8533.          ' If decimal places are called for, round number to requisite number
  8534.          ' decimals and generate string:
  8535.          ELSE
  8536.  
  8537.                  ' Limit places after decimal to six:
  8538.                  DP% = Places%
  8539.                  IF DP% > 6 THEN DP% = 6
  8540.                  RF% = 10 ^ DP%
  8541.  
  8542.                  ' Figure out integer portion:
  8543.                  IntX = FIX(XX)
  8544.  
  8545.                  ' Round the fractional part to correct number of decimals.  I
  8546.                  ' the fraction carries to the 1's place in the rounding
  8547.                  ' adjust IntX by adding 1:
  8548.                  FracX = CLNG((1 + XX - IntX) * RF%)
  8549.                  IF FracX >= 2 * RF% THEN
  8550.                          IntX = IntX + 1
  8551.                  END IF
  8552.  
  8553.                  'Finally, generate the output string:
  8554.                  ValStr$ = LTRIM$(STR$(IntX)) + "." + MID$(STR$(FracX), 3)
  8555.  
  8556.          END IF
  8557.  
  8558.          ' Add exponent ending if format is exponent:
  8559.          IF Format% <> cNormFormat OR ABS(X) > 2 ^ 31 THEN
  8560.                  ValStr$ = ValStr$ + "E"
  8561.                  IF ExpX >= 0 THEN ValStr$ = ValStr$ + "+"
  8562.                  ValStr$ = ValStr$ + LTRIM$(STR$(ExpX))
  8563.          END IF
  8564.  
  8565.          ' Add minus sign if appropriate:
  8566.          IF X < 0 AND VAL(ValStr$) <> 0 THEN ValStr$ = "-" + ValStr$
  8567.          clVal2Str$ = ValStr$
  8568.  
  8569.  END FUNCTION
  8570.  
  8571.  '=== clVPrint - Prints text vertically on the screen
  8572.  '
  8573.  '  Arguments:
  8574.  '     X     -  X position of lower left of first char (in absolute screen
  8575.  '              coordinates)
  8576.  '
  8577.  '     Y     -  Y position of lower left of first char (in absolute screen
  8578.  '              coordinates)
  8579.  '
  8580.  '     Txt$  -  Text to print
  8581.  '
  8582.  '  Return Values:
  8583.  '     None
  8584.  '
  8585.  '=================================================================
  8586.  SUB clVPrint (X%, Y%, Txt$)
  8587.  
  8588.          ' Map the input coordinates relative to the current viewport:
  8589.          X = PMAP(X%, 2)
  8590.          Y = PMAP(Y%, 3)
  8591.  
  8592.          ' Print text out vertically:
  8593.          SetGTextDir 1
  8594.          TextLen% = OutGText(X, Y, Txt$)
  8595.          SetGTextDir 0
  8596.  
  8597.  END SUB
  8598.  
  8599.  '=== DefaultChart - Sets up the ChartEnvironment variable to generate a
  8600.  '                   default chart of the type and style specified
  8601.  '
  8602.  '  Arguments:
  8603.  '     Env        - A ChartEnvironment variable
  8604.  '
  8605.  '     ChartType  - The chart type desired: 1=Bar, 2=Column, 3=Line,
  8606.  '                  4=Scatter, 5=Pie
  8607.  '
  8608.  '     ChartStyle - The chart style (depends on type, see README file)
  8609.  '
  8610.  '
  8611.  '  Return Values:
  8612.  '     Elements of Env variable are set to default values
  8613.  '
  8614.  '  Remarks:
  8615.  '     This subprogram should be called to initialize the ChartEnvironment
  8616.  '     variable before a charting routine is called.
  8617.  '
  8618.  '=================================================================
  8619.  SUB DefaultChart (Env AS ChartEnvironment, ChartType AS INTEGER, ChartStyle A
  8620.  
  8621.  SHARED DTitle AS TitleType, DWindow AS RegionType
  8622.  SHARED DAxis AS AxisType, DLegend AS LegendType
  8623.  
  8624.          ' Clear any previous chart errors:
  8625.          clClearError
  8626.  
  8627.          ' Check initialization:
  8628.          clChkInit
  8629.  
  8630.    ' Put type in environment:
  8631.          IF ChartType < 1 OR ChartType > 5 THEN
  8632.                  clSetError cBadType
  8633.                  EXIT SUB
  8634.          END IF
  8635.          Env.ChartType = ChartType
  8636.  
  8637.          ' Put chart style in environment:
  8638.          IF ChartStyle < 1 OR ChartStyle > 2 THEN
  8639.                  clSetError cBadStyle
  8640.                  ChartStyle = 1
  8641.          END IF
  8642.          Env.ChartStyle = ChartStyle
  8643.  
  8644.          ' Set elements of chart to default:
  8645.          Env.DataFont = 1
  8646.  
  8647.          Env.MainTitle = DTitle
  8648.          Env.SubTitle = DTitle
  8649.  
  8650.          Env.ChartWindow = DWindow           ' Chart window is default window
  8651.          Env.ChartWindow.Border = cYes       ' with a border.
  8652.  
  8653.          Env.DataWindow = DWindow
  8654.  
  8655.          Env.XAxis = DAxis
  8656.          Env.YAxis = DAxis
  8657.  
  8658.          Env.Legend = DLegend
  8659.  
  8660.  END SUB
  8661.  
  8662.  '=== GetPaletteDef - Changes an entry in the internal palette
  8663.  '
  8664.  '  Arguments:
  8665.  '     C%()     -  Color palette array
  8666.  '
  8667.  '     S%()     -  Style palette array
  8668.  '
  8669.  '     P$()     -  Pattern palette array
  8670.  '
  8671.  '     Char%()  -  Plot character palette array
  8672.  '
  8673.  '     B%()     -  Border style palette array
  8674.  '
  8675.  '  Return Values:
  8676.  '     Chart error may be set
  8677.  '
  8678.  '=================================================================
  8679.  SUB GetPaletteDef (C() AS INTEGER, s() AS INTEGER, P$(), Char() AS INTEGER, B
  8680.  SHARED GP AS GlobalParams
  8681.  SHARED PaletteC%(), PaletteS%(), PaletteP$(), PaletteCh%(), PaletteB%()
  8682.  
  8683.          ' Reset any outstanding errors:
  8684.          clClearError
  8685.  
  8686.          ' Make sure palettes have been initialized:
  8687.          IF NOT GP.PaletteSet THEN
  8688.                  clSetError cPalettesNotSet
  8689.                  EXIT SUB
  8690.          END IF
  8691.  
  8692.          ' Make sure the user's palettes are the correct size:
  8693.          clChkPalettes C(), s(), P$(), Char(), B()
  8694.          IF (ChartErr <> 0) THEN EXIT SUB
  8695.  
  8696.          ' Replace the palette values with input variables (making sure that
  8697.          ' the color and character numbers are in range):
  8698.          FOR N% = 0 TO cPalLen
  8699.                  C(N%) = PaletteC%(N%)
  8700.                  s(N%) = PaletteS%(N%)
  8701.                  P$(N%) = PaletteP$(N%)
  8702.                  Char(N%) = PaletteCh%(N%)
  8703.                  B(N%) = PaletteB%(N%)
  8704.          NEXT N%
  8705.  
  8706.  END SUB
  8707.  
  8708.  '=== GetPattern - Returns a pattern from among 3 pattern palettes
  8709.  '
  8710.  '  Arguments:
  8711.  '     Bits%       -  The number of bits per pixel for the pattern
  8712.  '
  8713.  '     PatternNum% -  The pattern number to return
  8714.  '
  8715.  '  Return Values:
  8716.  '     Returns a pattern tile from the list below.
  8717.  '
  8718.  '  Remarks:
  8719.  '     Below are three pattern sets.  There is a set of patterns for one, two
  8720.  '     and eight bit-per-pixel screens.
  8721.  '
  8722.  '=================================================================
  8723.  FUNCTION GetPattern$ (Bits%, PatternNum%)
  8724.  
  8725.          SELECT CASE Bits%
  8726.  
  8727.                  ' One bit-per-pixel patterns:
  8728.                  CASE 1:
  8729.                          SELECT CASE PatternNum%
  8730.                                  CASE 1: P$ = CHR$(&HFF)
  8731.                                  CASE 2: P$ = CHR$(&H55) + CHR$(&HAA)
  8732.                                  CASE 3: P$ = CHR$(&H33) + CHR$(&HCC)
  8733.                                  CASE 4: P$ = CHR$(&H0) + CHR$(&HE7)
  8734.                                  CASE 5: P$ = CHR$(&H7F) + CHR$(&HBF) + CHR$(&
  8735.                                  CASE 6: P$ = CHR$(&H7E) + CHR$(&HBD) + CHR$(&
  8736.                                  CASE 7: P$ = CHR$(&HFE) + CHR$(&HFD) + CHR$(&
  8737.                                  CASE 8: P$ = CHR$(&H33) + CHR$(&HCC) + CHR$(&
  8738.                                  CASE 9: P$ = CHR$(&H0) + CHR$(&HFD) + CHR$(&H
  8739.                                  CASE 10: P$ = CHR$(&HF) + CHR$(&H87) + CHR$(&
  8740.                                  CASE 11: P$ = CHR$(&HA8) + CHR$(&H51) + CHR$(
  8741.                                  CASE 12: P$ = CHR$(&HAA) + CHR$(&H55) + CHR$(
  8742.                                  CASE 13: P$ = CHR$(&H2A) + CHR$(&H15) + CHR$(
  8743.                                  CASE 14: P$ = CHR$(&H88) + CHR$(&H0) + CHR$(&
  8744.                                  CASE 15: P$ = CHR$(&HFF) + CHR$(&H0) + CHR$(&
  8745.                          END SELECT
  8746.  
  8747.                  ' Two bit-per-pixel patterns:
  8748.                  CASE 2:
  8749.                          SELECT CASE PatternNum%
  8750.                                  CASE 1: P$ = CHR$(&HFF)
  8751.                                  CASE 2: P$ = CHR$(&HCC) + CHR$(&H33)
  8752.                                  CASE 3: P$ = CHR$(&HF0) + CHR$(&H3C) + CHR$(&
  8753.                                  CASE 4: P$ = CHR$(&HF0) + CHR$(&HF)
  8754.                                  CASE 5: P$ = CHR$(&H3) + CHR$(&HC) + CHR$(&H3
  8755.                                  CASE 6: P$ = CHR$(&HFF) + CHR$(&HC)
  8756.                                  CASE 7: P$ = CHR$(&HF0) + CHR$(&HF0) + CHR$(&
  8757.                                  CASE 8: P$ = CHR$(&HFF) + CHR$(&HC) + CHR$(&H
  8758.                                  CASE 9: P$ = CHR$(&HC0) + CHR$(&H30) + CHR$(&
  8759.                                  CASE 10: P$ = CHR$(&HC0) + CHR$(&HC)
  8760.                                  CASE 11: P$ = CHR$(&HCC) + CHR$(&HCC) + CHR$(
  8761.                                  CASE 12: P$ = CHR$(&HCC) + CHR$(&HCC) + CHR$(
  8762.                                  CASE 13: P$ = CHR$(&HFF) + CHR$(&H33) + CHR$(
  8763.                                  CASE 14: P$ = CHR$(&HFF) + CHR$(&H0)
  8764.                                  CASE 15: P$ = CHR$(&HCC) + CHR$(&H30) + CHR$(
  8765.                          END SELECT
  8766.  
  8767.                  ' Eight bit-per-pixel patterns:
  8768.                  CASE 8:
  8769.                          P$ = CHR$(&HFF)
  8770.  
  8771.          END SELECT
  8772.  
  8773.          ' Return the pattern as the value of the function:
  8774.          GetPattern$ = P$
  8775.  
  8776.  END FUNCTION
  8777.  
  8778.  '=== LabelChartH - Prints horizontal text on a chart
  8779.  '
  8780.  '  Arguments:
  8781.  '     Env        - A ChartEnvironment variable
  8782.  '
  8783.  '     X          - Horizontal position of text relative to the left of
  8784.  '                  the Chart window (in pixels)
  8785.  '
  8786.  '     Y          - Vertical position of text relative to the top of
  8787.  '                  the Chart window (in pixels)
  8788.  '
  8789.  '     Font%      - Font number to use for the text
  8790.  '
  8791.  '     TxtColor   - Color number (in internal color palette) for text
  8792.  '
  8793.  '     TxtString$ - String variable containing text to print
  8794.  '
  8795.  '  Return Values:
  8796.  '     None
  8797.  '
  8798.  '=================================================================
  8799.  SUB LabelChartH (Env AS ChartEnvironment, X AS INTEGER, Y AS INTEGER, Font AS
  8800.  
  8801.          ' Reset any outstanding errors:
  8802.          clClearError
  8803.  
  8804.          ' Check initialization and fonts:
  8805.          clChkInit
  8806.          clChkFonts
  8807.          IF ChartErr >= 100 THEN EXIT SUB
  8808.  
  8809.          ' Select ChartWindow as reference viewport:
  8810.          clSelectChartWindow
  8811.  
  8812.          ' Select font and set color:
  8813.          SelectFont Font
  8814.          clSetCharColor TxtColor
  8815.  
  8816.          ' Call internal print routine to print text:
  8817.          clHPrint X, Y, TxtString$
  8818.  
  8819.  END SUB
  8820.  
  8821.  '=== LabelChartV - Prints vertical text on a chart
  8822.  '
  8823.  '  Arguments:
  8824.  '     Env        - A ChartEnvironment variable
  8825.  '
  8826.  '     X          - Horizontal position of text relative to the left of
  8827.  '                  the Chart window (in pixels)
  8828.  '
  8829.  '     Y          - Vertical position of text relative to the top of
  8830.  '                  the Chart window (in pixels)
  8831.  '
  8832.  '     Font%      - Font number to use for the text
  8833.  '
  8834.  '     TxtColor   - Color number (in internal color palette) for text
  8835.  '
  8836.  '     TxtString$ - String variable containing text to print
  8837.  '
  8838.  '  Return Values:
  8839.  '     None
  8840.  '
  8841.  '=================================================================
  8842.  SUB LabelChartV (Env AS ChartEnvironment, X AS INTEGER, Y AS INTEGER, Font AS
  8843.  
  8844.          ' Reset any outstanding errors:
  8845.          clClearError
  8846.  
  8847.          ' Check initialization and fonts:
  8848.          clChkInit
  8849.          clChkFonts
  8850.          IF ChartErr >= 100 THEN EXIT SUB
  8851.  
  8852.          ' Select ChartWindow as reference viewport:
  8853.          clSelectChartWindow
  8854.  
  8855.          ' Select font and set color:
  8856.          SelectFont Font%
  8857.          clSetCharColor TxtColor
  8858.  
  8859.          ' Call internal print routine to print text:
  8860.          clVPrint X, Y, TxtString$
  8861.  
  8862.  END SUB
  8863.  
  8864.  '=== MakeChartPattern$ - Makes a pattern given reference pattern and
  8865.  '                        foreground and background colors
  8866.  '
  8867.  '  Arguments:
  8868.  '     RefPattern$ -  Reference pattern
  8869.  '
  8870.  '     FG%         -  Foreground color
  8871.  '
  8872.  '     BG%         -  Background color
  8873.  '
  8874.  '  Return Values:
  8875.  '     Returns a pattern in standard PAINT format
  8876.  '     Sets error cBadScreen if ChartScreen hasn't been called
  8877.  '
  8878.  '=================================================================
  8879.  FUNCTION MakeChartPattern$ (RefPattern$, FG AS INTEGER, BG AS INTEGER)
  8880.  SHARED GP AS GlobalParams
  8881.  
  8882.          ' Reset any outstanding errors:
  8883.          clClearError
  8884.  
  8885.          ' Check initialization:
  8886.          clChkInit
  8887.          IF ChartErr >= 100 THEN EXIT FUNCTION
  8888.          IF NOT GP.PaletteSet THEN
  8889.                  clSetError cBadScreen
  8890.                  EXIT FUNCTION
  8891.          END IF
  8892.  
  8893.          FGColor% = clMap2Attrib%(FG%)
  8894.          BGColor% = clMap2Attrib%(BG%)
  8895.  
  8896.          ' Screens 1, 2, 11 and 13 are 1 bit plane modes and require one metho
  8897.          ' of generating pattern tiles.  The other modes supported are multipl
  8898.          ' bit plane modes and require another method of generating pattern
  8899.          ' tiles.  Select the appropriate method for this screen mode:
  8900.          SELECT CASE GP.PaletteScrn
  8901.  
  8902.                  ' One bit plane modes:
  8903.                  CASE 1, 2, 11, 13: SinglePlane% = cTrue
  8904.                  CASE ELSE: SinglePlane% = cFalse
  8905.  
  8906.          END SELECT
  8907.  
  8908.          ' Do foreground part of pattern:
  8909.          IF SinglePlane% THEN
  8910.                          FGPattern$ = clBuildBitP$(GP.PaletteBits, FGColor%, R
  8911.          ELSE
  8912.                          FGPattern$ = clBuildPlaneP$(GP.PaletteBits, FGColor%,
  8913.          END IF
  8914.  
  8915.          ' Do background part of pattern (if background color is black then
  8916.          ' the pattern is just the foreground pattern):
  8917.          IF BGColor% = 0 THEN
  8918.                  Pattern$ = FGPattern$
  8919.  
  8920.          ELSE
  8921.                  ' Background reference pattern is inverted foreground pattern
  8922.                  BGPattern$ = ""
  8923.                  FOR i% = 1 TO LEN(RefPattern$)
  8924.                          BGPattern$ = BGPattern$ + CHR$(ASC(MID$(RefPattern$,
  8925.                  NEXT i%
  8926.  
  8927.                  ' Build the corresponding PAINT style pattern:
  8928.                  IF SinglePlane% THEN
  8929.                                  BGPattern$ = clBuildBitP$(GP.PaletteBits, BGC
  8930.                  ELSE
  8931.                                  BGPattern$ = clBuildPlaneP$(GP.PaletteBits, B
  8932.                  END IF
  8933.  
  8934.                  ' Put foreground and background patterns back together:
  8935.                  Pattern$ = ""
  8936.                  FOR i% = 1 TO LEN(FGPattern$)
  8937.                          Pattern$ = Pattern$ + CHR$(ASC(MID$(FGPattern$, i%, 1
  8938.                  NEXT i%
  8939.  
  8940.          END IF
  8941.  
  8942.          MakeChartPattern$ = Pattern$
  8943.  
  8944.  END FUNCTION
  8945.  
  8946.  '=== ResetPaletteDef - Resets charting palettes for last screen
  8947.  '                      mode set with ChartScreen.
  8948.  '
  8949.  '=================================================================
  8950.  SUB ResetPaletteDef
  8951.  SHARED GP AS GlobalParams
  8952.  
  8953.          ' Clear outstanding errors:
  8954.          clClearError
  8955.  
  8956.          ' Check initialization:
  8957.          clChkInit
  8958.  
  8959.          ' Make sure that ChartScreen has been called at least once:
  8960.          IF NOT GP.PaletteSet THEN
  8961.                  clSetError cBadScreen
  8962.                  EXIT SUB
  8963.          END IF
  8964.  
  8965.          ' Now rebuild the palette with the last set screen mode:
  8966.          clBuildPalette GP.PaletteScrn, GP.PaletteBits
  8967.  
  8968.  END SUB
  8969.  
  8970.  '=== SetPaletteDef - Changes an entry in the internal palette
  8971.  '
  8972.  '  Arguments:
  8973.  '     C%()     -  Color palette array
  8974.  '
  8975.  '     S%()     -  Style palette array
  8976.  '
  8977.  '     P$()     -  Pattern palette array
  8978.  '
  8979.  '     Char%()  -  Plot character palette array
  8980.  '
  8981.  '     B%()     -  Border style palette array
  8982.  '
  8983.  '  Return Values:
  8984.  '     Internal chart palettes may be modified or ChartErr set
  8985.  '
  8986.  '=================================================================
  8987.  SUB SetPaletteDef (C() AS INTEGER, s() AS INTEGER, P$(), Char() AS INTEGER, B
  8988.  SHARED PaletteC%(), PaletteS%(), PaletteP$(), PaletteCh%(), PaletteB%()
  8989.  
  8990.          ' Reset any outstanding errors and check that palettes are dimesioned
  8991.          ' correctly:
  8992.          clClearError
  8993.          clChkPalettes C(), s(), P$(), Char(), B()
  8994.          IF (ChartErr <> 0) THEN EXIT SUB
  8995.  
  8996.          ' Check initialization:
  8997.          clChkInit
  8998.  
  8999.          ' Replace the palette values with input variables (making sure that
  9000.          ' the color and character numbers are in range):
  9001.          FOR N% = 0 TO cPalLen
  9002.                  PaletteC%(N%) = clMap2Attrib%(C%(N%))
  9003.                  PaletteS%(N%) = s(N%)
  9004.                  PaletteP$(N%) = P$(N%)
  9005.                  PaletteCh%(N%) = ABS(Char(N%)) MOD (cMaxChars + 1)
  9006.                  PaletteB%(N%) = B(N%)
  9007.          NEXT N%
  9008.  
  9009.  END SUB
  9010.  
  9011.  
  9012.  
  9013.  CHRTDEM1.BAS
  9014.  CD-ROM Disc Path:   \SAMPCODE\BASIC\CHRTDEM1.BAS
  9015.  
  9016.  '       CHRTDEM1.BAS - second module of the CHRTB demonstration program.
  9017.  '
  9018.  '               Copyright (C) 1989, Microsoft Corporation
  9019.  '
  9020.  '   Main module - CHRTDEMO.BAS
  9021.  '   Include files - CHRTDEMO.BI
  9022.  '
  9023.  '$INCLUDE: 'chrtdemo.bi'
  9024.  
  9025.  'local subs
  9026.  DECLARE SUB ChangeStyle ()
  9027.  
  9028.  DEFINT A-Z
  9029.  '
  9030.  ' Sub Name: ChangeAxis
  9031.  '
  9032.  ' Description: Allows user to view and change attributes of either
  9033.  '              chart axis.
  9034.  '
  9035.  ' Arguments: title$ - window title
  9036.  '            axis - X or Y axis variable
  9037.  '
  9038.  SUB ChangeAxis (title$, axis AS AxisType)
  9039.  
  9040.      DIM colorBox AS ListBox
  9041.      DIM styleBox AS ListBox
  9042.      DIM fontBox AS ListBox
  9043.  
  9044.      ' set up color list box
  9045.      colorBox.scrollButton = 2
  9046.      colorBox.areaButton = 3
  9047.      colorBox.listLen = numColors
  9048.      colorBox.topRow = 3
  9049.      colorBox.botRow = 16
  9050.      colorBox.leftCol = 4
  9051.      colorBox.rightCol = 18
  9052.      colorBox.listPos = axis.AxisColor + 1
  9053.  
  9054.      ' set up border style list box
  9055.      styleBox.scrollButton = 5
  9056.      styleBox.areaButton = 6
  9057.      styleBox.listLen = MAXSTYLES
  9058.      styleBox.topRow = 5
  9059.      styleBox.botRow = 16
  9060.      styleBox.leftCol = 24
  9061.      styleBox.rightCol = 40
  9062.      styleBox.listPos = axis.GridStyle
  9063.  
  9064.      ' set up font list box
  9065.      fontBox.scrollButton = 8
  9066.      fontBox.areaButton = 9
  9067.      fontBox.listLen = numFonts
  9068.      fontBox.topRow = 5
  9069.      fontBox.botRow = 9
  9070.      fontBox.leftCol = 46
  9071.      fontBox.rightCol = 65
  9072.      fontBox.listPos = axis.TicFont
  9073.  
  9074.      ' open window for display
  9075.      winRow = 4
  9076.      winCol = 6
  9077.      WindowOpen 1, winRow, winCol, 22, 73, 0, 7, 0, 7, 15, FALSE, FALSE, FALSE
  9078.      WindowBox 1, 2, 17, 20
  9079.      WindowLocate 2, 4
  9080.      WindowPrint 2, "Axis Color:"
  9081.      WindowBox 1, 22, 17, 42
  9082.      WindowLocate 4, 24
  9083.      WindowPrint 2, "Grid Style:"
  9084.      WindowBox 1, 44, 17, 67
  9085.      WindowLocate 4, 46
  9086.      WindowPrint 2, "Label Font:"
  9087.      WindowLocate 10, 46
  9088.      WindowPrint 2, "Range Type:"
  9089.      WindowBox 11, 46, 16, 65
  9090.      WindowLocate 14, 48
  9091.      WindowPrint 2, "Log Base:"
  9092.      WindowBox 13, 57, 15, 63
  9093.      WindowLine 18
  9094.  
  9095.      ' create list boxes
  9096.      CreateListBox colors$(), colorBox, 0
  9097.      CreateListBox styles$(), styleBox, 0
  9098.      CreateListBox fonts$(), fontBox, 0
  9099.  
  9100.      ' open control buttons
  9101.      ButtonOpen 4, 1, "Display Grid", 2, 24, 0, 0, 2
  9102.      ButtonOpen 7, 1, "Display Labels", 2, 46, 0, 0, 2
  9103.      ButtonOpen 10, 1, "Lin", 12, 48, 0, 0, 3
  9104.      ButtonOpen 11, 1, "Log", 12, 57, 0, 0, 3
  9105.      ButtonOpen 12, 2, "OK ", 19, 10, 0, 0, 1
  9106.      ButtonOpen 13, 1, "Cancel ", 19, 26, 0, 0, 1
  9107.      ButtonOpen 14, 1, "Axis Title ", 19, 46, 0, 0, 1
  9108.  
  9109.      ' edit field for log base
  9110.      EditFieldOpen 1, LTRIM$(STR$(axis.LogBase)), 14, 58, 0, 7, 5, 20
  9111.  
  9112.  
  9113.      currButton = 3                                      ' start with cursor o
  9114.      currEditField = 0
  9115.  
  9116.      optionButton = axis.RangeType + 9                   ' set proper state fo
  9117.      ButtonToggle optionButton
  9118.      IF axis.Labeled THEN ButtonToggle 7
  9119.      IF axis.Grid THEN ButtonToggle 4
  9120.  
  9121.      pushButton = 12                                     ' active command butt
  9122.  
  9123.      ' window control loop
  9124.      finished = FALSE
  9125.      WHILE NOT finished
  9126.          WindowDo currButton, currEditField              ' wait for event
  9127.          SELECT CASE Dialog(0)
  9128.              CASE 1                                      ' button pressed
  9129.                  currButton = Dialog(1)
  9130.                  SELECT CASE currButton
  9131.                      CASE 4, 7
  9132.                          ButtonToggle currButton
  9133.                          currEditField = 0
  9134.                      CASE 10, 11
  9135.                          ButtonToggle optionButton
  9136.                          optionButton = currButton
  9137.                          ButtonToggle optionButton
  9138.                          currEditField = 0
  9139.                      CASE 2, 3
  9140.                          currEditField = 0
  9141.                          ScrollList colors$(), colorBox, currButton, 1, 0, win
  9142.                          currButton = 3
  9143.                      CASE 5, 6
  9144.                          currEditField = 0
  9145.                          ScrollList styles$(), styleBox, currButton, 1, 0, win
  9146.                          currButton = 6
  9147.                      CASE 8, 9
  9148.                          currEditField = 0
  9149.                          ScrollList fonts$(), fontBox, currButton, 1, 0, winRo
  9150.                          currButton = 9
  9151.                      CASE 12, 13
  9152.                          pushButton = currButton
  9153.                          finished = TRUE
  9154.                      CASE 14
  9155.                          currEditField = 0
  9156.                          ButtonSetState pushButton, 1
  9157.                          ButtonSetState currButton, 2
  9158.                          pushButton = currButton
  9159.                          ChangeTitle 2, title$ + " Title", axis.AxisTitle, 6,
  9160.                  END SELECT
  9161.              CASE 2                                      ' edit field
  9162.                  currEditField = 1
  9163.                  currButton = 0
  9164.              CASE 6                                      ' enter
  9165.                  SELECT CASE pushButton
  9166.                      CASE 12, 13: finished = TRUE
  9167.                      CASE 14: ChangeTitle 2, title$ + " Title", axis.AxisTitle
  9168.                  END SELECT
  9169.                  currButton = pushButton
  9170.              CASE 7                                      ' tab
  9171.                  SELECT CASE currButton
  9172.                      CASE 0:
  9173.                          currEditField = 0
  9174.                          currButton = 12
  9175.                          ButtonSetState pushButton, 1
  9176.                          ButtonSetState currButton, 2
  9177.                          pushButton = currButton
  9178.                      CASE 2, 3: currButton = 4
  9179.                      CASE 4: currButton = 6
  9180.                      CASE 5, 6: currButton = 7
  9181.                      CASE 7: currButton = 9
  9182.                      CASE 8, 9: currButton = optionButton
  9183.                      CASE 10, 11:
  9184.                          currButton = 0
  9185.                          currEditField = 1
  9186.                      CASE 12, 13:
  9187.                          currButton = currButton + 1
  9188.                          ButtonSetState pushButton, 1
  9189.                          ButtonSetState currButton, 2
  9190.                          pushButton = currButton
  9191.                      CASE 14:
  9192.                          ButtonSetState currButton, 1
  9193.                          pushButton = 12
  9194.                          ButtonSetState pushButton, 2
  9195.                          currButton = 3
  9196.                  END SELECT
  9197.              CASE 8                                      ' back tab
  9198.                  SELECT CASE currButton
  9199.                      CASE 0:
  9200.                          currEditField = 0
  9201.                          currButton = optionButton
  9202.                      CASE 2, 3:
  9203.                          currButton = 14
  9204.                          ButtonSetState pushButton, 1
  9205.                          ButtonSetState currButton, 2
  9206.                          pushButton = currButton
  9207.                      CASE 4: currButton = 3
  9208.                      CASE 5, 6: currButton = 4
  9209.                      CASE 7: currButton = 6
  9210.                      CASE 8, 9: currButton = 7
  9211.                      CASE 10, 11: currButton = 9
  9212.                      CASE 12:
  9213.                          currButton = 0
  9214.                          currEditField = 1
  9215.                      CASE 13, 14:
  9216.                          currButton = currButton - 1
  9217.                          ButtonSetState pushButton, 1
  9218.                          ButtonSetState currButton, 2
  9219.                          pushButton = currButton
  9220.                  END SELECT
  9221.              CASE 9                                      ' escape
  9222.                  pushButton = 13
  9223.                  finished = TRUE
  9224.              CASE 10, 12                                 ' up, left arrow
  9225.                  SELECT CASE currButton
  9226.                      CASE 4, 7: ButtonSetState currButton, 2
  9227.                      CASE 2, 3: ScrollList colors$(), colorBox, currButton, 2,
  9228.                      CASE 5, 6: ScrollList styles$(), styleBox, currButton, 2,
  9229.                      CASE 8, 9: ScrollList fonts$(), fontBox, currButton, 2, 0
  9230.                      CASE 10, 11:
  9231.                          ButtonToggle currButton
  9232.                          currButton = 21 - currButton
  9233.                          optionButton = currButton
  9234.                          ButtonToggle optionButton
  9235.                  END SELECT
  9236.              CASE 11, 13                                 ' down, right arrow
  9237.                  SELECT CASE currButton
  9238.                      CASE 1, 4, 7: ButtonSetState currButton, 1
  9239.                      CASE 2, 3: ScrollList colors$(), colorBox, currButton, 3,
  9240.                      CASE 5, 6: ScrollList styles$(), styleBox, currButton, 3,
  9241.                      CASE 8, 9: ScrollList fonts$(), fontBox, currButton, 3, 0
  9242.                      CASE 10, 11:
  9243.                          ButtonToggle currButton
  9244.                          currButton = 21 - currButton
  9245.                          optionButton = currButton
  9246.                          ButtonToggle optionButton
  9247.                  END SELECT
  9248.              CASE 14                                     ' space bar
  9249.                  SELECT CASE currButton
  9250.                      CASE 1, 4, 7: ButtonToggle currButton
  9251.                      CASE 12, 13: finished = TRUE
  9252.                      CASE 14: ChangeTitle 2, title$ + " Title", axis.AxisTitle
  9253.                  END SELECT
  9254.          END SELECT
  9255.  
  9256.          ' error checking on log base before exiting
  9257.          IF finished AND pushButton = 12 THEN
  9258.              IF VAL(EditFieldInquire(1)) <= 0 THEN
  9259.                  PrintError " Log base must be greater than zero."
  9260.                  currEditField = 1
  9261.                  currButton = 0
  9262.                  finished = FALSE
  9263.              ELSEIF VAL(EditFieldInquire(1)) = 1 THEN
  9264.                  PrintError " Log base cannot equal one. Overflow results."
  9265.                  currEditField = 1
  9266.                  currButton = 0
  9267.                  finished = FALSE
  9268.              END IF
  9269.          END IF
  9270.      WEND
  9271.  
  9272.      ' if not canceled then assign and return new values
  9273.      IF pushButton = 12 THEN
  9274.          IF setNum > 0 THEN chartChanged = TRUE
  9275.  
  9276.          axis.LogBase = VAL(EditFieldInquire(1))
  9277.          axis.Grid = (ButtonInquire(4) = 2)
  9278.          axis.Labeled = (ButtonInquire(7) = 2)
  9279.          axis.RangeType = optionButton - 9
  9280.          axis.AxisColor = colorBox.listPos - 1
  9281.          axis.ScaleTitle.TitleColor = axis.AxisTitle.TitleColor
  9282.          axis.ScaleTitle.Justify = axis.AxisTitle.Justify
  9283.          axis.GridStyle = styleBox.listPos
  9284.          axis.TicFont = fontBox.listPos
  9285.      END IF
  9286.  
  9287.      WindowClose 1
  9288.  
  9289.  END SUB
  9290.  
  9291.  '
  9292.  ' Sub Name: ChangeChartType
  9293.  '
  9294.  ' Description: Changes chart type based on menu selection and
  9295.  '              allows the user access to changing the chart style.
  9296.  '
  9297.  ' Arguments: ctype - new chart type
  9298.  '
  9299.  SUB ChangeChartType (ctype)
  9300.  
  9301.      'change type if user selected a different type
  9302.      IF CEnv.ChartType <> ctype THEN
  9303.          IF setNum > 0 THEN chartChanged = TRUE
  9304.  
  9305.          ' reset chosen type
  9306.          MenuItemToggle GALLERYTITLE, CEnv.ChartType
  9307.          ' reset other affected menu items
  9308.          IF CEnv.ChartType = cPie THEN
  9309.              MenuSetState CHARTTITLE, 4, 1
  9310.              MenuSetState CHARTTITLE, 5, 1
  9311.              MenuSetState TITLETITLE, 3, 1
  9312.              MenuSetState TITLETITLE, 4, 1
  9313.          END IF
  9314.  
  9315.          CEnv.ChartType = ctype
  9316.  
  9317.          'if new type is pie then turn off some items
  9318.          IF CEnv.ChartType = cPie THEN
  9319.              MenuSetState CHARTTITLE, 4, 0
  9320.              MenuSetState CHARTTITLE, 5, 0
  9321.              MenuSetState TITLETITLE, 3, 0
  9322.              MenuSetState TITLETITLE, 4, 0
  9323.          END IF
  9324.  
  9325.          ' set type in menu bar
  9326.          MenuItemToggle GALLERYTITLE, CEnv.ChartType
  9327.      END IF
  9328.  
  9329.      ' allow user to change chart style
  9330.      ChangeStyle
  9331.  
  9332.  END SUB
  9333.  
  9334.  '
  9335.  ' Sub Name: ChangeLegend
  9336.  '
  9337.  ' Description: Allows user to view and modify all attributes of the chart
  9338.  '              legend
  9339.  '
  9340.  ' Arguments: none
  9341.  '
  9342.  SUB ChangeLegend
  9343.  
  9344.      DIM fgColorBox AS ListBox
  9345.      DIM fontBox AS ListBox
  9346.  
  9347.      ' set up foreground color box
  9348.      fgColorBox.scrollButton = 6
  9349.      fgColorBox.areaButton = 7
  9350.      fgColorBox.listLen = numColors
  9351.      fgColorBox.topRow = 3
  9352.      fgColorBox.botRow = 10
  9353.      fgColorBox.leftCol = 27
  9354.      fgColorBox.rightCol = 41
  9355.      fgColorBox.listPos = CEnv.Legend.TextColor + 1
  9356.  
  9357.      ' set up font box
  9358.      fontBox.scrollButton = 8
  9359.      fontBox.areaButton = 9
  9360.      fontBox.listLen = numFonts
  9361.      fontBox.topRow = 3
  9362.      fontBox.botRow = 10
  9363.      fontBox.leftCol = 43
  9364.      fontBox.rightCol = 57
  9365.      fontBox.listPos = CEnv.Legend.TextFont
  9366.  
  9367.      ' set up display window
  9368.      winRow = 6
  9369.      winCol = 10
  9370.      WindowOpen 1, winRow, winCol, 18, 69, 0, 7, 0, 7, 15, FALSE, FALSE, FALSE
  9371.      WindowBox 1, 2, 11, 23
  9372.      WindowLocate 5, 4
  9373.      WindowPrint 2, "Location:"
  9374.      WindowBox 6, 4, 10, 21
  9375.      WindowBox 1, 25, 11, 59
  9376.      WindowLocate 2, 27
  9377.      WindowPrint 2, "Text Color:"
  9378.      WindowLocate 2, 43
  9379.      WindowPrint 2, "Text Font:"
  9380.      WindowLine 12
  9381.  
  9382.      ' create list boxes
  9383.      CreateListBox colors$(), fgColorBox, 0
  9384.      CreateListBox fonts$(), fontBox, 0
  9385.  
  9386.      ' open command buttons
  9387.      ButtonOpen 1, 1, "Display Legend", 2, 4, 0, 0, 2
  9388.      ButtonOpen 2, 1, "Autosize", 3, 4, 0, 0, 2
  9389.      ButtonOpen 3, 1, "Overlay", 7, 6, 0, 0, 3
  9390.      ButtonOpen 4, 1, "Bottom", 8, 6, 0, 0, 3
  9391.      ButtonOpen 5, 1, "Right", 9, 6, 0, 0, 3
  9392.      ButtonOpen 10, 2, "OK ", 13, 8, 0, 0, 1
  9393.      ButtonOpen 11, 1, "Cancel ", 13, 21, 0, 0, 1
  9394.      ButtonOpen 12, 1, "Legend Window ", 13, 38, 0, 0, 1
  9395.  
  9396.      currButton = 1                                      ' start with cursor o
  9397.  
  9398.      ' set button states based on current values
  9399.      optionButton = CEnv.Legend.Place + 2
  9400.      ButtonToggle optionButton
  9401.      IF CEnv.Legend.Legend THEN ButtonToggle 1
  9402.      IF CEnv.Legend.AutoSize THEN ButtonToggle 2
  9403.      pushButton = 10
  9404.  
  9405.      ' window control loop
  9406.      finished = FALSE
  9407.      WHILE NOT finished
  9408.          WindowDo currButton, 0                          ' wait for event
  9409.          SELECT CASE Dialog(0)
  9410.              CASE 1                                      ' button pressed
  9411.                  currButton = Dialog(1)
  9412.                  SELECT CASE currButton
  9413.                      CASE 1, 2: ButtonToggle currButton
  9414.                      CASE 3, 4, 5
  9415.                          ButtonToggle optionButton
  9416.                          optionButton = currButton
  9417.                          ButtonToggle optionButton
  9418.                      CASE 6, 7:
  9419.                          ScrollList colors$(), fgColorBox, currButton, 1, 0, w
  9420.                          currButton = 7
  9421.                      CASE 8, 9:
  9422.                          ScrollList fonts$(), fontBox, currButton, 1, 0, winRo
  9423.                          currButton = 9
  9424.                      CASE 10, 11
  9425.                          pushButton = currButton
  9426.                          finished = TRUE
  9427.                      CASE 12
  9428.                          ButtonSetState pushButton, 1
  9429.                          ButtonSetState currButton, 2
  9430.                          pushButton = 12
  9431.                          ChangeWindow 2, "Legend Window", CEnv.Legend.LegendWi
  9432.                  END SELECT
  9433.              CASE 6                                      ' enter
  9434.                  IF pushButton <> 12 THEN
  9435.                      finished = TRUE
  9436.                  ELSE
  9437.                      ChangeWindow 2, "Legend Window", CEnv.Legend.LegendWindow
  9438.                  END IF
  9439.              CASE 7                                      ' tab
  9440.                  SELECT CASE currButton
  9441.                      CASE 1: currButton = 2
  9442.                      CASE 2: currButton = optionButton
  9443.                      CASE 3, 4, 5: currButton = 7
  9444.                      CASE 6, 7: currButton = 9
  9445.                      CASE 8, 9:
  9446.                          currButton = 10
  9447.                          ButtonSetState pushButton, 1
  9448.                          ButtonSetState currButton, 2
  9449.                          pushButton = currButton
  9450.                      CASE 10, 11:
  9451.                          currButton = currButton + 1
  9452.                          ButtonSetState pushButton, 1
  9453.                          ButtonSetState currButton, 2
  9454.                          pushButton = currButton
  9455.                      CASE 12:
  9456.                          ButtonSetState currButton, 1
  9457.                          pushButton = 10
  9458.                          ButtonSetState pushButton, 2
  9459.                          currButton = 1
  9460.                  END SELECT
  9461.              CASE 8                                      ' back tab
  9462.                  SELECT CASE currButton
  9463.                      CASE 1:
  9464.                          currButton = 12
  9465.                          ButtonSetState pushButton, 1
  9466.                          ButtonSetState currButton, 2
  9467.                          pushButton = currButton
  9468.                      CASE 2: currButton = 1
  9469.                      CASE 3, 4, 5: currButton = 2
  9470.                      CASE 6, 7: currButton = optionButton
  9471.                      CASE 8, 9: currButton = 7
  9472.                      CASE 10: currButton = 9
  9473.                      CASE 11, 12:
  9474.                          currButton = currButton - 1
  9475.                          ButtonSetState pushButton, 1
  9476.                          ButtonSetState currButton, 2
  9477.                          pushButton = currButton
  9478.                  END SELECT
  9479.              CASE 9                                      ' escape
  9480.                  pushButton = 11
  9481.                  finished = TRUE
  9482.              CASE 10, 12                                 ' up, left arrow
  9483.                  SELECT CASE currButton
  9484.                      CASE 1, 2: ButtonSetState currButton, 2
  9485.                      CASE 3:
  9486.                          ButtonToggle currButton
  9487.                          currButton = 5
  9488.                          optionButton = currButton
  9489.                          ButtonToggle optionButton
  9490.                      CASE 4, 5:
  9491.                          ButtonToggle currButton
  9492.                          currButton = currButton - 1
  9493.                          optionButton = currButton
  9494.                          ButtonToggle optionButton
  9495.                      CASE 6, 7: ScrollList colors$(), fgColorBox, currButton,
  9496.                      CASE 8, 9: ScrollList fonts$(), fontBox, currButton, 2, 0
  9497.                  END SELECT
  9498.              CASE 11, 13                                 ' down, right arrow
  9499.                  SELECT CASE currButton
  9500.                      CASE 1, 2: ButtonSetState currButton, 1
  9501.                      CASE 3, 4:
  9502.                          ButtonToggle currButton
  9503.                          currButton = currButton + 1
  9504.                          optionButton = currButton
  9505.                          ButtonToggle optionButton
  9506.                      CASE 5:
  9507.                          ButtonToggle currButton
  9508.                          currButton = 3
  9509.                          optionButton = currButton
  9510.                          ButtonToggle optionButton
  9511.                      CASE 6, 7: ScrollList colors$(), fgColorBox, currButton,
  9512.                      CASE 8, 9: ScrollList fonts$(), fontBox, currButton, 3, 0
  9513.                  END SELECT
  9514.              CASE 14                                     ' space bar
  9515.                  SELECT CASE currButton
  9516.                      CASE 1, 2: ButtonToggle currButton
  9517.                      CASE 10, 11: finished = TRUE
  9518.                      CASE 12: ChangeWindow 2, "Legend Window", CEnv.Legend.Leg
  9519.                 END SELECT
  9520.          END SELECT
  9521.      WEND
  9522.  
  9523.      ' if not canceled then return the new values
  9524.      IF pushButton = 10 THEN
  9525.          IF setNum > 0 THEN chartChanged = TRUE
  9526.  
  9527.          CEnv.Legend.TextColor = fgColorBox.listPos - 1
  9528.          CEnv.Legend.TextFont = fontBox.listPos
  9529.          CEnv.Legend.AutoSize = (ButtonInquire(2) = 2)
  9530.          CEnv.Legend.Legend = (ButtonInquire(1) = 2)
  9531.          CEnv.Legend.Place = optionButton - 2
  9532.      END IF
  9533.  
  9534.      WindowClose 1
  9535.  
  9536.  END SUB
  9537.  
  9538.  '
  9539.  ' Sub Name: ChangeStyle
  9540.  '
  9541.  ' Description: Allows user to view and modify the chart style
  9542.  '
  9543.  ' Arguments: none
  9544.  '
  9545.  SUB ChangeStyle
  9546.  DIM fontBox AS ListBox
  9547.  
  9548.      ' determine button labels based on chart type
  9549.      SELECT CASE CEnv.ChartType
  9550.          CASE cBar, cColumn
  9551.              style1$ = "Adjacent"
  9552.              style2$ = "Stacked"
  9553.          CASE cLine, cScatter
  9554.              style1$ = "Lines"
  9555.              style2$ = "No Lines"
  9556.          CASE cPie
  9557.              style1$ = "Percentages"
  9558.              style2$ = "No Percentages"
  9559.      END SELECT
  9560.  
  9561.      topRow = 8
  9562.      leftCol = 26
  9563.      ' if pie, line or scatter chart then add data font
  9564.      IF CEnv.ChartType > 2 THEN
  9565.          WindowOpen 1, topRow, leftCol, 19, 47, 0, 7, 0, 7, 15, FALSE, FALSE,
  9566.          okLine = 12
  9567.  
  9568.          WindowLocate 5, 3
  9569.          WindowPrint -2, "Data Font:"
  9570.          ' set up list box containing valid fonts
  9571.          fontBox.scrollButton = 3
  9572.          fontBox.areaButton = 4
  9573.          fontBox.listLen = numFonts
  9574.          fontBox.topRow = 6
  9575.          fontBox.botRow = 10
  9576.          fontBox.leftCol = 3
  9577.          fontBox.rightCol = 20
  9578.          fontBox.listPos = CEnv.DataFont
  9579.          CreateListBox fonts$(), fontBox, 0
  9580.      ELSE
  9581.          WindowOpen 1, topRow, leftCol, 13, 47, 0, 7, 0, 7, 15, FALSE, FALSE,
  9582.          okLine = 6
  9583.      END IF
  9584.  
  9585.      ' open buttons
  9586.      ButtonOpen 1, 1, style1$, 2, 3, 1, 0, 3
  9587.      ButtonOpen 2, 1, style2$, 3, 3, 1, 0, 3
  9588.      WindowLine okLine - 1
  9589.      ButtonOpen 5, 2, "OK", okLine, 3, 1, 0, 1
  9590.      ButtonOpen 6, 1, "Cancel", okLine, 11, 1, 0, 1
  9591.  
  9592.      pushButton = 5
  9593.      optionButton = CEnv.ChartStyle                     ' set current style
  9594.      currButton = optionButton
  9595.      ButtonSetState optionButton, 2
  9596.  
  9597.      ' window control loop
  9598.      finished = FALSE
  9599.      WHILE NOT finished
  9600.          WindowDo currButton, 0                          ' wait for event
  9601.          SELECT CASE Dialog(0)
  9602.              CASE 1                                      'button pressed
  9603.                  currButton = Dialog(1)
  9604.                  SELECT CASE currButton
  9605.                      CASE 1, 2:
  9606.                          ButtonSetState optionButton, 1
  9607.                          optionButton = currButton
  9608.                          ButtonSetState optionButton, 2
  9609.                      CASE 3, 4:
  9610.                          ScrollList fonts$(), fontBox, currButton, 1, 0, topRo
  9611.                          currButton = 4
  9612.                      CASE 5, 6:
  9613.                          finished = TRUE
  9614.                  END SELECT
  9615.              CASE 6                                      'enter
  9616.                  finished = TRUE
  9617.              CASE 7                                      'tab
  9618.                  SELECT CASE currButton
  9619.                      CASE 1, 2:
  9620.                          IF CEnv.ChartType > 2 THEN
  9621.                              currButton = 4
  9622.                          ELSE
  9623.                              currButton = 5
  9624.                              ButtonSetState pushButton, 1
  9625.                              pushButton = currButton
  9626.                              ButtonSetState pushButton, 2
  9627.                          END IF
  9628.                      CASE 3, 4:
  9629.                          currButton = 5
  9630.                          ButtonSetState pushButton, 1
  9631.                          pushButton = currButton
  9632.                          ButtonSetState currButton, 2
  9633.                      CASE 5:
  9634.                          currButton = 6
  9635.                          ButtonSetState pushButton, 1
  9636.                          pushButton = currButton
  9637.                          ButtonSetState currButton, 2
  9638.                      CASE 6:
  9639.                          currButton = optionButton
  9640.                          ButtonSetState pushButton, 1
  9641.                          pushButton = 5
  9642.                          ButtonSetState pushButton, 2
  9643.                  END SELECT
  9644.              CASE 8                                      'back tab
  9645.                  SELECT CASE currButton
  9646.                      CASE 1, 2:
  9647.                          currButton = 6
  9648.                          ButtonSetState pushButton, 1
  9649.                          pushButton = currButton
  9650.                          ButtonSetState pushButton, 2
  9651.                      CASE 3, 4:
  9652.                          currButton = optionButton
  9653.                      CASE 5:
  9654.                          IF CEnv.ChartType > 2 THEN
  9655.                              currButton = 4
  9656.                          ELSE
  9657.                              currButton = optionButton
  9658.                          END IF
  9659.                      CASE 6:
  9660.                          currButton = 5
  9661.                          ButtonSetState pushButton, 1
  9662.                          pushButton = currButton
  9663.                          ButtonSetState currButton, 2
  9664.                  END SELECT
  9665.              CASE 9                                      'escape
  9666.                  finished = TRUE
  9667.                  pushButton = 5
  9668.              CASE 10, 12                                 'up, left arrow
  9669.                  SELECT CASE currButton
  9670.                      CASE 1, 2:
  9671.                          ButtonSetState currButton, 1
  9672.                          currButton = 3 - currButton
  9673.                          optionButton = currButton
  9674.                          ButtonSetState currButton, 2
  9675.                      CASE 3, 4:
  9676.                          ScrollList fonts$(), fontBox, currButton, 2, 0, topRo
  9677.                  END SELECT
  9678.              CASE 11, 13                                 'down, right arrow
  9679.                  SELECT CASE currButton
  9680.                      CASE 1, 2:
  9681.                          ButtonSetState currButton, 1
  9682.                          currButton = 3 - currButton
  9683.                          optionButton = currButton
  9684.                          ButtonSetState currButton, 2
  9685.                      CASE 3, 4:
  9686.                          ScrollList fonts$(), fontBox, currButton, 3, 0, topRo
  9687.                  END SELECT
  9688.              CASE 14                                     'space bar
  9689.                  IF currButton > 4 THEN finished = TRUE
  9690.          END SELECT
  9691.      WEND
  9692.  
  9693.      ' if not canceled then set new chart style
  9694.      IF pushButton = 5 THEN
  9695.          IF setNum > 0 THEN chartChanged = TRUE
  9696.          CEnv.ChartStyle = optionButton
  9697.          IF CEnv.ChartType > 2 THEN CEnv.DataFont = fontBox.listPos
  9698.      END IF
  9699.  
  9700.      WindowClose 1
  9701.  
  9702.  END SUB
  9703.  
  9704.  '
  9705.  ' Sub Name: ChangeTitle
  9706.  '
  9707.  ' Description: Allows user to view and modify the chart titles
  9708.  '
  9709.  ' Arguments: handle - window number
  9710.  '            wTitle$ - window title
  9711.  '            title -  chart title
  9712.  '            topRow - top row of window
  9713.  '            leftCol - left column of window
  9714.  '
  9715.  SUB ChangeTitle (handle, wTitle$, title AS TitleType, topRow, leftCol)
  9716.  SHARED mode$(), numModes AS INTEGER
  9717.  
  9718.      DIM colorBox AS ListBox
  9719.      DIM fontBox AS ListBox
  9720.  
  9721.      ' set up foreground color box
  9722.      colorBox.scrollButton = 1
  9723.      colorBox.areaButton = 2
  9724.      colorBox.listLen = numColors
  9725.      colorBox.topRow = 6
  9726.      colorBox.botRow = 10
  9727.      colorBox.leftCol = 2
  9728.      colorBox.rightCol = 16
  9729.      colorBox.listPos = title.TitleColor + 1
  9730.  
  9731.      ' set up font box
  9732.      fontBox.scrollButton = 3
  9733.      fontBox.areaButton = 4
  9734.      fontBox.listLen = numFonts
  9735.      fontBox.topRow = 6
  9736.      fontBox.botRow = 10
  9737.      fontBox.leftCol = 18
  9738.      fontBox.rightCol = 36
  9739.      fontBox.listPos = title.TitleFont
  9740.  
  9741.      ' set up display window
  9742.      WindowOpen handle, topRow, leftCol, topRow + 11, leftCol + 50, 0, 7, 0, 7
  9743.      WindowLocate 2, 2
  9744.      WindowPrint 2, "Title:"
  9745.      WindowBox 1, 8, 3, 50
  9746.      WindowBox 6, 38, 10, 50
  9747.      WindowLine 4
  9748.      WindowLine 11
  9749.      WindowLocate 5, 1
  9750.      WindowPrint -1, " Color:          Font:               Justify:"
  9751.  
  9752.      ' set color attribute for title editfield background to that of the chart
  9753.      IF mode$(1) = "10" OR (mode$(1) = "2" AND mode$(2) <> "1") OR mode$(1) =
  9754.          func = 0
  9755.          EditFieldOpen 1, RTRIM$(title.title), 2, 9, 0, 7, 41, 70
  9756.      ELSE
  9757.          SetAtt 5, CEnv.ChartWindow.Background + 1
  9758.          EditFieldOpen 1, RTRIM$(title.title), 2, 9, 12, 5, 41, 70
  9759.          func = 2
  9760.      END IF
  9761.  
  9762.      ' create list boxes
  9763.      CreateListBox colors$(), colorBox, func
  9764.      CreateListBox fonts$(), fontBox, 0
  9765.  
  9766.      ' open buttons
  9767.      ButtonOpen 5, 1, "Left", 7, 39, 0, 0, 3
  9768.      ButtonOpen 6, 1, "Center", 8, 39, 0, 0, 3
  9769.      ButtonOpen 7, 1, "Right", 9, 39, 0, 0, 3
  9770.      ButtonOpen 8, 2, "OK ", 12, 10, 0, 0, 1
  9771.      ButtonOpen 9, 1, "Cancel ", 12, 33, 0, 0, 1
  9772.  
  9773.      currButton = 0                                      ' start in edit field
  9774.      currEditField = 1
  9775.      optionButton = 4 + title.Justify                    ' set button state
  9776.      ButtonToggle optionButton
  9777.      pushButton = 8
  9778.  
  9779.      ' window control loop
  9780.      finished = FALSE
  9781.      WHILE NOT finished
  9782.          WindowDo currButton, currEditField              ' wait for event
  9783.          SELECT CASE Dialog(0)
  9784.              CASE 1                                      ' button pressed
  9785.                  currButton = Dialog(1)
  9786.                  SELECT CASE currButton
  9787.                      CASE 1, 2
  9788.                          currEditField = 0
  9789.                          ScrollList colors$(), colorBox, currButton, 1, func,
  9790.                          currButton = 2
  9791.                      CASE 3, 4
  9792.                          currEditField = 0
  9793.                          ScrollList fonts$(), fontBox, currButton, 1, 0, topRo
  9794.                          currButton = 4
  9795.                      CASE 5, 6, 7
  9796.                          ButtonToggle optionButton
  9797.                          optionButton = currButton
  9798.                          ButtonToggle optionButton
  9799.                          currEditField = 0
  9800.                      CASE 8, 9
  9801.                          pushButton = currButton
  9802.                          finished = TRUE
  9803.                  END SELECT
  9804.              CASE 2                                      ' edit field
  9805.                  currButton = 0
  9806.                  currEditField = 1
  9807.              CASE 6                                      ' enter
  9808.                  finished = TRUE
  9809.              CASE 7                                      ' tab
  9810.                  SELECT CASE currButton
  9811.                      CASE 0:
  9812.                          currButton = 2
  9813.                          currEditField = 0
  9814.                      CASE 1, 2: currButton = 4
  9815.                      CASE 3, 4: currButton = optionButton
  9816.                      CASE 5, 6, 7:
  9817.                          currButton = 8
  9818.                          ButtonSetState pushButton, 1
  9819.                          ButtonSetState currButton, 2
  9820.                          pushButton = 8
  9821.                      CASE 8:
  9822.                          currButton = currButton + 1
  9823.                          ButtonSetState pushButton, 1
  9824.                          ButtonSetState currButton, 2
  9825.                          pushButton = currButton
  9826.                      CASE 9:
  9827.                          ButtonSetState currButton, 1
  9828.                          pushButton = 8
  9829.                          ButtonSetState pushButton, 2
  9830.                          currButton = 0
  9831.                          currEditField = 1
  9832.                  END SELECT
  9833.              CASE 8                                      ' back tab
  9834.                  SELECT CASE currButton
  9835.                      CASE 0:
  9836.                          currButton = 9
  9837.                          ButtonSetState pushButton, 1
  9838.                          ButtonSetState currButton, 2
  9839.                          pushButton = 9
  9840.                          currEditField = 0
  9841.                      CASE 1, 2:
  9842.                          currButton = 0
  9843.                          currEditField = 1
  9844.                      CASE 3, 4: currButton = 2
  9845.                      CASE 5, 6, 7: currButton = 4
  9846.                      CASE 8: currButton = optionButton
  9847.                      CASE 9:
  9848.                          currButton = currButton - 1
  9849.                          ButtonSetState pushButton, 1
  9850.                          ButtonSetState currButton, 2
  9851.                          pushButton = currButton
  9852.                  END SELECT
  9853.              CASE 9                                      ' escape
  9854.                  pushButton = 9
  9855.                  finished = TRUE
  9856.              CASE 10, 12                                 ' up, left arrow
  9857.                  SELECT CASE currButton
  9858.                      CASE 1, 2: ScrollList colors$(), colorBox, currButton, 2,
  9859.                      CASE 3, 4: ScrollList fonts$(), fontBox, currButton, 2, 0
  9860.                      CASE 5:
  9861.                          ButtonToggle currButton
  9862.                          currButton = 7
  9863.                          optionButton = 7
  9864.                          ButtonToggle optionButton
  9865.                      CASE 6, 7:
  9866.                          ButtonToggle currButton
  9867.                          currButton = currButton - 1
  9868.                          optionButton = currButton
  9869.                          ButtonToggle optionButton
  9870.                  END SELECT
  9871.              CASE 11, 13                                 ' down, right arrow
  9872.                  SELECT CASE currButton
  9873.                      CASE 1, 2: ScrollList colors$(), colorBox, currButton, 3,
  9874.                      CASE 3, 4: ScrollList fonts$(), fontBox, currButton, 3, 0
  9875.                      CASE 5, 6:
  9876.                          ButtonToggle currButton
  9877.                          currButton = currButton + 1
  9878.                          optionButton = currButton
  9879.                          ButtonToggle optionButton
  9880.                      CASE 7:
  9881.                          ButtonToggle currButton
  9882.                          currButton = 5
  9883.                          optionButton = 5
  9884.                          ButtonToggle optionButton
  9885.                  END SELECT
  9886.              CASE 14                                     ' space bar
  9887.                  IF currButton > 7 THEN
  9888.                      pushButton = currButton
  9889.                      finished = TRUE
  9890.                  END IF
  9891.          END SELECT
  9892.      WEND
  9893.  
  9894.      ' done and not canceled so return new title information
  9895.      IF pushButton = 8 THEN
  9896.          IF setNum > 0 THEN chartChanged = TRUE
  9897.  
  9898.          title.title = EditFieldInquire(1)
  9899.          title.TitleFont = fontBox.listPos
  9900.          title.TitleColor = colorBox.listPos - 1
  9901.          title.Justify = optionButton - 4
  9902.      END IF
  9903.  
  9904.      WindowClose handle
  9905.  
  9906.  END SUB
  9907.  
  9908.  '
  9909.  ' Sub Name: ChangeWindow
  9910.  '
  9911.  ' Description: Allows user to view and modify any of the chart windows
  9912.  '
  9913.  ' Arguments: handle - window number
  9914.  '            wTitle$ - window title
  9915.  '            win - chart window
  9916.  '
  9917.  SUB ChangeWindow (handle, title$, win AS RegionType)
  9918.  
  9919.      DIM bgColorBox AS ListBox
  9920.      DIM bdColorBox AS ListBox
  9921.      DIM bdStyleBox AS ListBox
  9922.  
  9923.      ' set up background color box
  9924.      bgColorBox.scrollButton = 1
  9925.      bgColorBox.areaButton = 2
  9926.      bgColorBox.listLen = numColors
  9927.      bgColorBox.topRow = 4
  9928.      bgColorBox.botRow = 14
  9929.      bgColorBox.leftCol = 4
  9930.      bgColorBox.rightCol = 18
  9931.      bgColorBox.listPos = win.Background + 1
  9932.  
  9933.      ' set up border color box
  9934.      bdColorBox.scrollButton = 3
  9935.      bdColorBox.areaButton = 4
  9936.      bdColorBox.listLen = numColors
  9937.      bdColorBox.topRow = 5
  9938.      bdColorBox.botRow = 14
  9939.      bdColorBox.leftCol = 24
  9940.      bdColorBox.rightCol = 38
  9941.      bdColorBox.listPos = win.BorderColor + 1
  9942.  
  9943.      ' set up border style box
  9944.      bdStyleBox.scrollButton = 5
  9945.      bdStyleBox.areaButton = 6
  9946.      bdStyleBox.listLen = MAXSTYLES
  9947.      bdStyleBox.topRow = 5
  9948.      bdStyleBox.botRow = 14
  9949.      bdStyleBox.leftCol = 40
  9950.      bdStyleBox.rightCol = 54
  9951.      bdStyleBox.listPos = win.BorderStyle
  9952.  
  9953.      ' set up display window
  9954.      winRow = 5
  9955.      winCol = 3
  9956.      WindowOpen handle, winRow, winCol, 21, 76, 0, 7, 0, 7, 15, FALSE, FALSE,
  9957.      WindowBox 1, 2, 15, 20
  9958.      WindowLocate 2, 5
  9959.      WindowPrint 2, "Background"
  9960.      WindowLocate 3, 5
  9961.      WindowPrint 2, "Color:"
  9962.      WindowBox 1, 22, 15, 56
  9963.      WindowLocate 4, 24
  9964.      WindowPrint 2, "Border Color:"
  9965.      WindowLocate 4, 40
  9966.      WindowPrint 2, "Border Style:"
  9967.      WindowBox 1, 58, 15, 73
  9968.      WindowLocate 2, 60
  9969.      WindowPrint 2, "Coordinates:"
  9970.      WindowBox 3, 63, 5, 71
  9971.      WindowLocate 4, 60
  9972.      WindowPrint 2, "X1:"
  9973.      WindowBox 6, 63, 8, 71
  9974.      WindowLocate 7, 60
  9975.      WindowPrint 2, "Y1:"
  9976.      WindowBox 9, 63, 11, 71
  9977.      WindowLocate 10, 60
  9978.      WindowPrint 2, "X2:"
  9979.      WindowBox 12, 63, 14, 71
  9980.      WindowLocate 13, 60
  9981.      WindowPrint 2, "Y2:"
  9982.      WindowLine 16
  9983.  
  9984.      CreateListBox colors$(), bgColorBox, 0
  9985.      CreateListBox colors$(), bdColorBox, 0
  9986.      CreateListBox styles$(), bdStyleBox, 0
  9987.  
  9988.      ButtonOpen 7, 1, "Display Border", 2, 24, 0, 0, 2
  9989.      ButtonOpen 8, 2, "OK ", 17, 14, 0, 0, 1
  9990.      ButtonOpen 9, 1, "Cancel ", 17, 51, 0, 0, 1
  9991.  
  9992.      EditFieldOpen 1, LTRIM$(STR$(win.X1)), 4, 64, 0, 7, 7, 10
  9993.      EditFieldOpen 2, LTRIM$(STR$(win.Y1)), 7, 64, 0, 7, 7, 10
  9994.      EditFieldOpen 3, LTRIM$(STR$(win.X2)), 10, 64, 0, 7, 7, 10
  9995.      EditFieldOpen 4, LTRIM$(STR$(win.Y2)), 13, 64, 0, 7, 7, 10
  9996.  
  9997.      currButton = 2                                      ' start in first list
  9998.      currEditField = 0
  9999.      IF win.border = TRUE THEN ButtonSetState 7, 2
  10000.      pushButton = 8
  10001.  
  10002.      ' window control loop
  10003.      finished = FALSE
  10004.      WHILE NOT finished
  10005.          WindowDo currButton, currEditField              ' wait for event
  10006.          SELECT CASE Dialog(0)
  10007.              CASE 1                                      ' button pressed
  10008.                  currButton = Dialog(1)
  10009.                  SELECT CASE currButton
  10010.                      CASE 1, 2
  10011.                          currEditField = 0
  10012.                          ScrollList colors$(), bgColorBox, currButton, 1, 0, w
  10013.                          currButton = 2
  10014.                      CASE 3, 4
  10015.                          currEditField = 0
  10016.                          ScrollList colors$(), bdColorBox, currButton, 1, 0, w
  10017.                          currButton = 4
  10018.                      CASE 5, 6
  10019.                          currEditField = 0
  10020.                          ScrollList styles$(), bdStyleBox, currButton, 1, 0, w
  10021.                          currButton = 6
  10022.                      CASE 7
  10023.                          ButtonToggle currButton
  10024.                          currEditField = 0
  10025.                      CASE 8, 9
  10026.                          pushButton = currButton
  10027.                          finished = TRUE
  10028.                  END SELECT
  10029.              CASE 2                                      ' edit field
  10030.                  currEditField = Dialog(2)
  10031.                  currButton = 0
  10032.              CASE 6                                      ' enter
  10033.                  finished = TRUE
  10034.              CASE 7                                      ' tab
  10035.                  SELECT CASE currButton
  10036.                      CASE 0:
  10037.                          SELECT CASE currEditField
  10038.                              CASE 1, 2, 3: currEditField = currEditField + 1
  10039.                              CASE 4:
  10040.                                  currEditField = 0
  10041.                                  currButton = 8
  10042.                                  ButtonSetState pushButton, 1
  10043.                                  ButtonSetState currButton, 2
  10044.                                  pushButton = currButton
  10045.                          END SELECT
  10046.                      CASE 1, 2: currButton = 7
  10047.                      CASE 3, 4: currButton = 6
  10048.                      CASE 5, 6:
  10049.                          currButton = 0
  10050.                          currEditField = 1
  10051.                      CASE 7: currButton = 4
  10052.                      CASE 8:
  10053.                          currButton = currButton + 1
  10054.                          ButtonSetState pushButton, 1
  10055.                          ButtonSetState currButton, 2
  10056.                          pushButton = currButton
  10057.                      CASE 9:
  10058.                          ButtonSetState currButton, 1
  10059.                          pushButton = 8
  10060.                          ButtonSetState pushButton, 2
  10061.                          currButton = 2
  10062.                          currEditField = 0
  10063.                  END SELECT
  10064.              CASE 8                                      ' back tab
  10065.                  SELECT CASE currButton
  10066.                      CASE 0:
  10067.                          SELECT CASE currEditField
  10068.                              CASE 1:
  10069.                                  currEditField = 0
  10070.                                  currButton = 6
  10071.                              CASE 2, 3, 4: currEditField = currEditField - 1
  10072.                          END SELECT
  10073.                      CASE 1, 2:
  10074.                          currButton = 9
  10075.                          ButtonSetState pushButton, 1
  10076.                          ButtonSetState currButton, 2
  10077.                          pushButton = currButton
  10078.                      CASE 3, 4: currButton = 7
  10079.                      CASE 5, 6: currButton = 4
  10080.                      CASE 7: currButton = 2
  10081.                      CASE 8:
  10082.                          currButton = 0
  10083.                          currEditField = 4
  10084.                      CASE 9:
  10085.                          currButton = currButton - 1
  10086.                          ButtonSetState pushButton, 1
  10087.                          ButtonSetState currButton, 2
  10088.                          pushButton = currButton
  10089.                  END SELECT
  10090.              CASE 9                                      ' escape
  10091.                  pushButton = 9
  10092.                  finished = TRUE
  10093.              CASE 10, 12                                 ' up, left arrow
  10094.                  SELECT CASE currButton
  10095.                      CASE 1, 2: ScrollList colors$(), bgColorBox, currButton,
  10096.                      CASE 3, 4: ScrollList colors$(), bdColorBox, currButton,
  10097.                      CASE 5, 6: ScrollList styles$(), bdStyleBox, currButton,
  10098.                      CASE 7: ButtonSetState currButton, 2
  10099.                  END SELECT
  10100.              CASE 11, 13                                 ' down, right arrow
  10101.                  SELECT CASE currButton
  10102.                      CASE 1, 2: ScrollList colors$(), bgColorBox, currButton,
  10103.                      CASE 3, 4: ScrollList colors$(), bdColorBox, currButton,
  10104.                      CASE 5, 6: ScrollList styles$(), bdStyleBox, currButton,
  10105.                      CASE 7: ButtonSetState currButton, 1
  10106.                  END SELECT
  10107.              CASE 14                                     ' space bar
  10108.                  SELECT CASE currButton
  10109.                      CASE 7: ButtonToggle currButton
  10110.                      CASE 8, 9: finished = TRUE
  10111.                  END SELECT
  10112.          END SELECT
  10113.      WEND
  10114.  
  10115.      ' return new window information
  10116.      IF pushButton = 8 THEN
  10117.          IF setNum > 0 THEN chartChanged = TRUE
  10118.  
  10119.          win.X1 = VAL(EditFieldInquire(1))
  10120.          win.Y1 = VAL(EditFieldInquire(2))
  10121.          win.X2 = VAL(EditFieldInquire(3))
  10122.          win.Y2 = VAL(EditFieldInquire(4))
  10123.          win.Background = bgColorBox.listPos - 1
  10124.          win.border = (ButtonInquire(7) = 2)
  10125.          win.BorderColor = bdColorBox.listPos - 1
  10126.          win.BorderStyle = bdStyleBox.listPos
  10127.      END IF
  10128.  
  10129.      WindowClose handle
  10130.  
  10131.  END SUB
  10132.  
  10133.  
  10134.  
  10135.  CHRTDEM2.BAS
  10136.  CD-ROM Disc Path:   \SAMPCODE\BASIC\CHRTDEM2.BAS
  10137.  
  10138.  '       CHRTDEM2.BAS - third module of the CHRTB demonstration program.
  10139.  '
  10140.  '               Copyright (C) 1989, Microsoft Corporation
  10141.  '
  10142.  '   Main module - CHRTDEMO.BAS
  10143.  '   Include files - CHRTDEMO.BI
  10144.  '
  10145.  '$INCLUDE: 'chrtdemo.bi'
  10146.  
  10147.  ' local functions
  10148.  DECLARE FUNCTION TrueColr% (colr%)
  10149.  
  10150.  ' local subs
  10151.  DECLARE SUB OpenChart (newFlag%)
  10152.  DECLARE SUB Quit ()
  10153.  DECLARE SUB InitFonts ()
  10154.  DECLARE SUB InitStyles ()
  10155.  DECLARE SUB SetDisplayColor ()
  10156.  DECLARE SUB SetUpBackground ()
  10157.  DECLARE SUB SetUpMenu ()
  10158.  DECLARE SUB ViewChart ()
  10159.  DECLARE SUB ViewFont ()
  10160.  DECLARE SUB ViewScreenMode ()
  10161.  
  10162.  DIM colorDisplay            AS INTEGER
  10163.  DIM egacolor(0 TO 15)       AS INTEGER
  10164.  DIM origPath$
  10165.  
  10166.  DEFINT A-Z
  10167.  '
  10168.  ' Sub Name: ClearData
  10169.  '
  10170.  ' Description: Clears all chart data
  10171.  ' Arguments: None
  10172.  '
  10173.  SUB ClearData
  10174.  SHARED Cat$(), catLen AS INTEGER
  10175.  SHARED setVal!(), setLen() AS INTEGER, setName$()
  10176.  
  10177.      ' Can't view  chart when no data present
  10178.      MenuSetState VIEWTITLE, 2, 0
  10179.  
  10180.      ' Clear categories
  10181.      FOR i = 1 TO cMaxValues
  10182.          Cat$(i) = ""
  10183.      NEXT i
  10184.      catLen = 0
  10185.  
  10186.      ' Clear set names and values
  10187.      FOR i = 1 TO cMaxSets
  10188.          setName$(i) = ""
  10189.          setLen(i) = 0
  10190.          FOR j = 1 TO cMaxValues
  10191.              setVal!(j, i) = cMissingValue
  10192.          NEXT j
  10193.      NEXT i
  10194.      setNum = 0
  10195.  
  10196.      ' chart not changed
  10197.      chartChanged = FALSE
  10198.  
  10199.  END SUB
  10200.  
  10201.  '
  10202.  ' Sub Name: ClearFonts
  10203.  '
  10204.  ' Description: Sets all chart font pointers to 1.  This is called
  10205.  '              each time new fonts are loaded to ensure that
  10206.  '              all chart fonts specify a meaningful font
  10207.  '
  10208.  ' Arguments: None
  10209.  '
  10210.  SUB ClearFonts
  10211.  
  10212.      ' reset all font pointers if don't map to current fonts
  10213.      IF CEnv.DataFont > numFonts THEN CEnv.DataFont = 1
  10214.      IF CEnv.MainTitle.TitleFont > numFonts THEN CEnv.MainTitle.TitleFont = 1
  10215.      IF CEnv.SubTitle.TitleFont > numFonts THEN CEnv.SubTitle.TitleFont = 1
  10216.      IF CEnv.XAxis.AxisTitle.TitleFont > numFonts THEN CEnv.XAxis.AxisTitle.Ti
  10217.      IF CEnv.XAxis.TicFont > numFonts THEN CEnv.XAxis.TicFont = 1
  10218.      IF CEnv.YAxis.AxisTitle.TitleFont > numFonts THEN CEnv.YAxis.AxisTitle.Ti
  10219.      IF CEnv.YAxis.TicFont > numFonts THEN CEnv.YAxis.TicFont = 1
  10220.      IF CEnv.Legend.TextFont > numFonts THEN CEnv.Legend.TextFont = 1
  10221.  
  10222.  END SUB
  10223.  
  10224.  '
  10225.  ' Sub Name: CreateListBox
  10226.  '
  10227.  ' Description: Creates a list box within the current window
  10228.  ' Arguments: text$() - the list
  10229.  '            tbox    - the listBox
  10230.  '            func    - function flag for DrawList
  10231.  '
  10232.  SUB CreateListBox (text$(), tbox AS ListBox, func)
  10233.  
  10234.      ' get box length
  10235.      tbox.boxLen = tbox.botRow - tbox.topRow - 1
  10236.  
  10237.      ' get displayable length
  10238.      IF tbox.listLen < tbox.boxLen THEN
  10239.          tbox.maxLen = tbox.listLen
  10240.      ELSE
  10241.          tbox.maxLen = tbox.boxLen
  10242.      END IF
  10243.  
  10244.      ' get box width
  10245.      tbox.boxWid = tbox.rightCol - tbox.leftCol - 1
  10246.  
  10247.      ' create box
  10248.      WindowBox tbox.topRow, tbox.leftCol, tbox.botRow, tbox.rightCol
  10249.  
  10250.      ' add scroll bar if necessary or if forced (func = 5)
  10251.      IF tbox.listLen <> tbox.maxLen OR func = 5 THEN
  10252.          ButtonOpen tbox.scrollButton, 1, "", tbox.topRow + 1, tbox.rightCol,
  10253.      ELSE
  10254.          tbox.scrollButton = 0
  10255.      END IF
  10256.  
  10257.      ' open area button
  10258.      ButtonOpen tbox.areaButton, 1, "", tbox.topRow + 1, tbox.leftCol + 1, tbo
  10259.  
  10260.      ' set current list element relative to list box top
  10261.      IF tbox.listPos <= tbox.maxLen THEN
  10262.          tbox.currTop = 1
  10263.          tbox.currPos = tbox.listPos
  10264.      ELSEIF tbox.listPos + tbox.maxLen > tbox.listLen + 1 THEN
  10265.          tbox.currTop = tbox.listLen - tbox.maxLen + 1
  10266.          tbox.currPos = tbox.listPos - tbox.currTop + 1
  10267.      ELSE
  10268.          tbox.currTop = tbox.listPos
  10269.          tbox.currPos = 1
  10270.      END IF
  10271.  
  10272.      ' Display list within the box
  10273.      DrawList text$(), tbox, func
  10274.  
  10275.  END SUB
  10276.  
  10277.  '
  10278.  ' Sub Name: DrawList
  10279.  '
  10280.  ' Description: Displays a list within the boundaries of a list box
  10281.  ' Arguments: text$() - the list
  10282.  '            tbox    - the listBox
  10283.  '            func    - function flag for special operations
  10284.  '
  10285.  SUB DrawList (text$(), tbox AS ListBox, func)
  10286.  
  10287.      ' Draw each element of list that should currently appear in box
  10288.      FOR i% = 1 TO tbox.boxLen
  10289.          ' highlight current list element
  10290.          IF i% = tbox.currPos THEN
  10291.              WindowColor 7, 0
  10292.          ELSE
  10293.              WindowColor 0, 7
  10294.          END IF
  10295.  
  10296.          WindowLocate tbox.topRow + i%, tbox.leftCol + 1
  10297.          IF i <= tbox.maxLen THEN
  10298.              WindowPrint -1, LEFT$(text$(tbox.currTop + i% - 1) + STRING$(tbox
  10299.          ELSE
  10300.              WindowPrint -1, STRING$(tbox.boxWid, " ")
  10301.          END IF
  10302.      NEXT i%
  10303.  
  10304.      ' update scrollbar position indicator if scrollbar present
  10305.      IF tbox.scrollButton <> 0 THEN
  10306.          IF tbox.listLen <> 0 THEN
  10307.              position = (tbox.currTop + tbox.currPos - 1) * (tbox.maxLen - 2)
  10308.              IF position < 1 THEN
  10309.                 position = 1
  10310.              ELSEIF position > tbox.maxLen - 2 THEN
  10311.                position = tbox.maxLen - 2
  10312.              END IF
  10313.          ELSE
  10314.              position = 1
  10315.          END IF
  10316.          ButtonSetState tbox.scrollButton, position
  10317.      END IF
  10318.  
  10319.      ' Reset color in case current element was last to be drawn
  10320.      WindowColor 0, 7
  10321.  
  10322.      ' update current position in case list has been scrolled
  10323.      tbox.listPos = tbox.currTop + tbox.currPos - 1
  10324.  
  10325.      ' handle special operation of immediately updating colors$ in title editf
  10326.      SELECT CASE func
  10327.          CASE 2: SetAtt 12, tbox.listPos          ' update title editfield for
  10328.      END SELECT
  10329.  
  10330.  END SUB
  10331.  
  10332.  '
  10333.  ' Func Name: HandleMenuEvent
  10334.  '
  10335.  ' Description: Determines the action to be performed when user makes
  10336.  '              a menu selection.
  10337.  '
  10338.  ' Arguments: none
  10339.  '
  10340.  SUB HandleMenuEvent
  10341.  SHARED saveFile$, colorDisplay AS INTEGER
  10342.  
  10343.      menu = MenuCheck(0)
  10344.      item = MenuCheck(1)
  10345.  
  10346.      SELECT CASE menu
  10347.          ' file menu title selection
  10348.          CASE FILETITLE
  10349.              SELECT CASE item
  10350.                  ' new chart
  10351.                  CASE 1: OpenChart TRUE
  10352.                  ' open existing chart
  10353.                  CASE 2: OpenChart FALSE
  10354.                  ' save current chart
  10355.                  CASE 3: junk = SaveChart(saveFile$, FALSE)
  10356.                  ' save current chart under new name
  10357.                  CASE 4: junk = SaveChart(saveFile$, TRUE)
  10358.                  ' exit program
  10359.                  CASE 6: Quit
  10360.              END SELECT
  10361.  
  10362.          ' view menu title selection
  10363.          CASE VIEWTITLE
  10364.              SELECT CASE item
  10365.                  ' Display and edit existing chart data
  10366.                  CASE 1: ViewData
  10367.                  ' Display chart
  10368.                  CASE 2: ViewChart
  10369.                  ' Display and load fonts
  10370.                  CASE 3: ViewFont
  10371.                  ' Display and edit screen mode
  10372.                  CASE 4: ViewScreenMode
  10373.              END SELECT
  10374.  
  10375.          ' Gallery menu title selection
  10376.          CASE GALLERYTITLE
  10377.              ' change chart type
  10378.              ChangeChartType item
  10379.  
  10380.          ' Chart menu title selection
  10381.          CASE CHARTTITLE
  10382.              SELECT CASE item
  10383.                  ' Change chart window
  10384.                  CASE 1: ChangeWindow 1, "Chart Window", CEnv.ChartWindow
  10385.                  ' Change data window
  10386.                  CASE 2: ChangeWindow 1, "Data Window", CEnv.DataWindow
  10387.                  ' Change legend
  10388.                  CASE 3: ChangeLegend
  10389.                  ' Change X axis
  10390.                  CASE 4: ChangeAxis "X Axis", CEnv.XAxis
  10391.                  ' Change Y axis
  10392.                  CASE 5: ChangeAxis "Y Axis", CEnv.YAxis
  10393.              END SELECT
  10394.  
  10395.          ' Title menu title selection
  10396.          CASE TITLETITLE
  10397.              SELECT CASE item
  10398.                  ' Display and modify main title
  10399.                  CASE 1: ChangeTitle 1, "Main Title", CEnv.MainTitle, 6, 16
  10400.                  ' Display and modify sub title
  10401.                  CASE 2: ChangeTitle 1, "Sub Title", CEnv.SubTitle, 6, 16
  10402.                  ' Display and modify x axis title
  10403.                  CASE 3:
  10404.                      ChangeTitle 1, "X-axis Title", CEnv.XAxis.AxisTitle, 6, 1
  10405.                      CEnv.XAxis.ScaleTitle.TitleColor = CEnv.XAxis.AxisTitle.T
  10406.                      CEnv.XAxis.ScaleTitle.Justify = CEnv.XAxis.AxisTitle.Just
  10407.                  ' Display and modify y axis title
  10408.                  CASE 4:
  10409.                      ChangeTitle 1, "Y-axis Title", CEnv.YAxis.AxisTitle, 6, 1
  10410.                      CEnv.YAxis.ScaleTitle.TitleColor = CEnv.YAxis.AxisTitle.T
  10411.                      CEnv.YAxis.ScaleTitle.Justify = CEnv.YAxis.AxisTitle.Just
  10412.              END SELECT
  10413.  
  10414.          ' Options menu title selection
  10415.          CASE OPTIONSTITLE
  10416.              colorDisplay = item - 2
  10417.              SetDisplayColor
  10418.      END SELECT
  10419.  
  10420.  END SUB
  10421.  
  10422.  '
  10423.  ' Func Name: InitAll
  10424.  '
  10425.  ' Description: Performs all initialization for the program
  10426.  '
  10427.  ' Arguments: none
  10428.  '
  10429.  SUB InitAll
  10430.  SHARED finished AS INTEGER, screenMode AS INTEGER, saveFile$
  10431.  SHARED origPath$, colorDisplay  AS INTEGER
  10432.  
  10433.      saveFile$ = ""                          ' No save file to begin with
  10434.      origPath$ = CURDIR$                     ' get working path
  10435.      colorDisplay = FALSE                    ' start with mono display
  10436.      GetBestMode screenMode                  ' get initial screen mode
  10437.  
  10438.      SCREEN 0                                ' init screen
  10439.      WIDTH 80, 25
  10440.      CLS
  10441.  
  10442.      MenuInit                                ' init menu routines
  10443.      WindowInit                              ' init window routines
  10444.      MouseInit                               ' init mouse routines
  10445.  
  10446.      ' exit if no graphic mode available
  10447.      IF screenMode = 0 THEN
  10448.          PrintError "No graphic screen modes available for charting. Exiting p
  10449.          finished = TRUE
  10450.          EXIT SUB
  10451.      ELSE
  10452.          finished = FALSE
  10453.      END IF
  10454.  
  10455.      SetUpMenu                               ' Set up menu bar
  10456.      SetUpBackground                         ' Set up screen background
  10457.      InitChart                               ' Initialize chart
  10458.      InitColors                              ' Set up color list
  10459.      InitStyles                              ' Set up border style list
  10460.      InitFonts                               ' Set up font lists
  10461.  
  10462.      MenuShow                                ' display menu bar
  10463.      MouseShow                               ' display mouse
  10464.  
  10465.      '               display program introduction
  10466.      a$ = "Microsoft QuickChart|"
  10467.      a$ = a$ + "A Presentation Graphics Toolbox Demo|"
  10468.      a$ = a$ + "for|"
  10469.      a$ = a$ + "Microsoft BASIC 7.0 Professional Development System|"
  10470.      a$ = a$ + "Copyright (c) 1989 Microsoft Corporation|"
  10471.  
  10472.      temp = Alert(4, a$, 9, 12, 15, 68, "Color", "Monochrome", "")
  10473.  
  10474.      ' set display to color or monochrome depending on colorDislay
  10475.      IF temp = 1 THEN colorDisplay = TRUE
  10476.  
  10477.      SetDisplayColor
  10478.  
  10479.  END SUB
  10480.  
  10481.  '
  10482.  ' Sub Name: InitChart
  10483.  '
  10484.  ' Description: Initializes chart environment variables and other
  10485.  '              related information.
  10486.  '
  10487.  ' Arguments: None
  10488.  '
  10489.  SUB InitChart
  10490.  
  10491.      MenuItemToggle GALLERYTITLE, cBar       ' default chart type is BAR so
  10492.                                              ' set up menu that way
  10493.  
  10494.      DefaultChart CEnv, cBar, cPlain         ' Get defaults for chart variable
  10495.  
  10496.      ClearData                               ' Clear all chart data
  10497.  
  10498.  END SUB
  10499.  
  10500.  '
  10501.  ' Sub Name: Initcolors
  10502.  '
  10503.  ' Description: Creates color list based on screen mode
  10504.  '
  10505.  ' Arguments: None
  10506.  '
  10507.  SUB InitColors
  10508.  SHARED screenMode AS INTEGER
  10509.  SHARED egacolor() AS INTEGER
  10510.  
  10511.      ' init EGA colors$ for SetAtt
  10512.      egacolor(0) = 0
  10513.      egacolor(1) = 1
  10514.      egacolor(2) = 2
  10515.      egacolor(3) = 3
  10516.      egacolor(4) = 4
  10517.      egacolor(5) = 5
  10518.      egacolor(6) = 20
  10519.      egacolor(7) = 7
  10520.      egacolor(8) = 56
  10521.      egacolor(9) = 57
  10522.      egacolor(10) = 58
  10523.      egacolor(11) = 59
  10524.      egacolor(12) = 60
  10525.      egacolor(13) = 61
  10526.      egacolor(14) = 62
  10527.      egacolor(15) = 63
  10528.  
  10529.      ' create list of displayable colors$ based on screen mode
  10530.      SELECT CASE screenMode
  10531.          CASE 1
  10532.              numColors = 4
  10533.              REDIM color$(numColors)
  10534.              colors$(1) = "Black"
  10535.              colors$(2) = "White"
  10536.              colors$(3) = "Bright Cyan"
  10537.              colors$(4) = "Bright Magenta"
  10538.          CASE 2, 3, 4, 11
  10539.              numColors = 2
  10540.              REDIM color$(numColors)
  10541.              colors$(1) = "Black"
  10542.              colors$(2) = "White"
  10543.          CASE 7, 8, 9, 12, 13
  10544.              numColors = 16
  10545.              REDIM color$(numColors)
  10546.              colors$(1) = "Black"
  10547.              colors$(2) = "High White"
  10548.              colors$(3) = "Blue"
  10549.              colors$(4) = "Green"
  10550.              colors$(5) = "Cyan"
  10551.              colors$(6) = "Red"
  10552.              colors$(7) = "Magenta"
  10553.              colors$(8) = "Brown"
  10554.              colors$(9) = "White"
  10555.              colors$(10) = "Gray"
  10556.              colors$(11) = "Bright Blue"
  10557.              colors$(12) = "Bright Green"
  10558.              colors$(13) = "Bright Cyan"
  10559.              colors$(14) = "Bright Red"
  10560.              colors$(15) = "Bright Magenta"
  10561.              colors$(16) = "Yellow"
  10562.          CASE 10
  10563.              numColors = 4
  10564.              REDIM color$(numColors)
  10565.              colors$(1) = "Off"
  10566.              colors$(2) = "On High"
  10567.              colors$(3) = "On Normal"
  10568.              colors$(4) = "Blink"
  10569.      END SELECT
  10570.  
  10571.      ' reset chart color pointers to default values
  10572.      IF numColors < 16 THEN
  10573.          CEnv.ChartWindow.Background = 0
  10574.          CEnv.ChartWindow.BorderColor = 1
  10575.          CEnv.DataWindow.Background = 0
  10576.          CEnv.DataWindow.BorderColor = 1
  10577.          CEnv.MainTitle.TitleColor = 1
  10578.          CEnv.SubTitle.TitleColor = 1
  10579.          CEnv.XAxis.AxisColor = 1
  10580.          CEnv.XAxis.AxisTitle.TitleColor = 1
  10581.          CEnv.YAxis.AxisColor = 1
  10582.          CEnv.YAxis.AxisTitle.TitleColor = 1
  10583.          CEnv.Legend.TextColor = 1
  10584.          CEnv.Legend.LegendWindow.Background = 0
  10585.          CEnv.Legend.LegendWindow.BorderColor = 1
  10586.      END IF
  10587.  END SUB
  10588.  
  10589.  '
  10590.  ' Sub Name: InitFonts
  10591.  '
  10592.  ' Description: sets up default font and initializes font list
  10593.  '
  10594.  ' Arguments: None
  10595.  '
  10596.  SUB InitFonts
  10597.  DIM FI AS FontInfo
  10598.  
  10599.      ' reset
  10600.      UnRegisterFonts
  10601.      SetMaxFonts 1, 1
  10602.  
  10603.      ' get default font
  10604.      DefaultFont Segment%, Offset%
  10605.      reg% = RegisterMemFont%(Segment%, Offset%)
  10606.  
  10607.      ' load default font
  10608.      numFonts = LoadFont("n1")
  10609.  
  10610.      IF numFonts = 0 THEN numFonts = 1
  10611.  
  10612.      fonts$(numFonts) = "IBM 8 Point"
  10613.  
  10614.      UnRegisterFonts
  10615.  END SUB
  10616.  
  10617.  '
  10618.  ' Sub Name: InitStyles
  10619.  '
  10620.  ' Description: Initializes border styles list
  10621.  '
  10622.  ' Arguments: None
  10623.  '
  10624.  SUB InitStyles
  10625.  
  10626.      ' create list of border styles
  10627.      styles$(1) = "────────────────"
  10628.      styles$(2) = "────    ────        "
  10629.      styles$(3) = "────         ──      "
  10630.      styles$(4) = "──  ──  ──      ──  "
  10631.      styles$(5) = "──  ─   ──  ─       "
  10632.      styles$(6) = "─── ─── ─── ──    ─ "
  10633.      styles$(7) = "─── ─ ─ ─── ─     ─ "
  10634.      styles$(8) = "──── ── ── ──── "
  10635.      styles$(9) = "──── ── ──── ── "
  10636.      styles$(10) = "──── ─ ─ ── ─     ─ "
  10637.      styles$(11) = "──  ─── ─    ─  ─── "
  10638.      styles$(12) = "─ ─ ─   ─ ─ ─       "
  10639.      styles$(13) = "─ ─ ─ ─ ─ ─ ─     ─ "
  10640.      styles$(14) = "───  ─  ───  ─      "
  10641.      styles$(15) = "──  ─   ─   ─    ─  "
  10642.  
  10643.  END SUB
  10644.  
  10645.  '
  10646.  ' Func Name: Min
  10647.  '
  10648.  ' Description: Compares two numbers and returns the smallest
  10649.  '
  10650.  ' Arguments: num1, num2 - numbers to compare
  10651.  '
  10652.  FUNCTION Min% (num1, num2)
  10653.  
  10654.      IF num1 <= num2 THEN
  10655.          Min% = num1
  10656.      ELSE
  10657.          Min% = num2
  10658.      END IF
  10659.  
  10660.  END FUNCTION
  10661.  
  10662.  '
  10663.  ' Sub Name: Quit
  10664.  '
  10665.  ' Description: Exits the program after allowing the user a chance to
  10666.  '              save the current chart
  10667.  '
  10668.  ' Arguments: None
  10669.  '
  10670.  SUB Quit
  10671.  SHARED finished AS INTEGER, saveFile$, origPath$
  10672.  
  10673.      ' Allow user to save chart if necessary
  10674.      IF chartChanged THEN
  10675.          a$ = "| " + "Current chart has not been saved.  Save now?"
  10676.  
  10677.          status = Alert(4, a$, 8, 15, 12, 65, "Yes", "No", "Cancel")
  10678.  
  10679.          ' save chart
  10680.          IF status = OK THEN
  10681.              status = SaveChart(saveFile$, FALSE)
  10682.          END IF
  10683.      ELSE
  10684.          status = OK
  10685.      END IF
  10686.  
  10687.      ' quit if operation has not been canceled.
  10688.      IF status <> CANCEL THEN
  10689.          CHDRIVE MID$(origPath$, 1, 2)
  10690.          CHDIR MID$(origPath$, 3, LEN(origPath$))
  10691.          finished = TRUE
  10692.          MouseHide
  10693.          COLOR 15, 0
  10694.          CLS
  10695.      END IF
  10696.  
  10697.  END SUB
  10698.  
  10699.  '
  10700.  ' Sub Name: ScrollList
  10701.  '
  10702.  ' Description: Handles scrolling for a list box.
  10703.  '
  10704.  ' Arguments: text$() - list
  10705.  '            tbox - list box
  10706.  '            currButton - current button
  10707.  '            status - to determine if button was pressed, or up or down arrow
  10708.  '                     keys were used
  10709.  '            func - for special operations (passed to DrawList)
  10710.  '            winRow - top row of current window
  10711.  '            winCol - left column of current window
  10712.  '
  10713.  SUB ScrollList (text$(), tbox AS ListBox, currButton, status, func, winRow, w
  10714.  
  10715.      ' scroll using scroll buttons
  10716.      IF currButton = tbox.scrollButton AND status = 1 THEN
  10717.          SELECT CASE Dialog(19)
  10718.              ' scroll up
  10719.              CASE -1:
  10720.                  IF tbox.currTop > 1 THEN
  10721.                      tbox.currTop = tbox.currTop - 1
  10722.                      tbox.currPos = tbox.currPos + 1
  10723.                      IF tbox.currPos > tbox.maxLen THEN tbox.currPos = tbox.ma
  10724.                  END IF
  10725.              ' scroll down
  10726.              CASE -2:
  10727.                  IF tbox.currTop + tbox.maxLen <= tbox.listLen THEN
  10728.                      tbox.currTop = tbox.currTop + 1
  10729.                      tbox.currPos = tbox.currPos - 1
  10730.                      IF tbox.currPos < 1 THEN tbox.currPos = 1
  10731.                  END IF
  10732.              ' scroll to position
  10733.              CASE ELSE:
  10734.                  position = Dialog(19)
  10735.                  IF position > 1 THEN
  10736.                      position = position * (tbox.listLen) / (tbox.boxLen - 2)
  10737.                      IF position < 1 THEN
  10738.                          positon = 1
  10739.                      ELSEIF position > tbox.listLen THEN
  10740.                          position = tbox.listLen
  10741.                      END IF
  10742.                  END IF
  10743.  
  10744.                  IF tbox.currTop <= position AND tbox.currTop + tbox.maxLen >
  10745.                      tbox.currPos = position - tbox.currTop + 1
  10746.                  ELSEIF position <= tbox.maxLen THEN
  10747.                      tbox.currTop = 1
  10748.                      tbox.currPos = position
  10749.                  ELSE
  10750.                      tbox.currTop = position - tbox.maxLen + 1
  10751.                      tbox.currPos = position - tbox.currTop + 1
  10752.                  END IF
  10753.          END SELECT
  10754.  
  10755.      ' area button chosen
  10756.      ELSEIF status = 1 THEN
  10757.          ' make selected position the current position
  10758.          IF Dialog(17) <= tbox.maxLen THEN
  10759.              tbox.currPos = Dialog(17)
  10760.              DrawList text$(), tbox, func
  10761.          END IF
  10762.  
  10763.          ' poll for repeated scrolling while mouse button is down
  10764.          DO
  10765.              X! = TIMER
  10766.              MousePoll r, c, lb, rb              ' poll mouse
  10767.              IF lb = TRUE THEN
  10768.                  ' if below list box then scroll down
  10769.                  IF r > tbox.botRow + winRow - 2 THEN
  10770.                      GOSUB Down1
  10771.                  ' if above list box then scroll up
  10772.                  ELSEIF r < tbox.topRow + winRow THEN
  10773.                      GOSUB Up1
  10774.                  ' if to right of list box then scroll down
  10775.                  ELSEIF c > tbox.rightCol + winCol - 2 THEN
  10776.                      GOSUB Down1
  10777.                  ' if to left of list box then scroll up
  10778.                  ELSEIF c < tbox.leftCol + winCol THEN
  10779.                      GOSUB Up1
  10780.                  ' inside box
  10781.                  ELSEIF r - winRow - tbox.topRow + 1 <= tbox.maxLen THEN
  10782.                      tbox.currPos = r - winRow - tbox.topRow + 1
  10783.                  END IF
  10784.  
  10785.                  ' draw list
  10786.                  DrawList text$(), tbox, func
  10787.              ELSE
  10788.                  EXIT DO
  10789.              END IF
  10790.              WHILE TIMER < X! + .05: WEND
  10791.          LOOP
  10792.  
  10793.      ' up arrow key hit
  10794.      ELSEIF status = 2 THEN
  10795.          GOSUB Up1
  10796.  
  10797.      ' down arrow key hit
  10798.      ELSEIF status = 3 THEN
  10799.          GOSUB Down1
  10800.      END IF
  10801.  
  10802.      DrawList text$(), tbox, func                    ' redraw list
  10803.  
  10804.      EXIT SUB
  10805.  
  10806.  ' scroll list up one
  10807.  Up1:
  10808.      IF tbox.currPos > 1 THEN
  10809.          tbox.currPos = tbox.currPos - 1
  10810.      ELSEIF tbox.currTop > 1 THEN
  10811.          tbox.currTop = tbox.currTop - 1
  10812.      END IF
  10813.  RETURN
  10814.  
  10815.  ' scroll list down one
  10816.  Down1:
  10817.      IF tbox.currPos < tbox.maxLen THEN
  10818.          tbox.currPos = tbox.currPos + 1
  10819.      ELSEIF tbox.currTop + tbox.maxLen <= tbox.listLen THEN
  10820.          tbox.currTop = tbox.currTop + 1
  10821.      END IF
  10822.  RETURN
  10823.  
  10824.  END SUB
  10825.  
  10826.  '
  10827.  ' Sub Name: Setatt
  10828.  '
  10829.  ' Description: Changes a color's attribute to that of another color's.
  10830.  '              This is used in the ChangeTitle routine to allow user
  10831.  '              color selections to immediately change the foreground
  10832.  '              color of the title edit field.  This allows the user
  10833.  '              to view the colors as they would look on a chart
  10834.  '
  10835.  ' Arguments: change - color to change
  10836.  '            source - color to change to
  10837.  '
  10838.  SUB SetAtt (change, source)
  10839.  SHARED screenMode AS INTEGER
  10840.  SHARED egacolor() AS INTEGER
  10841.  
  10842.      ' map colors$ based on screen mode
  10843.      SELECT CASE screenMode
  10844.          CASE 10:
  10845.              IF source > 2 THEN
  10846.                  temp = 9                            ' set "normal" and "blink
  10847.              ELSE
  10848.                  temp = source                       ' off = black; high white
  10849.              END IF
  10850.          CASE 1:
  10851.              IF source = 3 THEN                      ' map to cyan
  10852.                  temp = 13
  10853.              ELSEIF source = 4 THEN                  ' map to magenta
  10854.                  temp = 15
  10855.              ELSE                                    ' others okay
  10856.                  temp = source
  10857.              END IF
  10858.          CASE ELSE
  10859.              temp = source                           ' colors$ okay
  10860.      END SELECT
  10861.  
  10862.      ' change attribute
  10863.      DIM regs AS RegType
  10864.      regs.ax = &H1000
  10865.      regs.bx = 256 * egacolor(TrueColr(temp)) + change
  10866.      CALL INTERRUPT(&H10, regs, regs)
  10867.  
  10868.  END SUB
  10869.  
  10870.  '
  10871.  ' Sub Name: SetDisplayColor
  10872.  '
  10873.  ' Description: Changes the program's display to monochrome (no colors) or
  10874.  '              to color (include colors in menu bar) based on the value of
  10875.  '              colorDisplay.
  10876.  '
  10877.  ' Arguments: none
  10878.  '
  10879.  SUB SetDisplayColor
  10880.  SHARED colorDisplay AS INTEGER
  10881.  
  10882.      MouseHide
  10883.  
  10884.      ' redraw background based on display color
  10885.      SetUpBackground
  10886.  
  10887.      ' set menu bar to include colors
  10888.      IF colorDisplay THEN
  10889.          MenuSetState OPTIONSTITLE, 1, 2
  10890.          MenuSetState OPTIONSTITLE, 2, 1
  10891.          MenuColor 0, 7, 4, 8, 0, 4, 7
  10892.      ' set monochrome menu bar
  10893.      ELSE
  10894.          MenuSetState OPTIONSTITLE, 1, 1
  10895.          MenuSetState OPTIONSTITLE, 2, 2
  10896.          MenuColor 0, 7, 15, 8, 7, 0, 15
  10897.      END IF
  10898.  
  10899.      MenuShow
  10900.      MouseShow
  10901.  
  10902.  END SUB
  10903.  
  10904.  '
  10905.  ' Sub Name: SetUpBackground
  10906.  '
  10907.  ' Description: Creates and displays background screen pattern
  10908.  '
  10909.  ' Arguments: none
  10910.  '
  10911.  SUB SetUpBackground
  10912.  SHARED colorDisplay AS INTEGER
  10913.  
  10914.      MouseHide
  10915.  
  10916.      WIDTH , 25
  10917.      IF colorDisplay THEN
  10918.          COLOR 15, 1                             ' set color for background
  10919.      ELSE
  10920.          COLOR 15, 0
  10921.      END IF
  10922.      CLS
  10923.  
  10924.      FOR a = 2 TO 80 STEP 4                      ' create and display pattern
  10925.          FOR b = 2 TO 25 STEP 2
  10926.              LOCATE b, a
  10927.              PRINT CHR$(250);
  10928.          NEXT b
  10929.      NEXT a
  10930.  
  10931.      MouseShow
  10932.  
  10933.  END SUB
  10934.  
  10935.  '
  10936.  ' Sub Name: SetUpMenu
  10937.  '
  10938.  ' Description: Creates menu bar for the program
  10939.  '
  10940.  ' Arguments: none
  10941.  '
  10942.  SUB SetUpMenu
  10943.  
  10944.      ' file menu title
  10945.      MenuSet FILETITLE, 0, 1, "File", 1
  10946.      MenuSet FILETITLE, 1, 1, "New", 1
  10947.      MenuSet FILETITLE, 2, 1, "Open ...", 1
  10948.      MenuSet FILETITLE, 3, 1, "Save", 1
  10949.      MenuSet FILETITLE, 4, 1, "Save As ...", 6
  10950.      MenuSet FILETITLE, 5, 1, "-", 1
  10951.      MenuSet FILETITLE, 6, 1, "Exit", 2
  10952.  
  10953.      ' view menu title
  10954.      MenuSet VIEWTITLE, 0, 1, "View", 1
  10955.      MenuSet VIEWTITLE, 1, 1, "Data ...", 1
  10956.      MenuSet VIEWTITLE, 2, 1, "Chart        F5", 1
  10957.      MenuSet VIEWTITLE, 3, 1, "Fonts ...", 1
  10958.      MenuSet VIEWTITLE, 4, 1, "Screen Mode ...", 1
  10959.  
  10960.      ' gallery menu title
  10961.      MenuSet GALLERYTITLE, 0, 1, "Gallery", 1
  10962.      MenuSet GALLERYTITLE, 1, 1, "Bar ...", 1
  10963.      MenuSet GALLERYTITLE, 2, 1, "Column ...", 1
  10964.      MenuSet GALLERYTITLE, 3, 1, "Line ...", 1
  10965.      MenuSet GALLERYTITLE, 4, 1, "Scatter ...", 1
  10966.      MenuSet GALLERYTITLE, 5, 1, "Pie ...", 1
  10967.  
  10968.      ' chart menu title
  10969.      MenuSet CHARTTITLE, 0, 1, "Chart", 1
  10970.      MenuSet CHARTTITLE, 1, 1, "Chart Window ...", 1
  10971.      MenuSet CHARTTITLE, 2, 1, "Data Window ...", 1
  10972.      MenuSet CHARTTITLE, 3, 1, "Legend ...", 1
  10973.      MenuSet CHARTTITLE, 4, 1, "X Axis ...", 1
  10974.      MenuSet CHARTTITLE, 5, 1, "Y Axis ...", 1
  10975.  
  10976.      ' title menu title
  10977.      MenuSet TITLETITLE, 0, 1, "Title", 1
  10978.      MenuSet TITLETITLE, 1, 1, "Main ...", 1
  10979.      MenuSet TITLETITLE, 2, 1, "Sub ...", 1
  10980.      MenuSet TITLETITLE, 3, 1, "X Axis ...", 1
  10981.      MenuSet TITLETITLE, 4, 1, "Y Axis ...", 1
  10982.  
  10983.      ' options menu title
  10984.      MenuSet OPTIONSTITLE, 0, 1, "Options", 1
  10985.      MenuSet OPTIONSTITLE, 1, 1, "Color", 1
  10986.      MenuSet OPTIONSTITLE, 2, 1, "Monochrome", 1
  10987.  
  10988.      ' setup short cuts for some menu choices
  10989.      ShortCutKeySet VIEWTITLE, 2, CHR$(0) + CHR$(63)     ' F5 = View Chart
  10990.  
  10991.      ' set original menu colors for monochrome screen
  10992.      MenuColor 0, 7, 15, 8, 7, 0, 15
  10993.      MenuPreProcess
  10994.  
  10995.  END SUB
  10996.  
  10997.  '
  10998.  ' Function Name: TrueColr
  10999.  '
  11000.  ' Description: Maps a given chart color to its actual color
  11001.  '              and returns this color.  This is needed because the chart
  11002.  '              colors start with BLACK = 1 and HIGH WHITE = 2
  11003.  '
  11004.  ' Arguments: colr - chart color number
  11005.  '
  11006.  FUNCTION TrueColr% (colr)
  11007.  
  11008.      IF colr = 1 THEN                                ' black
  11009.          TrueColr% = 0                               ' bright white
  11010.      ELSEIF colr = 2 THEN
  11011.          TrueColr% = 15
  11012.      ELSE
  11013.          TrueColr% = colr - 2                        ' all others
  11014.      END IF
  11015.  
  11016.  END FUNCTION
  11017.  
  11018.  '
  11019.  ' Sub Name: ViewChart
  11020.  '
  11021.  ' Description: Displays the chart
  11022.  '
  11023.  ' Arguments: none
  11024.  '
  11025.  SUB ViewChart
  11026.  SHARED setVal!(), Cat$(), setLen() AS INTEGER, setName$()
  11027.  SHARED screenMode AS INTEGER
  11028.  
  11029.      ' When a chart is drawn, data is moved from the 2-dimensional array
  11030.      ' into arrays suitable for the charting library routines.  The
  11031.      ' following arrays are used directly in calls to the charting routines:
  11032.      DIM ValX1!(1 TO cMaxValues)                    ' pass to chart routine
  11033.      DIM ValY1!(1 TO cMaxValues)
  11034.      DIM ValX2!(1 TO cMaxValues, 1 TO cMaxSeries)   ' pass to chartMS routine
  11035.      DIM ValY2!(1 TO cMaxValues, 1 TO cMaxSeries)
  11036.  
  11037.      DIM explode(1 TO cMaxValues)  AS INTEGER       ' explode pie chart pieces
  11038.  
  11039.  
  11040.     ' Make sure some data exists
  11041.     IF setNum <= 0 THEN
  11042.         a$ = "|"
  11043.         a$ = a$ + "No data available for chart."
  11044.         junk = Alert(4, a$, 8, 15, 12, 65, "", "", "")
  11045.         EXIT SUB
  11046.     END IF
  11047.  
  11048.     ' find the longest series
  11049.     maxLen% = 0
  11050.     FOR i% = 1 TO setNum
  11051.        IF setLen(i%) > maxLen% THEN maxLen% = setLen(i%)
  11052.     NEXT i%
  11053.  
  11054.     ' Set up the proper screen mode (exit if not valid)
  11055.     ChartScreen screenMode
  11056.     IF ChartErr = cBadScreen THEN
  11057.          PrintError "Invalid screen mode. Can't display chart."
  11058.          EXIT SUB
  11059.     END IF
  11060.  
  11061.     ' Process depending on chart type
  11062.     SELECT CASE CEnv.ChartType
  11063.        CASE cBar, cColumn, cLine, cPie:
  11064.           ' If the chart is a single series one or a pie chart:
  11065.           IF setNum = 1 OR CEnv.ChartType = cPie THEN
  11066.  
  11067.              ' Transfer data into a single dimension array:
  11068.              FOR i% = 1 TO maxLen%
  11069.                 ValX1!(i%) = setVal!(i%, 1)
  11070.              NEXT i%
  11071.  
  11072.              IF CEnv.ChartType = cPie THEN
  11073.                  ' determine which pieces to explode
  11074.                  FOR i% = 1 TO maxLen%
  11075.                      IF setVal!(i%, 2) <> 0 THEN
  11076.                          explode(i%) = 1
  11077.                      ELSE
  11078.                          explode(i%) = 0
  11079.                      END IF
  11080.                  NEXT i%
  11081.  
  11082.                  ' display pie chart
  11083.                  ChartPie CEnv, Cat$(), ValX1!(), explode(), maxLen%
  11084.              ELSE
  11085.                  Chart CEnv, Cat$(), ValX1!(), maxLen%
  11086.              END IF
  11087.  
  11088.           ' If multiple series, then data is OK so just call routine:
  11089.           ELSE
  11090.              ChartMS CEnv, Cat$(), setVal!(), maxLen%, 1, setNum, setName$()
  11091.           END IF
  11092.  
  11093.        CASE cScatter:
  11094.           ' Make sure there's enough data sets:
  11095.           IF setNum = 1 THEN
  11096.              SCREEN 0
  11097.              WIDTH 80
  11098.              SetUpBackground
  11099.              MenuShow
  11100.              MouseShow
  11101.              a$ = "|"
  11102.              a$ = a$ + "Too few data sets for Scatter chart"
  11103.              junk = Alert(4, a$, 8, 15, 12, 65, "", "", "")
  11104.              EXIT SUB
  11105.  
  11106.           ' If it's a single series scatter, transfer data to one-
  11107.           ' dimensional arrays and make chart call:
  11108.           ELSEIF setNum = 2 THEN
  11109.              FOR i% = 1 TO maxLen%
  11110.                 ValX1!(i%) = setVal!(i%, 1)
  11111.                 ValY1!(i%) = setVal!(i%, 2)
  11112.              NEXT i%
  11113.              ChartScatter CEnv, ValX1!(), ValY1!(), maxLen%
  11114.  
  11115.           ' If it's a multiple series scatter, transfer odd columns to
  11116.           ' X-axis data array and even columns to Y-axis array and make
  11117.           ' chart call:
  11118.           ELSE
  11119.              FOR j% = 2 TO setNum STEP 2
  11120.                 FOR i% = 1 TO maxLen%
  11121.                    ValX2!(i%, j% \ 2) = setVal!(i%, j% - 1)
  11122.                    ValY2!(i%, j% \ 2) = setVal!(i%, j%)
  11123.                 NEXT i%
  11124.              NEXT j%
  11125.  
  11126.              ChartScatterMS CEnv, ValX2!(), ValY2!(), maxLen%, 1, setNum \ 2,
  11127.           END IF
  11128.  
  11129.     END SELECT
  11130.  
  11131.     ' If there's been a "fatal" error, indicate what it was:
  11132.     IF ChartErr <> 0 THEN
  11133.         GOSUB ViewError
  11134.  
  11135.     ' Otherwise, just wait for a keypress:
  11136.     ELSE
  11137.        ' Wait for keypress
  11138.        DO
  11139.              c$ = INKEY$
  11140.              MousePoll r, c, lb, rb
  11141.        LOOP UNTIL c$ <> "" OR lb OR rb
  11142.        SCREEN 0
  11143.        WIDTH 80
  11144.        SetUpBackground
  11145.        MenuShow
  11146.        MouseShow
  11147.     END IF
  11148.  
  11149.  EXIT SUB
  11150.  
  11151.  ' handle charting errors
  11152.  ViewError:
  11153.  
  11154.      ' re-init the display
  11155.      SCREEN 0
  11156.      WIDTH 80
  11157.      SetUpBackground
  11158.      MenuShow
  11159.      MouseShow
  11160.  
  11161.      ' display appropriate error message
  11162.      SELECT CASE ChartErr
  11163.          CASE cBadDataWindow:
  11164.              PrintError "Data window cannot be displayed in available space."
  11165.          CASE cBadLegendWindow:
  11166.              PrintError "Invalid legend coordinates."
  11167.          CASE cTooFewSeries:
  11168.              PrintError "Too few series to plot."
  11169.          CASE cTooSmallN:
  11170.              PrintError "No data in series."
  11171.          CASE IS > 200:                              ' basic error
  11172.              PrintError "BASIC error #" + LTRIM$(STR$(ChartErr - 200)) + " occ
  11173.          CASE ELSE:                                  ' extraneous error
  11174.              PrintError "Charting error #" + LTRIM$(STR$(ChartErr)) + " occurr
  11175.      END SELECT
  11176.  
  11177.  RETURN
  11178.  
  11179.  END SUB
  11180.  
  11181.  '
  11182.  ' Sub Name: ViewFont
  11183.  '
  11184.  ' Description: Displays list of registered fonts and allows user to
  11185.  '              select one or more of these fonts to load
  11186.  '
  11187.  ' Arguments: none
  11188.  '
  11189.  SUB ViewFont
  11190.  SHARED screenMode AS INTEGER
  11191.  SHARED origPath$
  11192.  DIM FI AS FontInfo
  11193.  DIM rfonts$(1 TO MAXFONTS)
  11194.  
  11195.      SetMaxFonts MAXFONTS, MAXFONTS
  11196.  
  11197.      ' get default font
  11198.      DefaultFont Segment%, Offset%
  11199.      numReg = RegisterMemFont%(Segment%, Offset%)
  11200.  
  11201.      ' use font files that are best suited for current screen mode
  11202.      IF MID$(origPath$, LEN(origPath$), 1) = "\" THEN
  11203.          t$ = ""
  11204.      ELSE
  11205.          t$ = "\"
  11206.      END IF
  11207.      SELECT CASE screenMode
  11208.          CASE 2, 8
  11209.              cour$ = origPath$ + t$ + "COURA.FON"
  11210.              helv$ = origPath$ + t$ + "HELVA.FON"
  11211.              tims$ = origPath$ + t$ + "TMSRA.FON"
  11212.          CASE 11, 12
  11213.              cour$ = origPath$ + t$ + "COURE.FON"
  11214.              helv$ = origPath$ + t$ + "HELVE.FON"
  11215.              tims$ = origPath$ + t$ + "TMSRE.FON"
  11216.          CASE ELSE
  11217.              cour$ = origPath$ + t$ + "COURB.FON"
  11218.              helv$ = origPath$ + t$ + "HELVB.FON"
  11219.              tims$ = origPath$ + t$ + "TMSRB.FON"
  11220.      END SELECT
  11221.      ' register courier fonts
  11222.      numReg = numReg + RegisterFonts%(cour$)
  11223.      fontname$ = cour$
  11224.      IF FontErr > 0 THEN GOSUB FontError
  11225.  
  11226.      ' register helvetica fonts
  11227.      numReg = numReg + RegisterFonts%(helv$)
  11228.      fontname$ = helv$
  11229.      IF FontErr > 0 THEN GOSUB FontError
  11230.  
  11231.      ' register times roman fonts
  11232.      numReg = numReg + RegisterFonts%(tims$)
  11233.      fontname$ = tims$
  11234.      IF FontErr > 0 THEN GOSUB FontError
  11235.  
  11236.      ' create a list of registered fonts
  11237.      FOR i = 1 TO numReg
  11238.          GetRFontInfo i, FI
  11239.          rfonts$(i) = RTRIM$(MID$(FI.FaceName, 1, 8)) + STR$(FI.Points) + " Po
  11240.      NEXT i
  11241.  
  11242.      ' set up window display
  11243.      winRow = 5
  11244.      winCol = 25
  11245.      WindowOpen 1, winRow, winCol, winRow + numReg + 1, 51, 0, 7, 0, 7, 15, FA
  11246.  
  11247.      ' open buttons for each font in list
  11248.      FOR i% = 1 TO numReg
  11249.          ButtonOpen i, 1, rfonts$(i), i, 4, 0, 0, 2
  11250.          FOR j% = 1 TO numFonts
  11251.              IF fonts$(j%) = rfonts$(i%) THEN ButtonSetState i, 2
  11252.          NEXT j%
  11253.      NEXT i%
  11254.  
  11255.      WindowLine numReg + 1
  11256.      ButtonOpen numReg + 1, 2, "Load", numReg + 2, 4, 0, 0, 1
  11257.      ButtonOpen numReg + 2, 1, "Cancel ", numReg + 2, 15, 0, 0, 1
  11258.  
  11259.      ' start with cursor on first button
  11260.      currButton = 1
  11261.      pushButton = numReg + 1
  11262.  
  11263.      ' window control loop
  11264.      finished = FALSE
  11265.      WHILE NOT finished
  11266.          WindowDo currButton, 0
  11267.          SELECT CASE Dialog(0)
  11268.              CASE 1                                     ' button pressed
  11269.                  currButton = Dialog(1)
  11270.                  IF currButton > numReg THEN
  11271.                      pushButton = currButton
  11272.                      finished = TRUE
  11273.                  ELSE
  11274.                      ButtonToggle currButton
  11275.                  END IF
  11276.              CASE 6                                      ' enter
  11277.                  finished = TRUE
  11278.              CASE 7                                      ' tab
  11279.                  SELECT CASE currButton
  11280.                      CASE numReg, numReg + 1:
  11281.                          currButton = currButton + 1
  11282.                          ButtonSetState pushButton, 1
  11283.                          ButtonSetState currButton, 2
  11284.                          pushButton = currButton
  11285.                      CASE numReg + 2:
  11286.                          currButton = 1
  11287.                          ButtonSetState pushButton, 1
  11288.                          pushButton = numReg + 1
  11289.                          ButtonSetState pushButton, 2
  11290.                      CASE ELSE:
  11291.                          currButton = currButton + 1
  11292.                  END SELECT
  11293.              CASE 8                                      ' back tab
  11294.                  SELECT CASE currButton
  11295.                      CASE 1:
  11296.                          currButton = numReg + 2
  11297.                          ButtonSetState pushButton, 1
  11298.                          ButtonSetState currButton, 2
  11299.                          pushButton = currButton
  11300.                      CASE numReg + 2:
  11301.                          currButton = numReg + 1
  11302.                          ButtonSetState pushButton, 1
  11303.                          ButtonSetState currButton, 2
  11304.                          pushButton = currButton
  11305.                      CASE ELSE:
  11306.                          currButton = currButton - 1
  11307.                      END SELECT
  11308.              CASE 9                                      ' escape
  11309.                  pushButton = numReg + 2
  11310.                  finished = TRUE
  11311.              CASE 10, 12                                 ' up, left arrow
  11312.                  IF currButton <= numReg THEN ButtonSetState currButton, 2
  11313.              CASE 11, 13                                 ' down, right arrow
  11314.                  IF currButton <= numReg THEN ButtonSetState currButton, 1
  11315.              CASE 14                                     ' space bar
  11316.                  IF currButton <= numReg THEN
  11317.                      ButtonToggle currButton
  11318.                  ELSE
  11319.                      finished = TRUE
  11320.                  END IF
  11321.          END SELECT
  11322.  
  11323.      ' finished and not cancelled
  11324.      IF finished AND pushButton = numReg + 1 THEN
  11325.          ' create font spec for load operation
  11326.          FontSpec$ = ""
  11327.          FOR i% = 1 TO numReg
  11328.              IF ButtonInquire(i) = 2 THEN
  11329.                  FontSpec$ = FontSpec$ + "/n" + LTRIM$(STR$(i))
  11330.              END IF
  11331.          NEXT i%
  11332.  
  11333.          ' default if none chosen
  11334.          IF FontSpec$ = "" THEN
  11335.              PrintError "No fonts selected - using default."
  11336.              numFonts = LoadFont%("N1")
  11337.              REDIM fonts$(1)
  11338.              fonts$(1) = rfonts$(1)
  11339.          ELSE
  11340.              ' load selected fonts
  11341.              numLoaded = LoadFont%(RIGHT$(FontSpec$, LEN(FontSpec$) - 1))
  11342.  
  11343.              ' notify user of error and let them try again.
  11344.              IF FontErr <> 0 THEN
  11345.                  GOSUB FontError
  11346.                  finished = FALSE
  11347.                  currButton = 1
  11348.              ELSE
  11349.                  REDIM fonts$(numLoaded)
  11350.                  ' create a list of loaded fonts
  11351.                  FOR i = 1 TO numLoaded
  11352.                      SelectFont i
  11353.                      GetFontInfo FI
  11354.                      fonts$(i) = RTRIM$(MID$(FI.FaceName, 1, 8)) + STR$(FI.Poi
  11355.                  NEXT i
  11356.                  numFonts = numLoaded
  11357.                  ClearFonts
  11358.              END IF
  11359.          END IF
  11360.      ' reload existing fonts if operation cancelled
  11361.      ELSEIF finished = TRUE AND pushButton = numReg + 2 THEN
  11362.          FontSpec$ = ""
  11363.          FOR i = 1 TO numReg
  11364.              FOR j% = 1 TO numFonts
  11365.                  IF fonts$(j%) = rfonts$(i%) THEN FontSpec$ = FontSpec$ + "/n"
  11366.              NEXT j%
  11367.          NEXT i
  11368.          numFonts = LoadFont%(RIGHT$(FontSpec$, LEN(FontSpec$) - 1))
  11369.      END IF
  11370.  
  11371.      WEND
  11372.  
  11373.      UnRegisterFonts
  11374.  
  11375.      WindowClose 1
  11376.  
  11377.      EXIT SUB
  11378.  
  11379.  ' handle font loading errors
  11380.  FontError:
  11381.      SELECT CASE FontErr
  11382.          CASE cNoFontMem:
  11383.              PrintError "Not enough memory to load selected fonts."
  11384.          CASE cFileNotFound:
  11385.              PrintError fontname$ + " font file not found."
  11386.          CASE cTooManyFonts:
  11387.              numReg = MAXFONTS
  11388.          CASE cBadFontFile:
  11389.              PrintError "Invalid font file format for " + fontname$ + "."
  11390.          CASE cNoFonts:
  11391.              PrintError "No fonts are loaded."
  11392.          CASE cBadFontType:
  11393.              PrintError "Font not a bitmap font."
  11394.          CASE IS > 200:                                  ' basic error
  11395.              PrintError "BASIC error #" + LTRIM$(STR$(FontErr - 200)) + " occu
  11396.          CASE ELSE                                       ' unplanned font erro
  11397.              PrintError "Font error #" + LTRIM$(STR$(FontErr)) + " occurred."
  11398.      END SELECT
  11399.  
  11400.  RETURN
  11401.  
  11402.  END SUB
  11403.  
  11404.  '
  11405.  ' Sub Name: ViewScreenMode
  11406.  '
  11407.  ' Description: Displays list of valid screen modes and allows the
  11408.  '              user to select one for viewing the chart
  11409.  '
  11410.  ' Arguments: none
  11411.  '
  11412.  SUB ViewScreenMode
  11413.  SHARED screenMode AS INTEGER, numModes AS INTEGER, mode$()
  11414.  
  11415.  DIM modeBox AS ListBox
  11416.  
  11417.      ' set up list box containing valid screen modes
  11418.      modeBox.scrollButton = 1
  11419.      modeBox.areaButton = 2
  11420.      modeBox.listLen = numModes
  11421.      modeBox.topRow = 1
  11422.      modeBox.botRow = numModes + 2
  11423.      modeBox.leftCol = 7
  11424.      modeBox.rightCol = 21
  11425.  
  11426.      ' determine current screen mode
  11427.      FOR i = 1 TO numModes
  11428.          IF screenMode = VAL(mode$(i)) THEN modeBox.listPos = i
  11429.      NEXT i
  11430.  
  11431.      ' set up display window
  11432.      winRow = 6
  11433.      winCol = 25
  11434.      WindowOpen 1, winRow, winCol, winRow + numModes + 3, 51, 0, 7, 0, 7, 15,
  11435.      WindowLine numModes + 3
  11436.  
  11437.      ' create the list box
  11438.      CreateListBox mode$(), modeBox, 0
  11439.  
  11440.      ' open command buttons
  11441.      ButtonOpen 3, 2, "OK ", numModes + 4, 4, 0, 0, 1
  11442.      ButtonOpen 4, 1, "Cancel ", numModes + 4, 16, 0, 0, 1
  11443.  
  11444.  
  11445.          a$ = "Screen Mode Warning ||"
  11446.          a$ = a$ + "Selecting screen modes that support less than |"
  11447.          a$ = a$ + "than 16 colors will reset all chart colors to |"
  11448.          a$ = a$ + "their black and white defaults.               |"
  11449.          a$ = a$ + "|" + " Fonts should be reloaded after screen mode is   |"
  11450.          a$ = a$ + " changed to ensure best font match for screen   |"
  11451.          a$ = a$ + " resolution.                                     "
  11452.          junk = Alert(4, a$, 6, 15, 16, 65, "", "", "")
  11453.  
  11454.  
  11455.      ' start with cursor in area button
  11456.      currButton = 2
  11457.      pushButton = 3
  11458.  
  11459.      ' window control loop
  11460.      finished = FALSE
  11461.      WHILE NOT finished
  11462.          WindowDo currButton, 0                         ' wait for event
  11463.          SELECT CASE Dialog(0)
  11464.              CASE 1                                     ' button pressed
  11465.                  currButton = Dialog(1)
  11466.                  SELECT CASE currButton
  11467.                      CASE 1, 2:
  11468.                          ScrollList mode$(), modeBox, currButton, 1, 0, winRow
  11469.                          currButton = 2
  11470.                      CASE 3, 4:
  11471.                          pushButton = currButton
  11472.                          finished = TRUE
  11473.                  END SELECT
  11474.              CASE 6                                      ' enter
  11475.                  finished = TRUE
  11476.              CASE 7                                      ' tab
  11477.                  SELECT CASE currButton
  11478.                      CASE 1, 2:
  11479.                          currButton = 3
  11480.                          ButtonSetState pushButton, 1
  11481.                          ButtonSetState currButton, 2
  11482.                          pushButton = 3
  11483.                      CASE 3:
  11484.                          currButton = 4
  11485.                          ButtonSetState pushButton, 1
  11486.                          ButtonSetState currButton, 2
  11487.                          pushButton = 4
  11488.                      CASE 4:
  11489.                          ButtonSetState currButton, 1
  11490.                          currButton = 2
  11491.                          pushButton = 3
  11492.                          ButtonSetState pushButton, 2
  11493.                  END SELECT
  11494.              CASE 8                                      ' back tab
  11495.                  SELECT CASE currButton
  11496.                      CASE 1, 2:
  11497.                          currButton = 4
  11498.                          ButtonSetState pushButton, 1
  11499.                          ButtonSetState currButton, 2
  11500.                          pushButton = 4
  11501.                      CASE 3: currButton = 2
  11502.                      CASE 4:
  11503.                          currButton = 3
  11504.                          ButtonSetState pushButton, 1
  11505.                          ButtonSetState currButton, 2
  11506.                          pushButton = 3
  11507.                      END SELECT
  11508.              CASE 9                                      ' escape
  11509.                  pushButton = 4
  11510.                  finished = TRUE
  11511.              CASE 10, 12                                 ' up, left arrow
  11512.                  SELECT CASE currButton
  11513.                      CASE 1, 2: ScrollList mode$(), modeBox, currButton, 2, 0,
  11514.                  END SELECT
  11515.              CASE 11, 13                                 ' down, right arrow
  11516.                  SELECT CASE currButton
  11517.                      CASE 1, 2: ScrollList mode$(), modeBox, currButton, 3, 0,
  11518.                  END SELECT
  11519.              CASE 14                                     ' space bar
  11520.                  IF currButton > 2 THEN finished = TRUE
  11521.          END SELECT
  11522.      WEND
  11523.  
  11524.      ' if not canceled
  11525.      IF pushButton = 3 THEN
  11526.          ' change screen mode
  11527.          IF screenMode <> VAL(mode$(modeBox.listPos)) THEN
  11528.              IF setNum > 0 THEN chartChanged = TRUE
  11529.  
  11530.              screenMode = VAL(mode$(modeBox.listPos))
  11531.  
  11532.              ' reset window coords
  11533.              CEnv.ChartWindow.X1 = 0
  11534.              CEnv.ChartWindow.Y1 = 0
  11535.              CEnv.ChartWindow.X2 = 0
  11536.              CEnv.ChartWindow.Y2 = 0
  11537.  
  11538.              ' change color list based on new screen mode
  11539.              InitColors
  11540.          END IF
  11541.      END IF
  11542.  
  11543.      WindowClose 1
  11544.  
  11545.  END SUB
  11546.  
  11547.  
  11548.  
  11549.  CHRTDEMO.BAS
  11550.  CD-ROM Disc Path:   \SAMPCODE\BASIC\CHRTDEMO.BAS
  11551.  
  11552.  '       CHRTDEMO.BAS - Main module of CHRTB demonstration program
  11553.  '
  11554.  '             Copyright (C) 1989, Microsoft Corporation
  11555.  '
  11556.  '   This demo program uses the Presentation Graphics and User Interface
  11557.  '   toolboxes to implement a general purpose charting package.
  11558.  '   It consists of three modules (CHRTDEMO.BAS, CHRTDEM1.BAS and CHRTDEM2.BAS
  11559.  '   and one include file (CHRTDEMO.BI).  It requires access to both the
  11560.  '   Presentation Graphics and User Interface toolboxes.
  11561.  '
  11562.  '   EMS is needed to load and run the demo under QBX.  If you do not
  11563.  '   have EMS, refer to the command line compile instructions below which
  11564.  '   will allow you to run the demo from the DOS prompt.  Running the
  11565.  '   demo under QBX requires access to the Presentation Graphics and User
  11566.  '   Interface toolboxes.  This can be done in one of two methods:
  11567.  '       1) One large QuickLib covering both toolboxes can be created.  The
  11568.  '          library "CHRTDEM.LIB" and QuickLib "CHRTDEM.QLB" are created
  11569.  '          as follows:
  11570.  '           BC /X/FS chrtb.bas;
  11571.  '           BC /X/FS fontb.bas;
  11572.  '           LIB chrtdem.lib + uitbefr.lib + fontasm + chrtasm + fontb + chrtb
  11573.  '           LINK /Q chrtdem.lib, chrtdem.qlb,,qbxqlb.lib;
  11574.  '          Once created, just start QBX with this QuickLib and load the
  11575.  '          demo's modules (chrtdemo.bas, chrtdem1.bas and chrtdem2.bas).
  11576.  '
  11577.  '       2) Either the Presentation Graphics or User Interface QuickLib
  11578.  '          may be used alone provided the other's source code files
  11579.  '          are loaded into the QBX environment.  If CHRTBEFR.QLB is
  11580.  '          is used then WINDOW.BAS, GENERAL.BAS, MENU.BAS and MOUSE.BAS
  11581.  '          must be loaded.  If UITBEFR.QLB is used then CHRTB.BAS and
  11582.  '          FONTB.BAS must be loaded.  Once a QuickLib is specified and
  11583.  '          all necessary source files are loaded, load the program
  11584.  '          modules (chrtdemo.bas, chrtdem1.bas and chrtdem2.bas)
  11585.  '
  11586.  '   To create a compiled version of the chart demo program perform the
  11587.  '   following steps:
  11588.  '       BC /X/FS chrtb.bas;
  11589.  '       BC /X/FS fontb.bas;
  11590.  '       LIB chrtdem.lib + uitbefr.lib + fontasm + chrtasm + fontb + chrtb;
  11591.  '       BC /X/FS chrtdemo.bas;
  11592.  '       BC /FS chrtdem1.bas;
  11593.  '       BC /FS chrtdem2.bas;
  11594.  '       LINK /EX chrtdemo chrtdem1 chrtdem2, chrtdemo.exe,, chrtdem.lib;
  11595.  '   "CHRTDEMO" can now be run from the command line.
  11596.  '
  11597.  '
  11598.  DEFINT A-Z
  11599.  
  11600.  '$INCLUDE: 'chrtdemo.bi'
  11601.  
  11602.  ' local functions
  11603.  DECLARE FUNCTION GetLoadFile% (FileName$)
  11604.  DECLARE FUNCTION GetSaveFile% (FileName$)
  11605.  DECLARE FUNCTION GetFileCount% (fileSpec$)
  11606.  
  11607.  ' local subs
  11608.  DECLARE SUB LoadChart (fileNum%)
  11609.  DECLARE SUB ShowError (errorNum%)
  11610.  
  11611.  
  11612.  ' necessary variables for the toolboxes
  11613.  DIM GloTitle(MAXMENU)           AS MenuTitleType
  11614.  DIM GloItem(MAXMENU, MAXITEM)   AS MenuItemType
  11615.  DIM GloWindow(MAXWINDOW)        AS windowType
  11616.  DIM GloButton(MAXBUTTON)        AS buttonType
  11617.  DIM GloEdit(MAXEDITFIELD)       AS EditFieldType
  11618.  DIM GloWindowStack(MAXWINDOW)   AS INTEGER
  11619.  DIM GloBuffer$(MAXWINDOW + 1, 2)
  11620.  
  11621.  ' variables shared across modules
  11622.  DIM colors$(1 TO MAXCOLORS)                     'valid colors$
  11623.  DIM styles$(1 TO MAXSTYLES)                     'border style list
  11624.  DIM fonts$(1 TO MAXFONTS)                       'fonts list
  11625.  DIM Cat$(1 TO cMaxValues)                       'category names
  11626.  DIM setName$(1 TO cMaxSets)                     'set names
  11627.  DIM setLen(1 TO cMaxSets)   AS INTEGER          '# values per set
  11628.  DIM setVal!(1 TO cMaxValues, 1 TO cMaxSets)     ' actual values
  11629.  DIM mode$(1 TO 13)                              'list of modes
  11630.  
  11631.  
  11632.      ' set up main error handler
  11633.      ON ERROR GOTO ErrorHandle
  11634.  
  11635.      ' initialize the program
  11636.      InitAll
  11637.  
  11638.      ' Main loop
  11639.      WHILE NOT finished
  11640.          kbd$ = MenuInkey$
  11641.          WHILE MenuCheck(2)
  11642.              HandleMenuEvent
  11643.          WEND
  11644.      WEND
  11645.  
  11646.      END
  11647.  
  11648.  'catch all error handler
  11649.  ErrorHandle:
  11650.      ShowError ERR
  11651.      WindowClose 1                               ' close any active windows
  11652.      WindowClose 2
  11653.  RESUME NEXT
  11654.  
  11655.  '
  11656.  ' Function Name: GetBestMode
  11657.  '
  11658.  ' Description: Creates a list of valid screen modes for use by charting funct
  11659.  '              and sets the initial screen mode to the highest resolution
  11660.  '              possible.  If no graphic screen modes are available then
  11661.  '              it causes the program to exit.
  11662.  '
  11663.  ' Arguments: screenMode
  11664.  '
  11665.  SUB GetBestMode (screenMode)
  11666.  SHARED mode$(), numModes AS INTEGER
  11667.  
  11668.  ON LOCAL ERROR GOTO badmode                     ' trap screen mode errors
  11669.  
  11670.      ' test all possible screen modes creating a list of valid ones as we go
  11671.      numModes = 0
  11672.      FOR i = 13 TO 1 STEP -1
  11673.          valid = TRUE
  11674.          SCREEN i
  11675.          IF valid THEN
  11676.              numModes = numModes + 1
  11677.              mode$(numModes) = LTRIM$(STR$(i))
  11678.          END IF
  11679.      NEXT i
  11680.  
  11681.      ' exit if no modes available
  11682.      IF numModes = 0 THEN
  11683.          screenMode = 0
  11684.      ' set current screen mode to best possible
  11685.      ELSEIF mode$(1) = "13" THEN
  11686.          screenMode = VAL(mode$(2))
  11687.      ELSE
  11688.          screenMode = VAL(mode$(1))
  11689.      END IF
  11690.  
  11691.  EXIT SUB
  11692.  
  11693.  badmode:
  11694.      valid = FALSE
  11695.  RESUME NEXT
  11696.  
  11697.  END SUB
  11698.  
  11699.  '
  11700.  ' Func Name: GetFileCount
  11701.  '
  11702.  ' Description: Returns number of DOS files matching a given file spec
  11703.  '
  11704.  ' Arguments: fileSpec$ - DOS file spec  (i.e. "*.*")
  11705.  '
  11706.  FUNCTION GetFileCount% (fileSpec$)
  11707.  
  11708.  ON LOCAL ERROR GOTO GetCountError
  11709.  
  11710.      count = 0
  11711.  
  11712.      FileName$ = DIR$(fileSpec$)             ' Get first match if any
  11713.  
  11714.      DO WHILE FileName$ <> ""                ' continue until no more matches
  11715.          count = count + 1
  11716.          FileName$ = DIR$
  11717.      LOOP
  11718.  
  11719.      GetFileCount = count                    ' return count
  11720.  
  11721.      EXIT FUNCTION
  11722.  
  11723.  GetCountError:
  11724.  
  11725.      ShowError ERR                               ' display error message
  11726.  
  11727.  RESUME NEXT
  11728.  
  11729.  END FUNCTION
  11730.  
  11731.  '
  11732.  ' Func Name: GetLoadFile
  11733.  '
  11734.  ' Description: Called by OpenChart, this prompts the user for a
  11735.  '              DOS file to open.  It returns the file number of
  11736.  '              the chart file with the actual file name being
  11737.  '              passed back via the argument.
  11738.  '
  11739.  ' Arguments: FileName$ - name of file to open
  11740.  '
  11741.  FUNCTION GetLoadFile% (FileName$)
  11742.  DIM fileList$(1 TO 10)
  11743.  DIM fileBox AS ListBox
  11744.  
  11745.  ON LOCAL ERROR GOTO GetLoadError                ' handle file opening errors
  11746.  
  11747.      fileSpec$ = "*.CHT"                         ' default file spec
  11748.      origDir$ = CURDIR$
  11749.      origPos = 0                                 ' no file list element select
  11750.  
  11751.      ' get list of files matching spec
  11752.      fileCount = GetFileCount(fileSpec$)
  11753.      IF fileCount THEN
  11754.          REDIM fileList$(fileCount)
  11755.      END IF
  11756.      fileList$(1) = DIR$(fileSpec$)
  11757.      FOR i% = 2 TO fileCount
  11758.          fileList$(i%) = DIR$
  11759.      NEXT i%
  11760.  
  11761.      ' set up list box for file list
  11762.      fileBox.scrollButton = 1
  11763.      fileBox.areaButton = 2
  11764.      fileBox.listLen = fileCount
  11765.      fileBox.topRow = 8
  11766.      fileBox.botRow = 14
  11767.      fileBox.leftCol = 7
  11768.      fileBox.rightCol = 22
  11769.      fileBox.listPos = origPos
  11770.  
  11771.      ' create window for display
  11772.      winRow = 6
  11773.      winCol = 25
  11774.      WindowOpen 1, winRow, winCol, 21, 52, 0, 7, 0, 7, 15, FALSE, FALSE, FALSE
  11775.      WindowLocate 2, 2
  11776.      WindowPrint 2, "File Name:"
  11777.      WindowBox 1, 13, 3, 27
  11778.      WindowLocate 5, 2
  11779.      WindowPrint -1, origDir$
  11780.      WindowLocate 7, 11
  11781.      WindowPrint 2, "Files"
  11782.      WindowLine 15
  11783.  
  11784.      ' create list box for file list
  11785.      CreateListBox fileList$(), fileBox, 5
  11786.  
  11787.      ' open edit field for file spec
  11788.      EditFieldOpen 1, fileSpec$, 2, 14, 0, 7, 13, 70
  11789.  
  11790.      ' open command buttons
  11791.      ButtonOpen 3, 2, "OK", 16, 5, 0, 0, 1
  11792.      ButtonOpen 4, 1, "Cancel", 16, 15, 0, 0, 1
  11793.  
  11794.      ' start with cursor in edit field
  11795.      currButton = 0
  11796.      currEditField = 1
  11797.      pushButton = 3
  11798.  
  11799.      ' control loop
  11800.      finished = FALSE
  11801.      WHILE NOT finished
  11802.          WindowDo currButton, currEditField              ' wait for event
  11803.          SELECT CASE Dialog(0)
  11804.              CASE 1                                      ' button pressed
  11805.                  currButton = Dialog(1)
  11806.                  SELECT CASE currButton
  11807.                      CASE 1, 2: currEditField = 0
  11808.                          ScrollList fileList$(), fileBox, currButton, 1, 0, wi
  11809.                          currButton = 2
  11810.                      CASE 3, 4: pushButton = currButton
  11811.                          finished = TRUE
  11812.                  END SELECT
  11813.              CASE 2                                      ' Edit Field
  11814.                  currButton = 0
  11815.                  currEditField = 1
  11816.              CASE 6                                      ' enter
  11817.                  IF INSTR(EditFieldInquire$(1), "*") = 0 THEN finished = TRUE
  11818.              CASE 7                                      ' tab
  11819.                  SELECT CASE currButton
  11820.                      CASE 0: currButton = 2
  11821.                          currEditField = 0
  11822.                      CASE 1, 2:
  11823.                          currButton = 3
  11824.                          ButtonSetState 3, 2
  11825.                          ButtonSetState 4, 1
  11826.                          pushButton = 3
  11827.                      CASE 3:
  11828.                          currButton = 4
  11829.                          ButtonSetState 3, 1
  11830.                          ButtonSetState 4, 2
  11831.                          pushButton = 4
  11832.                      CASE 4:
  11833.                          currButton = 0
  11834.                          currEditField = 1
  11835.                          ButtonSetState 3, 2
  11836.                          ButtonSetState 4, 1
  11837.                          pushButton = 3
  11838.                  END SELECT
  11839.              CASE 8                                      ' back tab
  11840.                  SELECT CASE currButton
  11841.                      CASE 0: currButton = 4
  11842.                          currEditField = 0
  11843.                          ButtonSetState 3, 1
  11844.                          ButtonSetState 4, 2
  11845.                          pushButton = 4
  11846.                      CASE 1, 2:
  11847.                          currButton = 0
  11848.                          currEditField = 1
  11849.                      CASE 3:
  11850.                          currButton = 2
  11851.                      CASE 4:
  11852.                          currButton = 3
  11853.                          ButtonSetState 3, 2
  11854.                          ButtonSetState 4, 1
  11855.                          pushButton = 3
  11856.                  END SELECT
  11857.              CASE 9                                      ' escape
  11858.                  pushButton = 4
  11859.                  finished = TRUE
  11860.              CASE 10, 12                                 ' up, left arrow
  11861.                  IF currButton = 1 OR currButton = 2 THEN ScrollList fileList$
  11862.              CASE 11, 13                                 'down, right arrow
  11863.                  IF currButton = 1 OR currButton = 2 THEN ScrollList fileList$
  11864.              CASE 14                                     ' space bar
  11865.                  IF currButton > 2 THEN
  11866.                      pushButton = currButton
  11867.                      finished = TRUE
  11868.                  END IF
  11869.          END SELECT
  11870.  
  11871.          temp$ = EditFieldInquire$(1)
  11872.  
  11873.          ' simple error checking before finishing
  11874.          IF finished AND pushButton <> 4 THEN
  11875.              ' invalid file specified
  11876.              IF INSTR(temp$, "*") THEN
  11877.                  PrintError "Invalid file specification."
  11878.                  finished = FALSE
  11879.              ELSEIF LEN(temp$) = 0 THEN
  11880.                  PrintError "Must specify a name."
  11881.                  finished = FALSE
  11882.              ELSE
  11883.                  fileSpec$ = temp$
  11884.                  fileNum% = FREEFILE
  11885.                  OPEN fileSpec$ FOR INPUT AS fileNum%
  11886.  
  11887.              END IF
  11888.          END IF
  11889.  
  11890.          ' more processing to do
  11891.          IF NOT finished THEN
  11892.              ' update edit field display based on list box selection
  11893.              IF fileBox.listPos <> origPos THEN
  11894.                  fileSpec$ = fileList$(fileBox.listPos)
  11895.                  origPos = fileBox.listPos
  11896.                  EditFieldClose 1
  11897.                  EditFieldOpen 1, fileSpec$, 2, 14, 0, 7, 13, 70
  11898.              ' update list box contents based on new edit field contents
  11899.              ELSEIF LTRIM$(RTRIM$(fileSpec$)) <> LTRIM$(RTRIM$(temp$)) THEN
  11900.                  fileSpec$ = UCASE$(temp$)
  11901.                  IF fileSpec$ <> "" THEN
  11902.                      IF MID$(fileSpec$, 2, 1) = ":" THEN
  11903.                          CHDRIVE MID$(fileSpec$, 1, 2)
  11904.                          fileSpec$ = MID$(fileSpec$, 3, LEN(fileSpec$))
  11905.                      END IF
  11906.                      position = 0
  11907.                      WHILE INSTR(position + 1, fileSpec$, "\") <> 0
  11908.                          position = INSTR(position + 1, fileSpec$, "\")
  11909.                      WEND
  11910.                      IF position = 1 THEN
  11911.                          CHDIR "\"
  11912.                      ELSEIF position > 0 THEN
  11913.                          CHDIR LEFT$(fileSpec$, position - 1)
  11914.                      END IF
  11915.                      fileSpec$ = MID$(fileSpec$, position + 1, LEN(fileSpec$))
  11916.                      WindowLocate 5, 2
  11917.                      IF LEN(CURDIR$) > 26 THEN
  11918.                          direct$ = LEFT$(CURDIR$, 26)
  11919.                      ELSE
  11920.                          direct$ = CURDIR$
  11921.                      END IF
  11922.                      WindowPrint -1, direct$ + STRING$(26 - LEN(direct$), " ")
  11923.  
  11924.                      fileCount = GetFileCount(fileSpec$)
  11925.                  ELSE
  11926.                      fileCount = 0
  11927.                  END IF
  11928.  
  11929.                  EditFieldClose 1
  11930.                  EditFieldOpen 1, fileSpec$, 2, 14, 0, 7, 13, 70
  11931.  
  11932.                  fileBox.listLen = fileCount
  11933.                  fileBox.maxLen = Min(fileCount, fileBox.boxLen)
  11934.                  origPos = 0
  11935.                  fileBox.listPos = origPos
  11936.                  fileBox.currTop = 1
  11937.                  fileBox.currPos = 0
  11938.                  ' get new file list
  11939.                  IF fileCount = 0 THEN
  11940.                      REDIM fileList$(10)
  11941.                  ELSE
  11942.                      REDIM fileList$(fileCount)
  11943.                      fileList$(1) = DIR$(fileSpec$)
  11944.                      FOR i% = 2 TO fileCount
  11945.                          fileList$(i%) = DIR$
  11946.                      NEXT i%
  11947.                  END IF
  11948.  
  11949.                  DrawList fileList$(), fileBox, 0   ' redraw file list
  11950.              END IF
  11951.          END IF
  11952.      WEND
  11953.  
  11954.      ' if operation not canceled return file name and file number
  11955.      IF pushButton = 3 THEN
  11956.          FileName$ = fileSpec$
  11957.          GetLoadFile% = fileNum%
  11958.      ELSE
  11959.          GetLoadFile% = 0
  11960.  
  11961.          CHDRIVE MID$(origDir$, 1, 2)
  11962.          CHDIR MID$(origDir$, 3, LEN(origDir$))
  11963.      END IF
  11964.  
  11965.      WindowClose 1
  11966.  
  11967.      EXIT FUNCTION
  11968.  
  11969.  ' handle any file opening errors
  11970.  GetLoadError:
  11971.      CLOSE fileNum%
  11972.      finished = FALSE                            ' don't allow exit until vali
  11973.  
  11974.      ShowError ERR                               ' display error message
  11975.  RESUME NEXT
  11976.  
  11977.  END FUNCTION
  11978.  
  11979.  '
  11980.  ' Func Name: GetSaveFile
  11981.  '
  11982.  ' Description: Prompts the user for a DOS file to save the current
  11983.  '              chart data and settings in.  It returns the file number
  11984.  '              with the actual file name being passed back via the
  11985.  '              argument.
  11986.  '
  11987.  ' Arguments: fileName$ - name of save file
  11988.  '
  11989.  FUNCTION GetSaveFile% (FileName$)
  11990.  
  11991.  ON LOCAL ERROR GOTO GetSaveError                    ' handle file open errors
  11992.  
  11993.      ' Open window for display
  11994.      WindowOpen 1, 8, 20, 12, 58, 0, 7, 0, 7, 15, FALSE, FALSE, FALSE, TRUE, 1
  11995.      WindowLocate 2, 2
  11996.      WindowPrint 2, "File Name:"
  11997.      WindowBox 1, 13, 3, 38
  11998.      WindowLine 4
  11999.  
  12000.      ' open edit field for file name
  12001.      EditFieldOpen 1, RTRIM$(FileName$), 2, 14, 0, 7, 24, 70
  12002.  
  12003.      ' open command buttons
  12004.      ButtonOpen 1, 2, "OK", 5, 6, 0, 0, 1
  12005.      ButtonOpen 2, 1, "Cancel", 5, 25, 0, 0, 1
  12006.  
  12007.      ' start with cursor in edit field
  12008.      currButton = 0
  12009.      currEditField = 1
  12010.      pushButton = 1
  12011.  
  12012.      ' control loop for window
  12013.      finished = FALSE
  12014.      WHILE NOT finished
  12015.          WindowDo currButton, currEditField              ' wait for event
  12016.          SELECT CASE Dialog(0)
  12017.              CASE 1                                      ' Button pressed
  12018.                  pushButton = Dialog(1)
  12019.                  finished = TRUE
  12020.              CASE 2                                      ' Edit Field
  12021.                  currButton = 0
  12022.                  currEditField = 1
  12023.              CASE 6                                      ' enter
  12024.                  finished = TRUE
  12025.              CASE 7                                      ' tab
  12026.                  SELECT CASE currButton
  12027.                      CASE 0, 1:
  12028.                          ButtonSetState currButton, 1
  12029.                          currButton = currButton + 1
  12030.                          pushButton = currButton
  12031.                          ButtonSetState pushButton, 2
  12032.                          currEditField = 0
  12033.                      CASE 2
  12034.                          currButton = 0
  12035.                          pushButton = 1
  12036.                          currEditField = 1
  12037.                          ButtonSetState 1, 2
  12038.                          ButtonSetState 2, 1
  12039.                  END SELECT
  12040.              CASE 8                                      ' back tab
  12041.                  SELECT CASE currButton
  12042.                      CASE 0:
  12043.                          currButton = 2
  12044.                          pushButton = 2
  12045.                          currEditField = 0
  12046.                          ButtonSetState 1, 1
  12047.                          ButtonSetState 2, 2
  12048.                      CASE 1
  12049.                          currButton = 0
  12050.                          currEditField = 1
  12051.                      CASE 2
  12052.                          currButton = 1
  12053.                          pushButton = 1
  12054.                          ButtonSetState 1, 2
  12055.                          ButtonSetState 2, 1
  12056.                  END SELECT
  12057.              CASE 9                                      ' escape
  12058.                  pushButton = 2
  12059.                  finished = TRUE
  12060.              CASE 14                                     ' space bar
  12061.                  IF currButton <> 0 THEN
  12062.                      finished = TRUE
  12063.                  END IF
  12064.          END SELECT
  12065.  
  12066.          ' simple error checking before finishing
  12067.          IF finished = TRUE AND pushButton = 1 THEN
  12068.              temp$ = EditFieldInquire$(1)
  12069.              ' must specify a file
  12070.              IF temp$ = "" THEN
  12071.                  PrintError "Must specify a name."
  12072.                  finished = FALSE
  12073.              ' check if file is valid and can be opened
  12074.              ELSE
  12075.                  ' open file
  12076.                  fileNum% = FREEFILE
  12077.                  OPEN temp$ FOR OUTPUT AS fileNum%
  12078.  
  12079.              END IF
  12080.          END IF
  12081.      WEND
  12082.  
  12083.      ' if operation not canceled return file name and file number
  12084.      IF pushButton = 1 THEN
  12085.          FileName$ = EditFieldInquire$(1)
  12086.          GetSaveFile% = fileNum%
  12087.      ELSE
  12088.          GetSaveFile% = 0
  12089.      END IF
  12090.  
  12091.      WindowClose 1
  12092.  
  12093.      EXIT FUNCTION
  12094.  
  12095.  ' local error handler
  12096.  GetSaveError:
  12097.        finished = FALSE                              ' don't exit until valid
  12098.        CLOSE fileNum%
  12099.  
  12100.        ShowError ERR                                 ' display errors
  12101.  RESUME NEXT
  12102.  
  12103.  END FUNCTION
  12104.  
  12105.  '
  12106.  ' Sub Name: LoadChart
  12107.  '
  12108.  ' Description: Loads chart data and settings from the given file.
  12109.  '
  12110.  ' Arguments: fileNum%  - file number
  12111.  '
  12112.  SUB LoadChart (fileNum%)
  12113.  SHARED Cat$(), catLen AS INTEGER
  12114.  SHARED setLen() AS INTEGER, setName$(), setVal!()
  12115.  SHARED screenMode AS INTEGER, numModes AS INTEGER, mode$()
  12116.  
  12117.  ON LOCAL ERROR GOTO LoadError                       ' handle file loading err
  12118.  
  12119.      ' Read file until EOF is reached:
  12120.      DO UNTIL EOF(fileNum%)
  12121.          ' get data type from file (C=category, V=value, T=title, S=setting):
  12122.          INPUT #fileNum%, type$
  12123.  
  12124.          ' category data
  12125.          IF UCASE$(type$) = "C" THEN
  12126.              INPUT #fileNum%, catLen
  12127.              FOR i% = 1 TO catLen
  12128.                  INPUT #fileNum%, Cat$(i%)
  12129.              NEXT i%
  12130.  
  12131.          ' value data
  12132.          ELSEIF UCASE$(type$) = "V" THEN
  12133.              ' too many sets in file
  12134.              IF setNum >= cMaxSets THEN
  12135.                  PrintError "Too many data sets in file. Extra sets lost."
  12136.                  EXIT DO
  12137.              END IF
  12138.  
  12139.              setNum = setNum + 1
  12140.              INPUT #fileNum%, setName$(setNum)         ' get set name
  12141.              INPUT #fileNum%, setLen(setNum)           ' get set length
  12142.              FOR i% = 1 TO setLen(setNum)
  12143.                  INPUT #fileNum%, setVal!(i%, setNum)  ' get set values
  12144.              NEXT i%
  12145.  
  12146.          ' title data
  12147.          ELSEIF UCASE$(type$) = "T" THEN
  12148.              INPUT #fileNum%, CEnv.MainTitle.title
  12149.              INPUT #fileNum%, CEnv.SubTitle.title
  12150.              INPUT #fileNum%, CEnv.XAxis.AxisTitle.title
  12151.              INPUT #fileNum%, CEnv.YAxis.AxisTitle.title
  12152.  
  12153.          ' chart settings
  12154.          ELSEIF UCASE$(type$) = "S" THEN
  12155.              INPUT #fileNum%, screenMode
  12156.              ' test for valid screen mode
  12157.              valid = FALSE
  12158.              FOR i = 1 TO numModes
  12159.                  IF screenMode = VAL(mode$(i)) THEN valid = TRUE
  12160.              NEXT i
  12161.              IF NOT valid THEN
  12162.                  IF mode$(1) = "13" THEN
  12163.                      screenMode = VAL(mode$(2))
  12164.                  ELSE
  12165.                      screenMode = VAL(mode$(1))
  12166.                  END IF
  12167.              END IF
  12168.  
  12169.              INPUT #fileNum%, CEnv.ChartType, CEnv.ChartStyle, CEnv.DataFont
  12170.  
  12171.              INPUT #fileNum%, CEnv.ChartWindow.X1, CEnv.ChartWindow.Y1, CEnv.C
  12172.              INPUT #fileNum%, CEnv.ChartWindow.Background, CEnv.ChartWindow.bo
  12173.              INPUT #fileNum%, CEnv.DataWindow.X1, CEnv.DataWindow.Y1, CEnv.Dat
  12174.              INPUT #fileNum%, CEnv.DataWindow.Background, CEnv.DataWindow.bord
  12175.  
  12176.              INPUT #fileNum%, CEnv.MainTitle.TitleFont, CEnv.MainTitle.TitleCo
  12177.              INPUT #fileNum%, CEnv.SubTitle.TitleFont, CEnv.SubTitle.TitleColo
  12178.  
  12179.              INPUT #fileNum%, CEnv.XAxis.Grid, CEnv.XAxis.GridStyle, CEnv.XAxi
  12180.              INPUT #fileNum%, CEnv.XAxis.AxisTitle.TitleFont, CEnv.XAxis.AxisT
  12181.              INPUT #fileNum%, CEnv.XAxis.RangeType, CEnv.XAxis.LogBase, CEnv.X
  12182.              INPUT #fileNum%, CEnv.XAxis.ScaleMax, CEnv.XAxis.ScaleFactor, CEn
  12183.              INPUT #fileNum%, CEnv.XAxis.ScaleTitle.title
  12184.              INPUT #fileNum%, CEnv.XAxis.ScaleTitle.TitleFont, CEnv.XAxis.Scal
  12185.  
  12186.              INPUT #fileNum%, CEnv.YAxis.Grid, CEnv.YAxis.GridStyle, CEnv.YAxi
  12187.              INPUT #fileNum%, CEnv.YAxis.AxisTitle.TitleFont, CEnv.YAxis.AxisT
  12188.              INPUT #fileNum%, CEnv.YAxis.RangeType, CEnv.YAxis.LogBase, CEnv.Y
  12189.              INPUT #fileNum%, CEnv.YAxis.ScaleMax, CEnv.YAxis.ScaleFactor, CEn
  12190.              INPUT #fileNum%, CEnv.YAxis.ScaleTitle.title
  12191.              INPUT #fileNum%, CEnv.YAxis.ScaleTitle.TitleFont, CEnv.YAxis.Scal
  12192.  
  12193.              INPUT #fileNum%, CEnv.Legend.Legend, CEnv.Legend.Place, CEnv.Lege
  12194.              INPUT #fileNum%, CEnv.Legend.LegendWindow.X1, CEnv.Legend.LegendW
  12195.              INPUT #fileNum%, CEnv.Legend.LegendWindow.Background, CEnv.Legend
  12196.          ELSE
  12197.              GOSUB LoadError
  12198.          END IF
  12199.      LOOP
  12200.  
  12201.      ' close the file
  12202.      CLOSE fileNum%
  12203.  
  12204.      ' clear any font pointers that don't map to current fonts
  12205.      ClearFonts
  12206.  
  12207.      ' initialize color list depending on newly loaded screen mode
  12208.      InitColors
  12209.  
  12210.      EXIT SUB
  12211.  
  12212.  ' handle any file format errors
  12213.  LoadError:
  12214.  
  12215.      IF ERR THEN
  12216.          ShowError ERR
  12217.      ELSE
  12218.          PrintError "Invalid file format.  Can't continue loading."
  12219.      END IF
  12220.  
  12221.      CLOSE fileNum%                              ' close and exit
  12222.      EXIT SUB
  12223.  
  12224.  RESUME NEXT
  12225.  
  12226.  END SUB
  12227.  
  12228.  '
  12229.  ' Sub Name: OpenChart
  12230.  '
  12231.  ' Description: Handles both the "New" and "Open" operations from the
  12232.  '              "File" menu title.
  12233.  '
  12234.  ' Arguments: newFlag - flag for determining which operation (New or Open)
  12235.  '                      to perform.
  12236.  '
  12237.  SUB OpenChart (newFlag)
  12238.  SHARED saveFile$
  12239.  
  12240.      ' allow user to save current chart if necessary
  12241.      IF chartChanged THEN
  12242.          a$ = "|"
  12243.          a$ = a$ + "Current chart has not been saved.  Save now?"
  12244.  
  12245.          status = Alert(4, a$, 8, 15, 12, 65, "Yes", "No", "Cancel")
  12246.  
  12247.          ' save current chart
  12248.          IF status = OK THEN
  12249.              status = SaveChart(saveFile$, FALSE)
  12250.          END IF
  12251.      ELSE
  12252.          status = OK
  12253.      END IF
  12254.  
  12255.      IF status <> CANCEL THEN
  12256.          ' New option chosen so clear existing data, leave chart settings alon
  12257.          IF newFlag = TRUE THEN
  12258.              MenuItemToggle GALLERYTITLE, CEnv.ChartType
  12259.              IF CEnv.ChartType = cPie THEN
  12260.                  MenuSetState CHARTTITLE, 4, 1
  12261.                  MenuSetState CHARTTITLE, 5, 1
  12262.                  MenuSetState TITLETITLE, 3, 1
  12263.                  MenuSetState TITLETITLE, 4, 1
  12264.              END IF
  12265.              InitChart
  12266.              saveFile$ = ""
  12267.          ' Open operation chosen so get file and load data
  12268.          ELSE
  12269.              fileNum% = GetLoadFile(saveFile$)
  12270.              ' if no errors opening file and operation not canceled then load
  12271.              IF fileNum <> 0 THEN
  12272.                  ' reset menu bar to nothing selected
  12273.                  MenuItemToggle GALLERYTITLE, CEnv.ChartType
  12274.                  IF CEnv.ChartType = cPie THEN
  12275.                      MenuSetState CHARTTITLE, 4, 1
  12276.                      MenuSetState CHARTTITLE, 5, 1
  12277.                      MenuSetState TITLETITLE, 3, 1
  12278.                      MenuSetState TITLETITLE, 4, 1
  12279.                  END IF
  12280.  
  12281.                  ClearData                       'clear current data
  12282.  
  12283.                  setNum = 0
  12284.                  LoadChart fileNum%             ' load the data
  12285.  
  12286.                  ' set menu bar according to new chart settings
  12287.                  MenuItemToggle GALLERYTITLE, CEnv.ChartType
  12288.                  IF CEnv.ChartType = cPie THEN
  12289.                      MenuSetState CHARTTITLE, 4, 0
  12290.                      MenuSetState CHARTTITLE, 5, 0
  12291.                      MenuSetState TITLETITLE, 3, 0
  12292.                      MenuSetState TITLETITLE, 4, 0
  12293.                  END IF
  12294.  
  12295.                  ' new chart not changed
  12296.                  chartChanged = FALSE
  12297.  
  12298.                  ' chart data exists so allow user to view chart
  12299.                  IF setNum > 0 THEN
  12300.                      MenuSetState VIEWTITLE, 2, 1
  12301.                  END IF
  12302.              END IF
  12303.          END IF
  12304.      END IF
  12305.  
  12306.  END SUB
  12307.  
  12308.  '
  12309.  ' Sub Name: PrintError
  12310.  '
  12311.  ' Description: Prints error messages on the screen in an Alert box.
  12312.  '
  12313.  ' Arguments: text$ - error message
  12314.  '
  12315.  SUB PrintError (text$)
  12316.  
  12317.      textLen = LEN(text$) + 2
  12318.      lefCol = ((80 - textLen) / 2) - 1
  12319.      a$ = "| " + text$
  12320.      junk = Alert(4, a$, 8, lefCol, 12, textLen + lefCol, "", "", "")
  12321.  
  12322.  END SUB
  12323.  
  12324.  '
  12325.  ' Func Name: SaveChart
  12326.  '
  12327.  ' Description: Performs both the "Save" and "Save AS" operations from
  12328.  '              the "File" menu title.  If "Save As" was chosen or if
  12329.  '              "Save" was chosen and no save file has been previously
  12330.  '              specified, it prompts the user for a new file in
  12331.  '              which to save the current chart.  Also returns the status of
  12332.  '              save operation for use in other routines
  12333.  '
  12334.  ' Arguments: fileName$ - name of previously specified save file (may be nil)
  12335.  '            saveAsFlag - flag for invoking the "Save As" operation.
  12336.  '
  12337.  FUNCTION SaveChart% (FileName$, saveAsFlag)
  12338.  SHARED Cat$(), catLen AS INTEGER
  12339.  SHARED setLen() AS INTEGER, setName$(), setVal!()
  12340.  SHARED screenMode AS INTEGER
  12341.  
  12342.  ON LOCAL ERROR GOTO SaveError                   ' handle file errors
  12343.  
  12344.      ' get new file name if necessary
  12345.      IF FileName$ = "" OR saveAsFlag THEN
  12346.          fileNum% = GetSaveFile(FileName$)
  12347.      ' otherwise just open the file
  12348.      ELSE
  12349.          fileNum% = FREEFILE
  12350.          OPEN FileName$ FOR OUTPUT AS fileNum%
  12351.      END IF
  12352.  
  12353.      ' quit save if cancel chosen above or error occurred during open.
  12354.      IF fileNum% = 0 THEN
  12355.          SaveChart% = CANCEL                     ' return status
  12356.          EXIT FUNCTION
  12357.      END IF
  12358.  
  12359.      ' save category data
  12360.      IF catLen > 0 THEN
  12361.          PRINT #fileNum%, "C"
  12362.          PRINT #fileNum%, catLen
  12363.  
  12364.          FOR i% = 1 TO catLen
  12365.              PRINT #fileNum%, Cat$(i%)
  12366.          NEXT i%
  12367.      END IF
  12368.  
  12369.      ' save value data
  12370.      IF setNum > 0 THEN
  12371.          FOR j% = 1 TO setNum
  12372.              PRINT #fileNum%, "V"
  12373.              PRINT #fileNum%, setName$(j%)
  12374.              PRINT #fileNum%, setLen(j%)
  12375.  
  12376.              FOR i% = 1 TO setLen(j%)
  12377.                  PRINT #fileNum%, setVal!(i%, j%)
  12378.              NEXT i%
  12379.          NEXT j%
  12380.      END IF
  12381.  
  12382.      ' save titles
  12383.      PRINT #fileNum%, "T"
  12384.      PRINT #fileNum%, CEnv.MainTitle.title
  12385.      PRINT #fileNum%, CEnv.SubTitle.title
  12386.      PRINT #fileNum%, CEnv.XAxis.AxisTitle.title
  12387.      PRINT #fileNum%, CEnv.YAxis.AxisTitle.title
  12388.  
  12389.      'save chart settings
  12390.      PRINT #fileNum%, "S"
  12391.      PRINT #fileNum%, screenMode
  12392.  
  12393.      PRINT #fileNum%, CEnv.ChartType, CEnv.ChartStyle, CEnv.DataFont
  12394.  
  12395.      PRINT #fileNum%, CEnv.ChartWindow.X1, CEnv.ChartWindow.Y1, CEnv.ChartWind
  12396.      PRINT #fileNum%, CEnv.ChartWindow.Background, CEnv.ChartWindow.border, CE
  12397.      PRINT #fileNum%, CEnv.DataWindow.X1, CEnv.DataWindow.Y1, CEnv.DataWindow.
  12398.      PRINT #fileNum%, CEnv.DataWindow.Background, CEnv.DataWindow.border, CEnv
  12399.  
  12400.      PRINT #fileNum%, CEnv.MainTitle.TitleFont, CEnv.MainTitle.TitleColor, CEn
  12401.      PRINT #fileNum%, CEnv.SubTitle.TitleFont, CEnv.SubTitle.TitleColor, CEnv.
  12402.  
  12403.      PRINT #fileNum%, CEnv.XAxis.Grid, CEnv.XAxis.GridStyle, CEnv.XAxis.AxisCo
  12404.      PRINT #fileNum%, CEnv.XAxis.AxisTitle.TitleFont, CEnv.XAxis.AxisTitle.Tit
  12405.      PRINT #fileNum%, CEnv.XAxis.RangeType, CEnv.XAxis.LogBase, CEnv.XAxis.Aut
  12406.      PRINT #fileNum%, CEnv.XAxis.ScaleMax, CEnv.XAxis.ScaleFactor, CEnv.XAxis.
  12407.      PRINT #fileNum%, CEnv.XAxis.ScaleTitle.title
  12408.      PRINT #fileNum%, CEnv.XAxis.ScaleTitle.TitleFont, CEnv.XAxis.ScaleTitle.T
  12409.  
  12410.      PRINT #fileNum%, CEnv.YAxis.Grid, CEnv.YAxis.GridStyle, CEnv.YAxis.AxisCo
  12411.      PRINT #fileNum%, CEnv.YAxis.AxisTitle.TitleFont, CEnv.YAxis.AxisTitle.Tit
  12412.      PRINT #fileNum%, CEnv.YAxis.RangeType, CEnv.YAxis.LogBase, CEnv.YAxis.Aut
  12413.      PRINT #fileNum%, CEnv.YAxis.ScaleMax, CEnv.YAxis.ScaleFactor, CEnv.YAxis.
  12414.      PRINT #fileNum%, CEnv.YAxis.ScaleTitle.title
  12415.      PRINT #fileNum%, CEnv.YAxis.ScaleTitle.TitleFont, CEnv.YAxis.ScaleTitle.T
  12416.  
  12417.      PRINT #fileNum%, CEnv.Legend.Legend, CEnv.Legend.Place, CEnv.Legend.TextC
  12418.      PRINT #fileNum%, CEnv.Legend.LegendWindow.X1, CEnv.Legend.LegendWindow.Y1
  12419.      PRINT #fileNum%, CEnv.Legend.LegendWindow.Background, CEnv.Legend.LegendW
  12420.  
  12421.      CLOSE fileNum%
  12422.  
  12423.      SaveChart% = OK                             ' return status
  12424.  
  12425.      chartChanged = FALSE                        ' reset global change flag
  12426.  
  12427.      EXIT FUNCTION
  12428.  
  12429.  ' local error handler
  12430.  SaveError:
  12431.        SaveChart% = CANCEL                       ' return cancel status
  12432.        CLOSE fileNum%
  12433.  
  12434.        ShowError ERR                             ' display error message
  12435.  
  12436.        EXIT FUNCTION                             ' exit on error
  12437.  RESUME NEXT
  12438.  
  12439.  END FUNCTION
  12440.  
  12441.  '
  12442.  ' Sub Name: ShowError
  12443.  '
  12444.  ' Description: Displays an appropriate error message for the given error
  12445.  '
  12446.  ' Arguments: errorNum - error number
  12447.  '
  12448.  SUB ShowError (errorNum)
  12449.        SELECT CASE errorNum
  12450.          CASE 6:                                 ' overflow
  12451.              PrintError "Overflow occurred."
  12452.          CASE 14:                                ' out of space
  12453.              PrintError "Out of string space.  Please restart."
  12454.          CASE 53:                                ' file not found
  12455.              PrintError "File not found."
  12456.          CASE 62:                                ' input past end of file
  12457.              PrintError "Invalid file format. Can't continue loading."
  12458.          CASE 64:                                ' bad file name
  12459.              PrintError "Invalid file name."
  12460.          CASE 68:                                ' device unavailable
  12461.              PrintError "Selected device unavailable."
  12462.          CASE 71:                                ' disk not ready
  12463.              PrintError "Disk not ready."
  12464.          CASE 75:                                ' path access error
  12465.              PrintError "Invalid path."
  12466.          CASE 76:                                ' path not found
  12467.              PrintError "Path not found."
  12468.          CASE ELSE                               ' catch all
  12469.              PrintError "BASIC error #" + LTRIM$(STR$(ERR)) + " occurred."
  12470.       END SELECT
  12471.  
  12472.  
  12473.  END SUB
  12474.  
  12475.  '
  12476.  ' Sub Name: ViewData
  12477.  '
  12478.  ' Description: Displays the current chart data and allows the user to
  12479.  '              modify, delete or add to that data.
  12480.  '
  12481.  ' Arguments: none
  12482.  '
  12483.  SUB ViewData
  12484.  SHARED setVal!(), setLen()  AS INTEGER, setName$()
  12485.  SHARED Cat$(), catLen AS INTEGER
  12486.  SHARED GloEdit() AS EditFieldType
  12487.  
  12488.      ' temporary data storage that allows user to cancel all changes and
  12489.      ' restore original data
  12490.      DIM tsetVal$(1 TO 15, 1 TO 15), tCat$(1 TO 15), tsetName$(1 TO 15)
  12491.      DIM tsetNum AS INTEGER
  12492.      DIM tsetLen(1 TO 15) AS INTEGER
  12493.      DIM tcatLen  AS INTEGER
  12494.  
  12495.      ON LOCAL ERROR GOTO ViewDatError
  12496.  
  12497.      ' fill out temp data
  12498.      FOR i = 1 TO cMaxSets
  12499.          tsetName$(i) = setName$(i)
  12500.          tCat$(i) = Cat$(i)
  12501.          tsetLen(i) = setLen(i)
  12502.          FOR j = 1 TO tsetLen(i)
  12503.              tsetVal$(j, i) = LTRIM$(STR$(setVal!(j, i)))
  12504.          NEXT j
  12505.          FOR j = tsetLen(i) + 1 TO cMaxValues
  12506.              tsetVal$(j, i) = ""
  12507.          NEXT j
  12508.      NEXT i
  12509.      tsetNum = setNum
  12510.      tcatLen = catLen
  12511.  
  12512.      ' set up window
  12513.      winRow = 4
  12514.      winCol = 8
  12515.      WindowOpen 1, winRow, winCol, 23, 74, 0, 7, 0, 7, 15, FALSE, FALSE, FALSE
  12516.      WindowLocate 1, 2
  12517.      WindowPrint 2, "Series Name:"
  12518.      WindowBox 2, 2, 18, 24
  12519.      WindowLocate 1, 26
  12520.      WindowPrint 2, "Categories:"
  12521.      WindowBox 2, 26, 18, 48
  12522.      WindowLocate 1, 50
  12523.      WindowPrint 2, "Values:"
  12524.      WindowBox 2, 50, 18, 66
  12525.      WindowLine 19
  12526.  
  12527.      ' display chart data
  12528.      FOR i = 1 TO 15
  12529.          IF i < 10 THEN
  12530.              a$ = " "
  12531.          ELSE
  12532.              a$ = ""
  12533.          END IF
  12534.          a$ = a$ + LTRIM$(STR$(i)) + ". "
  12535.          WindowLocate i + 2, 3
  12536.          WindowPrint 2, a$ + tsetName$(i)
  12537.          WindowLocate i + 2, 27
  12538.          WindowPrint 2, a$ + tCat$(i)
  12539.          WindowLocate i + 2, 51
  12540.          WindowPrint 2, a$ + MID$(tsetVal$(i, 1), 1, 10)
  12541.      NEXT i
  12542.      ' highlight first set name
  12543.      EditFieldOpen 1, tsetName$(1), 3, 7, 7, 0, 17, 16
  12544.  
  12545.      IF tsetNum < cMaxSets THEN tsetNum = tsetNum + 1
  12546.      IF tcatLen < cMaxValues THEN tcatLen = tcatLen + 1
  12547.      IF tsetLen(1) < cMaxValues THEN tsetLen(1) = tsetLen(1) + 1
  12548.  
  12549.      ' area buttons
  12550.      ButtonOpen 1, 1, "", 3, 3, 17, 23, 4
  12551.      ButtonOpen 2, 1, "", 3, 27, 17, 47, 4
  12552.      ButtonOpen 3, 1, "", 3, 51, 17, 65, 4
  12553.  
  12554.      ' command buttons
  12555.      ButtonOpen 4, 1, "OK", 20, 15, 0, 0, 1
  12556.      ButtonOpen 5, 1, "Cancel", 20, 45, 0, 0, 1
  12557.  
  12558.      ' start with cursor in first set name edit field
  12559.      currButton = 1
  12560.      prevButton = 1
  12561.      currRow = 1
  12562.      currEditField = 1
  12563.      currCat = 1
  12564.      currVal = 1
  12565.      currSet = 1
  12566.  
  12567.      IF CEnv.ChartType = cPie THEN
  12568.          a$ = " Pie chart information||"
  12569.          a$ = a$ + " Only data values from the first series are plotted in pie
  12570.          a$ = a$ + " Data values from the second series are used in determinin
  12571.          a$ = a$ + " or not pie pieces are exploded.  Non-zero values in this
  12572.          a$ = a$ + " will cause corresponding pie pieces to be exploded.  All
  12573.          a$ = a$ + "  series will be ignored.
  12574.  
  12575.          junk = Alert(4, a$, 8, 7, 17, 75, "", "", "")
  12576.      END IF
  12577.  
  12578.      ' window control loop
  12579.      finished = FALSE
  12580.      WHILE NOT finished
  12581.          WindowDo currButton, currEditField
  12582.  
  12583.          SELECT CASE Dialog(0)
  12584.              CASE 1                                      ' button pressed
  12585.                  currButton = Dialog(1)
  12586.                  SELECT CASE currButton
  12587.                      CASE 1, 2, 3
  12588.                          currRow = Dialog(17)
  12589.                      CASE 4, 5
  12590.                          finished = TRUE
  12591.                  END SELECT
  12592.                  GOSUB UpdateEdit
  12593.              CASE 2                                      ' Edit Field
  12594.                  currEditField = Dialog(2)
  12595.              CASE 6, 11                                  ' enter, down arrow
  12596.                  IF currButton > 3 AND Dialog(0) = 6 THEN
  12597.                      finished = TRUE
  12598.                  ELSE
  12599.                      currRow = currRow + 1
  12600.                      GOSUB UpdateEdit
  12601.                  END IF
  12602.              CASE 7                                      'tab
  12603.                  SELECT CASE currButton
  12604.                      CASE 1:
  12605.                          currButton = 2
  12606.                          currRow = currCat
  12607.                          GOSUB UpdateEdit
  12608.                      CASE 2:
  12609.                          currButton = 3
  12610.                          currRow = currVal
  12611.                          GOSUB UpdateEdit
  12612.                      CASE 3:
  12613.                          currButton = 4
  12614.                          ButtonToggle 4
  12615.                          GOSUB UpdateEdit
  12616.                      CASE 4:
  12617.                          currButton = 5
  12618.                          ButtonToggle 4
  12619.                          ButtonToggle 5
  12620.                      CASE 5:
  12621.                          currButton = 1
  12622.                          currRow = currSet
  12623.                          ButtonToggle 5
  12624.                          GOSUB UpdateEdit
  12625.                  END SELECT
  12626.              CASE 8                                      'back tab
  12627.                  SELECT CASE currButton
  12628.                      CASE 1:
  12629.                          currButton = 5
  12630.                          ButtonToggle 5
  12631.                          GOSUB UpdateEdit
  12632.                      CASE 2:
  12633.                          currButton = 1
  12634.                          currRow = currSet
  12635.                          GOSUB UpdateEdit
  12636.                      CASE 3:
  12637.                          currButton = 2
  12638.                          currRow = currCat
  12639.                          GOSUB UpdateEdit
  12640.                      CASE 4:
  12641.                          currButton = 3
  12642.                          currRow = currVal
  12643.                          ButtonToggle 4
  12644.                          GOSUB UpdateEdit
  12645.                      CASE 5:
  12646.                          currButton = 4
  12647.                          ButtonToggle 5
  12648.                          ButtonToggle 4
  12649.                  END SELECT
  12650.              CASE 9                                      'escape
  12651.                  currButton = 5
  12652.                  finished = TRUE
  12653.              CASE 10:                                    'up arrow
  12654.                  IF currButton < 4 THEN
  12655.                      currRow = currRow - 1
  12656.                      GOSUB UpdateEdit
  12657.                  END IF
  12658.              CASE 14                                     'space
  12659.                  IF currButton > 3 THEN finished = TRUE
  12660.          END SELECT
  12661.  
  12662.          ' give delete warning before exit
  12663.          IF finished = TRUE AND currButton = 4 THEN
  12664.              temp = FALSE
  12665.              FOR i = 1 TO tsetNum
  12666.                  IF tsetName$(i) = "" AND tsetLen(i) > 0 AND NOT (tsetLen(i) =
  12667.              NEXT i
  12668.              IF temp = TRUE THEN
  12669.                  a$ = "|"
  12670.                  a$ = a$ + "Series without names will be deleted upon exit."
  12671.                  reply = Alert(4, a$, 8, 10, 12, 70, "OK", "Cancel", "")
  12672.                  IF reply <> 1 THEN finished = FALSE
  12673.              END IF
  12674.          END IF
  12675.      WEND
  12676.  
  12677.      ' finished so save new data
  12678.      IF currButton = 4 THEN
  12679.          ClearData                                       ' clear existing data
  12680.  
  12681.          ' copy temporary values to permanent locations
  12682.          indx = 0
  12683.          FOR i = 1 TO tsetNum
  12684.              IF tsetName$(i) <> "" THEN
  12685.                  indx = indx + 1
  12686.                  setName$(indx) = tsetName$(i)              ' store set names
  12687.                  indx2 = 0
  12688.                  FOR j = 1 TO tsetLen(i)
  12689.                      IF tsetVal$(j, i) <> "" THEN
  12690.                          indx2 = indx2 + 1
  12691.                          setVal!(indx2, i) = VAL(tsetVal$(j, i))   ' store set
  12692.                      END IF
  12693.                  NEXT j
  12694.                  setLen(indx) = indx2                     ' get set lengths
  12695.              END IF
  12696.          NEXT i
  12697.          setNum = indx
  12698.  
  12699.          ' clear leftover names and set lengths
  12700.          FOR i = setNum + 1 TO cMaxSets
  12701.              setName$(i) = ""
  12702.              setLen(i) = 0
  12703.          NEXT i
  12704.  
  12705.          ' store category names
  12706.          FOR i = 1 TO tcatLen
  12707.              Cat$(i) = tCat$(i)
  12708.          NEXT i
  12709.          catLen = tcatLen
  12710.  
  12711.          FOR i = tcatLen TO 1 STEP -1
  12712.              IF Cat$(i) = "" THEN
  12713.                  catLen = catLen - 1
  12714.                  IF catLen <= 0 THEN EXIT FOR
  12715.              ELSE
  12716.                  EXIT FOR
  12717.              END IF
  12718.          NEXT i
  12719.  
  12720.          ' clear leftover category names
  12721.          FOR i = catLen + 1 TO cMaxValues
  12722.              Cat$(i) = ""
  12723.          NEXT i
  12724.  
  12725.          ' update active menu titles based on current data
  12726.          IF setNum > 0 THEN
  12727.              MenuSetState VIEWTITLE, 2, 1
  12728.              chartChanged = TRUE
  12729.          ELSE
  12730.              MenuSetState VIEWTITLE, 2, 0
  12731.          END IF
  12732.      END IF
  12733.      WindowClose 1
  12734.  
  12735.  
  12736.      EXIT SUB
  12737.  
  12738.  ViewDatError:
  12739.      PrintError "BASIC error #" + LTRIM$(STR$(ERR)) + " occurred."
  12740.  RESUME NEXT
  12741.  
  12742.  ' redraws the value edit column so it displays the current set's values
  12743.  ResetVal:
  12744.      ' display new values
  12745.      FOR i = 1 TO cMaxValues
  12746.          WindowLocate i + 2, 55
  12747.          WindowPrint 2, tsetVal$(i, currSet) + STRING$(10 - LEN(tsetVal$(i, cu
  12748.      NEXT i
  12749.  
  12750.      IF tsetLen(currSet) = 0 THEN
  12751.          tsetLen(currSet) = tsetLen(currSet) + 1
  12752.      ELSEIF tsetLen(currSet) < cMaxValues AND tsetVal$(tsetLen(currSet), currS
  12753.          tsetLen(currSet) = tsetLen(currSet) + 1
  12754.      END IF
  12755.  
  12756.      currVal = 31
  12757.  
  12758.  RETURN
  12759.  
  12760.  UpdateEdit:
  12761.      IF prevButton < 4 THEN GOSUB ClosePrevEdit
  12762.  
  12763.      SELECT CASE currButton
  12764.          CASE 1:
  12765.              IF currRow <= 0 THEN
  12766.                  currRow = tsetNum
  12767.              ELSEIF currRow > 15 THEN
  12768.                  currRow = 1
  12769.              ELSEIF currRow = tsetNum + 1 AND tsetName$(tsetNum) <> "" THEN
  12770.                  tsetNum = tsetNum + 1
  12771.              ELSEIF currRow > tsetNum THEN
  12772.                  currRow = 1
  12773.              END IF
  12774.              WindowColor 0, 7
  12775.              WindowLocate currSet + 2, 7
  12776.              WindowPrint 2, tsetName$(currSet) + STRING$(17 - LEN(tsetName$(cu
  12777.  
  12778.              FG = 7
  12779.              BG = 0
  12780.              vislen = 17
  12781.              totlen = 16
  12782.              currSet = currRow
  12783.              currCol = 7
  12784.              temp$ = tsetName$(currSet)
  12785.              IF prevButton = 1 THEN GOSUB ResetVal
  12786.          CASE 2:
  12787.              IF currRow <= 0 THEN
  12788.                  currRow = tcatLen
  12789.              ELSEIF currRow > 15 THEN
  12790.                  currRow = 1
  12791.              ELSEIF currRow > tcatLen THEN
  12792.                  tcatLen = currRow
  12793.              END IF
  12794.              FG = 0
  12795.              BG = 7
  12796.              vislen = 17
  12797.              totlen = 16
  12798.              currCat = currRow
  12799.              currCol = 31
  12800.              temp$ = tCat$(currCat)
  12801.          CASE 3:
  12802.              IF currRow <= 0 THEN
  12803.                  currRow = tsetLen(currSet)
  12804.              ELSEIF currRow > 15 THEN
  12805.                  currRow = 1
  12806.              ELSEIF currRow = tsetLen(currSet) + 1 AND tsetVal$(tsetLen(currSe
  12807.                  tsetLen(currSet) = tsetLen(currSet) + 1
  12808.              ELSEIF currRow > tsetLen(currSet) THEN
  12809.                  currRow = 1
  12810.              END IF
  12811.              FG = 0
  12812.              BG = 7
  12813.              vislen = 11
  12814.              totlen = 20
  12815.              currVal = currRow
  12816.              currCol = 55
  12817.              temp$ = tsetVal$(currVal, currSet)
  12818.          CASE ELSE
  12819.              prevButton = currButton
  12820.              RETURN
  12821.      END SELECT
  12822.  
  12823.      EditFieldOpen 1, temp$, currRow + 2, currCol, FG, BG, vislen, totlen
  12824.      currEditField = 1
  12825.      prevButton = currButton
  12826.  RETURN
  12827.  
  12828.  ClosePrevEdit:
  12829.      temp$ = RTRIM$(EditFieldInquire$(1))
  12830.      EditFieldClose 1
  12831.      currEditField = 0
  12832.      IF prevButton = 1 THEN
  12833.          WindowColor 7, 0
  12834.      ELSE
  12835.          WindowColor 0, 7
  12836.      END IF
  12837.  
  12838.      SELECT CASE prevButton
  12839.          CASE 1:
  12840.              tsetName$(currSet) = temp$
  12841.              temp$ = temp$ + STRING$(17 - LEN(temp$), " ")
  12842.              editRow = currSet + 2
  12843.              editCol = 7
  12844.          CASE 2:
  12845.              tCat$(currCat) = temp$
  12846.              editRow = currCat + 2
  12847.              editCol = 31
  12848.          CASE 3:
  12849.              tsetVal$(currVal, currSet) = temp$
  12850.              tval# = VAL(temp$)
  12851.              IF tval# = 0 AND temp$ <> "0" AND LEN(RTRIM$(temp$)) <> 0 THEN
  12852.                  PrintError "Warning: Non-numeric values will default to zero
  12853.              END IF
  12854.              temp$ = MID$(temp$, 1, 10)
  12855.              editRow = currVal + 2
  12856.              editCol = 55
  12857.      END SELECT
  12858.  
  12859.      WindowLocate editRow, editCol
  12860.      WindowPrint 2, temp$
  12861.      WindowColor 0, 7
  12862.  RETURN
  12863.  
  12864.  END SUB
  12865.  
  12866.  
  12867.  
  12868.  COLORS.BAS
  12869.  CD-ROM Disc Path:   \SAMPCODE\BASIC\COLORS.BAS
  12870.  
  12871.  SCREEN 1
  12872.  
  12873.  Esc$ = CHR$(27)
  12874.  ' Draw three boxes and paint the interior
  12875.  ' of each box with a different color:
  12876.  FOR ColorVal = 1 TO 3
  12877.     LINE (X, Y) -STEP(60, 50), ColorVal, BF
  12878.     X = X + 61
  12879.     Y = Y + 51
  12880.  NEXT ColorVal
  12881.  
  12882.  LOCATE 21, 1
  12883.  PRINT "Press ESC to end."
  12884.  PRINT "Press any other key to continue."
  12885.  
  12886.  ' Restrict additional printed output to the 23rd line:
  12887.  VIEW PRINT 23 TO 23
  12888.  DO
  12889.     PaletteVal = 1
  12890.     DO
  12891.  
  12892.        ' PaletteVal is either 1 or 0:
  12893.        PaletteVal = 1 - PaletteVal
  12894.  
  12895.        ' Set the background color and choose the palette:
  12896.        COLOR BackGroundVal, PaletteVal
  12897.        PRINT "Background ="; BackGroundVal;
  12898.        PRINT "Palette ="; PaletteVal;
  12899.  
  12900.        Pause$ = INPUT$(1)        ' Wait for a keystroke.
  12901.        PRINT
  12902.     ' Exit the loop if both palettes have been shown,
  12903.     ' or if the user pressed the ESC key:
  12904.     LOOP UNTIL PaletteVal = 1 OR Pause$ = Esc$
  12905.  
  12906.     BackGroundVal = BackGroundVal + 1
  12907.  
  12908.  ' Exit this loop if all 16 background colors have
  12909.  ' been shown, or if the user pressed the ESC key:
  12910.  LOOP UNTIL BackGroundVal > 15 OR Pause$ = Esc$
  12911.  
  12912.  SCREEN 0                     ' Restore text mode and
  12913.  WIDTH 80                     ' 80-column screen width.
  12914.  
  12915.  
  12916.  
  12917.  CRLF.BAS
  12918.  CD-ROM Disc Path:   \SAMPCODE\BASIC\CRLF.BAS
  12919.  
  12920.  DEFINT A-Z             ' Default variable type is integer.
  12921.  
  12922.  ' The Backup$ FUNCTION makes a backup file with
  12923.  ' the same base as FileName$ plus a .BAK extension:
  12924.  DECLARE FUNCTION Backup$ (FileName$)
  12925.  
  12926.  ' Initialize symbolic constants and variables:
  12927.  CONST FALSE = 0, TRUE = NOT FALSE
  12928.  
  12929.  CarReturn$ = CHR$(13)
  12930.  LineFeed$ = CHR$(10)
  12931.  
  12932.  DO
  12933.     CLS
  12934.  
  12935.     ' Input the name of the file to change:
  12936.     INPUT "Which file do you want to convert"; OutFile$
  12937.  
  12938.     InFile$ = Backup$(OutFile$)  ' Get backup file's name.
  12939.  
  12940.     ON ERROR GOTO ErrorHandler   ' Turn on error trapping.
  12941.  
  12942.     NAME OutFile$ AS InFile$     ' Rename input file as
  12943.                                  ' backup file.
  12944.  
  12945.     ON ERROR GOTO 0              ' Turn off error trapping.
  12946.  
  12947.     ' Open backup file for input and old file for output:
  12948.     OPEN InFile$ FOR INPUT AS #1
  12949.     OPEN OutFile$ FOR OUTPUT AS #2
  12950.  
  12951.     ' The PrevCarReturn variable is a flag set to TRUE
  12952.     ' whenever the program reads a carriage-return character:
  12953.     PrevCarReturn = FALSE
  12954.  ' Read from input file until reaching end of file:
  12955.     DO UNTIL EOF(1)
  12956.  
  12957.        ' This is not end of file, so read a character:
  12958.        FileChar$ = INPUT$(1, #1)
  12959.  
  12960.        SELECT CASE FileChar$
  12961.  
  12962.           CASE CarReturn$        ' The character is a CR.
  12963.  
  12964.              ' If the previous character was also a
  12965.              ' CR, put a LF before the character:
  12966.              IF PrevCarReturn THEN
  12967.                  FileChar$ = LineFeed$ + FileChar$
  12968.              END IF
  12969.  
  12970.              ' In any case, set the PrevCarReturn
  12971.              ' variable to TRUE:
  12972.              PrevCarReturn = TRUE
  12973.  
  12974.           CASE LineFeed$         ' The character is a LF.
  12975.  
  12976.              ' If the previous character was not a
  12977.              ' CR, put a CR before the character:
  12978.              IF NOT PrevCarReturn THEN
  12979.                  FileChar$ = CarReturn$ + FileChar$
  12980.              END IF
  12981.  
  12982.              ' Set the PrevCarReturn variable to FALSE:
  12983.              PrevCarReturn = FALSE
  12984.  
  12985.           CASE ELSE              ' Neither a CR nor a LF.
  12986.  
  12987.              ' If the previous character was a CR,
  12988.              ' set the PrevCarReturn variable to FALSE
  12989.              ' and put a LF before the current character:
  12990.              IF PrevCarReturn THEN
  12991.                 PrevCarReturn = FALSE
  12992.                 FileChar$ = LineFeed$ + FileChar$
  12993.              END IF
  12994.  
  12995.        END SELECT
  12996.  
  12997.        ' Write the character(s) to the new file:
  12998.        PRINT #2, FileChar$;
  12999.     LOOP
  13000.  
  13001.     ' Write a LF if the last character in the file was a CR:
  13002.     IF PrevCarReturn THEN PRINT #2, LineFeed$;
  13003.  CLOSE                        ' Close both files.
  13004.     PRINT "Another file (Y/N)?"  ' Prompt to continue.
  13005.  
  13006.     ' Change the input to uppercase (capital letter):
  13007.     More$ = UCASE$(INPUT$(1))
  13008.  
  13009.  ' Continue the program if the user entered a "Y" or a "Y":
  13010.  LOOP WHILE More$ = "Y"
  13011.  END
  13012.  
  13013.  ErrorHandler:           ' Error-handling routine
  13014.     CONST NOFILE = 53, FILEEXISTS = 58
  13015.  
  13016.     ' The ERR function returns the error code for last error:
  13017.     SELECT CASE ERR
  13018.        CASE NOFILE       ' Program couldn't find file
  13019.                          ' with input name.
  13020.  
  13021.           PRINT "No such file in current directory."
  13022.           INPUT "Enter new name: ", OutFile$
  13023.           InFile$ = Backup$(OutFile$)
  13024.           RESUME
  13025.        CASE FILEEXISTS   ' There is already a file named
  13026.                          ' <filename>.BAK in this directory:
  13027.                          ' remove it, then continue.
  13028.           KILL InFile$
  13029.           RESUME
  13030.        CASE ELSE         ' An unanticipated error occurred:
  13031.                          ' stop the program.
  13032.           ON ERROR GOTO 0
  13033.     END SELECT
  13034.  
  13035.  ' ======================== BACKUP$ =========================
  13036.  '   This procedure returns a file name that consists of the
  13037.  '   base name of the input file (everything before the ".")
  13038.  '   plus the extension ".BAK"
  13039.  ' ==========================================================
  13040.  
  13041.  FUNCTION Backup$ (FileName$) STATIC
  13042.  
  13043.     ' Look for a period:
  13044.     Extension = INSTR(FileName$, ".")
  13045.  
  13046.     ' If there is a period, add .BAK to the base:
  13047.     IF Extension > 0 THEN
  13048.        Backup$ = LEFT$(FileName$, Extension - 1) + ".BAK"
  13049.     ' Otherwise, add .BAK to the whole name:
  13050.     ELSE
  13051.        Backup$ = FileName$ + ".BAK"
  13052.     END IF
  13053.  END FUNCTION
  13054.  
  13055.  
  13056.  
  13057.  CUBE.BAS
  13058.  CD-ROM Disc Path:   \SAMPCODE\BASIC\CUBE.BAS
  13059.  
  13060.  ' Define the macro string used to draw the cube
  13061.  ' and paint its sides:
  13062.  One$ =        "BR30 BU25 C1 R54 U45 L54 D45 BE20 P1,1        G20 C2 G20"
  13063.  Two$ =        "R54 E20 L54 BD5 P2,2 U5 C4 G20 U45 E20 D45 BL5 P4,4"
  13064.  Plot$ = One$ + Two$
  13065.  
  13066.  APage% = 1        ' Initialize values for the active and visual
  13067.  VPage% = 0        ' pages as well as the angle of rotation.
  13068.  Angle% = 0
  13069.  
  13070.  DO
  13071.     SCREEN 7, , APage%, VPage% ' Draw to the active page
  13072.                                   ' while showing the visual page.
  13073.  
  13074.     CLS 1                      ' Clear the active page.
  13075.  
  13076.     ' Rotate the        cube "Angle%" degrees:
  13077.     DRAW        "TA" + STR$(Angle%) + Plot$
  13078.  
  13079.     ' Angle% is some multiple of        15 degrees:
  13080.     Angle% = (Angle% + 15) MOD 360
  13081.  
  13082.     ' Drawing is complete, so make the cube visible in its
  13083.     ' new position by switching the active and visual pages:
  13084.     SWAP        APage%,        VPage%
  13085.  
  13086.  LOOP WHILE INKEY$ = ""              ' A keystroke ends the program.
  13087.  
  13088.  END
  13089.  
  13090.  
  13091.  
  13092.  EDPAT.BAS
  13093.  CD-ROM Disc Path:   \SAMPCODE\BASIC\EDPAT.BAS
  13094.  
  13095.  DECLARE SUB DrawPattern ()
  13096.  DECLARE SUB EditPattern ()
  13097.  DECLARE SUB Initialize ()
  13098.  DECLARE SUB ShowPattern (OK$)
  13099.  
  13100.  DIM Bit%(0 TO 7), Pattern$, PatternSize%
  13101.  DO
  13102.     Initialize
  13103.     EditPattern
  13104.     ShowPattern OK$
  13105.  LOOP WHILE OK$ = "Y"
  13106.  
  13107.  END
  13108.  ' ======================= DRAWPATTERN ====================
  13109.  '  Draws a patterned rectangle on the right side of screen
  13110.  ' ========================================================
  13111.  
  13112.  ' ======================= EDITPATTERN =====================
  13113.  '                  Edits a tile-byte pattern
  13114.  ' =========================================================
  13115.  
  13116.  
  13117.  ' ======================= INITIALIZE ======================
  13118.  '             Sets up starting pattern and screen
  13119.  ' =========================================================
  13120.  
  13121.  ' ======================== SHOWPATTERN ====================
  13122.  '   Prints the CHR$ values used by PAINT to make pattern
  13123.  ' =========================================================
  13124.  
  13125.  SUB DrawPattern STATIC
  13126.  SHARED Pattern$
  13127.     VIEW (320, 24)-(622, 160), 0, 1  ' Set view to rectangle.
  13128.     PAINT (1, 1), Pattern$       ' Use PAINT to fill it.
  13129.     VIEW                 ' Set view to full screen.
  13130.  
  13131.  END SUB
  13132.  
  13133.  SUB EditPattern STATIC
  13134.  SHARED Pattern$, Bit%(), PatternSize%
  13135.  
  13136.     ByteNum% = 1     ' Starting position.
  13137.     BitNum% = 7
  13138.     Null$ = CHR$(0)  ' CHR$(0) is the first byte of the
  13139.                                          ' two-byte string returned when a
  13140.                                          ' direction key such as UP or DOWN is
  13141.                                          ' pressed.
  13142.     DO
  13143.  
  13144.            ' Calculate starting location on screen of this bit:
  13145.            X% = ((7 - BitNum%) * 16) + 80
  13146.            Y% = (ByteNum% + 2) * 8
  13147.  
  13148.            ' Wait for a key press (flash cursor each 3/10 second):
  13149.            State% = 0
  13150.            RefTime = 0
  13151.            DO
  13152.  
  13153.           ' Check timer and switch cursor state if 3/10 second:
  13154.           IF ABS(TIMER - RefTime) > .3 THEN
  13155.                  RefTime = TIMER
  13156.                  State% = 1 - State%
  13157.  
  13158.                  ' Turn the  border of bit on and off:
  13159.                  LINE (X% - 1, Y% - 1)-STEP(15, 8), State%, B
  13160.           END IF
  13161.  
  13162.           Check$ = INKEY$    ' Check for keystroke.
  13163.  
  13164.            LOOP WHILE Check$ = ""    ' Loop until a key is pressed.
  13165.  
  13166.            ' Erase cursor:
  13167.            LINE (X% - 1, Y% - 1)-STEP(15, 8), 0, B
  13168.  
  13169.            SELECT CASE Check$    ' Respond to keystroke.
  13170.  
  13171.            CASE CHR$(27)     ' ESC key pressed:
  13172.                   EXIT SUB       ' exit this subprogram.
  13173.            CASE CHR$(32)     ' SPACEBAR pressed:
  13174.                                                  ' reset state of bit.
  13175.  
  13176.                   ' Invert bit in pattern string:
  13177.                   CurrentByte% = ASC(MID$(Pattern$, ByteNum%, 1))
  13178.                   CurrentByte% = CurrentByte% XOR Bit%(BitNum%)
  13179.                   MID$(Pattern$, ByteNum%) = CHR$(CurrentByte%)
  13180.  
  13181.                   ' Redraw bit on screen:
  13182.                   IF (CurrentByte% AND Bit%(BitNum%)) <> 0 THEN
  13183.                           CurrentColor% = 1
  13184.                   ELSE
  13185.                           CurrentColor% = 0
  13186.                   END IF
  13187.                   LINE (X% + 1, Y% + 1)-STEP(11, 4), CurrentColor%, BF
  13188.  
  13189.            CASE CHR$(13)      ' ENTER key pressed: draw
  13190.                   DrawPattern         ' pattern in box on right.
  13191.  
  13192.            CASE Null$ + CHR$(75)  ' LEFT key: move cursor left.
  13193.  
  13194.                   BitNum% = BitNum% + 1
  13195.                   IF BitNum% > 7 THEN BitNum% = 0
  13196.  
  13197.            CASE Null$ + CHR$(77)  ' RIGHT key: move cursor right.
  13198.  
  13199.                   BitNum% = BitNum% - 1
  13200.                   IF BitNum% < 0 THEN BitNum% = 7
  13201.  
  13202.            CASE Null$ + CHR$(72)  ' UP key: move cursor up.
  13203.  
  13204.                   ByteNum% = ByteNum% - 1
  13205.                   IF ByteNum% < 1 THEN ByteNum% = PatternSize%
  13206.  
  13207.            CASE Null$ + CHR$(80)  ' DOWN key: move cursor down.
  13208.  
  13209.                   ByteNum% = ByteNum% + 1
  13210.                   IF ByteNum% > PatternSize% THEN ByteNum% = 1
  13211.            END SELECT
  13212.     LOOP
  13213.  END SUB
  13214.  
  13215.  SUB Initialize STATIC
  13216.  SHARED Pattern$, Bit%(), PatternSize%
  13217.  
  13218.     ' Set up an array holding bits in positions 0 to 7:
  13219.     FOR I% = 0 TO 7
  13220.            Bit%(I%) = 2 ^ I%
  13221.     NEXT I%
  13222.  
  13223.     CLS
  13224.  
  13225.     ' Input the pattern size (in number of bytes):
  13226.     LOCATE 5, 5
  13227.     PRINT "Enter pattern size (1-16 rows):";
  13228.     DO
  13229.            LOCATE 5, 38
  13230.            PRINT "         ";
  13231.            LOCATE 5, 38
  13232.            INPUT "", PatternSize%
  13233.     LOOP WHILE PatternSize% < 1 OR PatternSize% > 16
  13234.  
  13235.     ' Set initial pattern to all bits set:
  13236.     Pattern$ = STRING$(PatternSize%, 255)
  13237.  
  13238.     SCREEN 2     ' 640 x 200 monochrome graphics mode
  13239.  
  13240.     ' Draw dividing lines:
  13241.     LINE (0, 10)-(635, 10), 1
  13242.     LINE (300, 0)-(300, 199)
  13243.     LINE (302, 0)-(302, 199)
  13244.  
  13245.     ' Print titles:
  13246.     LOCATE 1, 13: PRINT "Pattern Bytes"
  13247.     LOCATE 1, 53: PRINT "Pattern View"
  13248.  
  13249.  
  13250.  ' Draw editing screen for pattern:
  13251.     FOR I% = 1 TO PatternSize%
  13252.  
  13253.            ' Print label on left of each line:
  13254.            LOCATE I% + 3, 8
  13255.            PRINT USING "##:"; I%
  13256.  
  13257.            ' Draw "bit" boxes:
  13258.            X% = 80
  13259.            Y% = (I% + 2) * 8
  13260.            FOR J% = 1 TO 8
  13261.                  LINE (X%, Y%)-STEP(13, 6), 1, BF
  13262.                  X% = X% + 16
  13263.            NEXT J%
  13264.     NEXT I%
  13265.  
  13266.     DrawPattern      ' Draw  "Pattern View" box.
  13267.  
  13268.     LOCATE 21, 1
  13269.     PRINT "DIRECTION keys........Move cursor"
  13270.     PRINT "SPACEBAR............Changes point"
  13271.     PRINT "ENTER............Displays pattern"
  13272.     PRINT "ESC.........................Quits";
  13273.  
  13274.  END SUB
  13275.  
  13276.  SUB ShowPattern (OK$) STATIC
  13277.  SHARED Pattern$, PatternSize%
  13278.  
  13279.     ' Return screen to 80-column text mode:
  13280.     SCREEN 0, 0
  13281.     WIDTH 80
  13282.  
  13283.     PRINT "The following characters make up your pattern:"
  13284.     PRINT
  13285.  
  13286.     ' Print out the value for each pattern byte:
  13287.     FOR I% = 1 TO PatternSize%
  13288.            PatternByte% = ASC(MID$(Pattern$, I%, 1))
  13289.            PRINT "CHR$("; LTRIM$(STR$(PatternByte%)); ")"
  13290.     NEXT I%
  13291.     PRINT
  13292.     LOCATE , , 1
  13293.     PRINT "New pattern? ";
  13294.     OK$ = UCASE$(INPUT$(1))
  13295.  END SUB
  13296.  
  13297.  
  13298.  
  13299.  ENTAB.BAS
  13300.  CD-ROM Disc Path:   \SAMPCODE\BASIC\ENTAB.BAS
  13301.  
  13302.  ' ENTAB.BAS
  13303.  '
  13304.  ' Replace runs of spaces in a file with tabs.
  13305.  '
  13306.  DECLARE SUB SetTabPos ()
  13307.  DECLARE SUB StripCommand (CLine$)
  13308.  
  13309.  
  13310.  DEFINT A-Z
  13311.  DECLARE FUNCTION ThisIsATab (Column AS INTEGER)
  13312.  
  13313.  CONST MAXLINE = 255
  13314.  CONST TABSPACE = 8
  13315.  CONST NO = 0, YES = NOT NO
  13316.  
  13317.  DIM SHARED TabStops(MAXLINE) AS INTEGER
  13318.  
  13319.  StripCommand (COMMAND$)
  13320.  
  13321.  ' Set the tab positions (uses the global array TabStops).
  13322.  SetTabPos
  13323.  
  13324.  LastColumn = 1
  13325.  
  13326.  DO
  13327.  
  13328.     CurrentColumn = LastColumn
  13329.  
  13330.  ' Replace a run of blanks with a tab when you reach a tab
  13331.  ' column. CurrentColumn is the current column read.
  13332.  ' LastColumn is the last column that was printed.
  13333.     DO
  13334.        C$ = INPUT$(1,#1)
  13335.        IF C$ <> " " AND C$ <> CHR$(9) THEN EXIT DO
  13336.        CurrentColumn = CurrentColumn + 1
  13337.        IF C$=CHR$(9) OR ThisIsATab(CurrentColumn) THEN
  13338.           ' Go to a tab column if we have a tab and this
  13339.           ' is not a tab column.
  13340.           DO WHILE NOT ThisIsATab(CurrentColumn)
  13341.              CurrentColumn=CurrentColumn+1
  13342.           LOOP
  13343.           PRINT #2, CHR$(9);
  13344.           LastColumn = CurrentColumn
  13345.        END IF
  13346.     LOOP
  13347.  
  13348.  ' Print out any blanks left over.
  13349.     DO WHILE LastColumn < CurrentColumn
  13350.        PRINT #2, " ";
  13351.        LastColumn = LastColumn + 1
  13352.     LOOP
  13353.  
  13354.  ' Print the non-blank character.
  13355.     PRINT #2, C$;
  13356.  
  13357.  ' Reset the column position if this is the end of a line.
  13358.     IF C$ = CHR$(10) THEN
  13359.        LastColumn = 1
  13360.     ELSE
  13361.        LastColumn = LastColumn + 1
  13362.     END IF
  13363.  
  13364.  LOOP UNTIL EOF(1)
  13365.  CLOSE #1, #2
  13366.  END
  13367.  
  13368.  '------------------SUB SetTabPos-------------------------
  13369.  ' Set the tab positions in the array TabStops.
  13370.  '
  13371.  SUB SetTabPos STATIC
  13372.     FOR I = 1 TO 255
  13373.        TabStops(I) = ((I MOD TABSPACE) = 1)
  13374.     NEXT I
  13375.  END SUB
  13376.  '
  13377.  '------------------SUB StripCommand----------------------
  13378.  '
  13379.  SUB StripCommand (CommandLine$) STATIC
  13380.     IF CommandLine$ = "" THEN
  13381.        INPUT "File to entab:   ", InFileName$
  13382.        INPUT "Store entabbed file in:   ", OutFileName$
  13383.     ELSE
  13384.        SpacePos = INSTR(CommandLine$, " ")
  13385.        IF SpacePos > 0 THEN
  13386.           InFileName$ = LEFT$(CommandLine$, SpacePos - 1)
  13387.           OutFileName$ = LTRIM$(MID$(CommandLine$, SpacePos))
  13388.        ELSE
  13389.           InFileName$ = CommandLine$
  13390.           INPUT "Store entabbed file in:   ", OutFileName$
  13391.        END IF
  13392.     END IF
  13393.     OPEN InFileName$ FOR INPUT AS #1
  13394.     OPEN OutFileName$ FOR OUTPUT AS #2
  13395.  END SUB
  13396.  '---------------FUNCTION ThisIsATab----------------------
  13397.  ' Answer the question, "Is this a tab position?"
  13398.  '
  13399.  FUNCTION ThisIsATab (LastColumn AS INTEGER) STATIC
  13400.     IF LastColumn > MAXLINE THEN
  13401.        ThisIsATab = YES
  13402.     ELSE
  13403.        ThisIsATab = TabStops(LastColumn)
  13404.     END IF
  13405.  END FUNCTION
  13406.  
  13407.  
  13408.  FLPT.BAS
  13409.  CD-ROM Disc Path:   \SAMPCODE\BASIC\FLPT.BAS
  13410.  
  13411.  '
  13412.  ' FLPT.BAS
  13413.  '
  13414.  ' Displays how a given real value is stored in memory.
  13415.  '
  13416.  '
  13417.  DEFINT A-Z
  13418.  DECLARE FUNCTION MHex$ (X AS INTEGER)
  13419.  DIM Bytes(3)
  13420.  
  13421.  CLS
  13422.  PRINT "Internal format of IEEE number (all values in hexadecimal)"
  13423.  PRINT
  13424.  DO
  13425.  
  13426.     ' Get the value and calculate the address of the variable.
  13427.     INPUT "Enter a real number (or END to quit): ", A$
  13428.     IF UCASE$(A$) = "END" THEN EXIT DO
  13429.     RealValue! = VAL(A$)
  13430.     ' Convert the real value to a long without changing any of
  13431.     ' the bits.
  13432.     AsLong& = CVL(MKS$(RealValue!))
  13433.     ' Make a string of hex digits, and add leading zeroes.
  13434.     Strout$ = HEX$(AsLong&)
  13435.     Strout$ = STRING$(8 - LEN(Strout$), "0") + Strout$
  13436.  
  13437.     ' Save the sign bit, and then eliminate it so it doesn't
  13438.     ' affect breaking out the bytes
  13439.     SignBit& = AsLong& AND &H80000000
  13440.     AsLong& = AsLong& AND &H7FFFFFFF
  13441.     ' Split the real value into four separate bytes
  13442.     ' --the AND removes unwanted bits; dividing by 256 shifts
  13443.     ' the value right 8 bit positions.
  13444.     FOR I = 0 TO 3
  13445.        Bytes(I) = AsLong& AND &HFF&
  13446.        AsLong& = AsLong& \ 256&
  13447.     NEXT I
  13448.     ' Display how the value appears in memory.
  13449.     PRINT
  13450.     PRINT "Bytes in Memory"
  13451.     PRINT " High    Low"
  13452.     FOR I = 1 TO 7 STEP 2
  13453.        PRINT " "; MID$(Strout$, I, 2);
  13454.     NEXT I
  13455.     PRINT : PRINT
  13456.  
  13457.     ' Set the value displayed for the sign bit.
  13458.     Sign = ABS(SignBit& <> 0)
  13459.  
  13460.     ' The exponent is the right seven bits of byte 3 and the
  13461.     ' leftmost bit of byte 2. Multiplying by 2 shifts left and
  13462.     ' makes room for the additional bit from byte 2.
  13463.     Exponent = Bytes(3) * 2 + Bytes(2) \ 128
  13464.  
  13465.     ' The first part of the mantissa is the right seven bits
  13466.     ' of byte 2.  The OR operation makes sure the implied bit
  13467.     ' is displayed by setting the leftmost bit.
  13468.     Mant1 = (Bytes(2) OR &H80)
  13469.     PRINT " Bit 31    Bits 30-23  Implied Bit & Bits 22-0"
  13470.     PRINT "Sign Bit  Exponent Bits     Mantissa Bits"
  13471.     PRINT TAB(4); Sign; TAB(17); MHex$(Exponent);
  13472.     PRINT TAB(33); MHex$(Mant1); MHex$(Bytes(1)); MHex$(Bytes(0))
  13473.     PRINT
  13474.  
  13475.  LOOP
  13476.  
  13477.  ' MHex$ makes sure we always get two hex digits.
  13478.  FUNCTION MHex$ (X AS INTEGER) STATIC
  13479.     D$ = HEX$(X)
  13480.     IF LEN(D$) < 2 THEN D$ = "0" + D$
  13481.     MHex$ = D$
  13482.  END FUNCTION
  13483.  
  13484.  
  13485.  
  13486.  FONTASM.ASM
  13487.  CD-ROM Disc Path:   \SAMPCODE\BASIC\FONTASM.ASM
  13488.  
  13489.  .MODEL        MEDIUM
  13490.  ;************************************************************
  13491.  ; FONTASM.ASM - assembly lang routines for Font Toolbox
  13492.  ;
  13493.  ;  Copyright (C) 1989 Microsoft Corporation, All Rights Reserved
  13494.  ;
  13495.  ;   fl_SetBltDir    - Sets bltchar direction increments
  13496.  ;   fl_SetBltColor  - Sets color parameter for bltchar
  13497.  ;   fl_SetBltParams - Sets font related parameters for bltchar
  13498.  ;   fl_BltChar            - Character output routine
  13499.  ;
  13500.  ;   fl_MovMem            - Moves memory
  13501.  ;   fl_ansi            - Maps IBM chars to Windows ANSI;
  13502.  ;
  13503.  ;************************************************************
  13504.  
  13505.  ; BltChar data block
  13506.  .DATA
  13507.  
  13508.  ; These are set by fl_SetBltParams
  13509.  HdrLen            dw            0            ;length of windows font file hea
  13510.  CharHeight  dw            0            ;character height
  13511.  FirstChar   dw            0            ;first character defined in font
  13512.  LastChar    dw            0            ;last character defined in font
  13513.  DefaultChar dw            0            ;default character to use
  13514.  
  13515.  ; This is set by fl_SetBltColor
  13516.  CharColor   dw            0            ;current character color
  13517.  
  13518.  ; These are set by fl_SetBltDir
  13519.  XPixInc     dw            1            ;x inc for each pixel in character bit
  13520.  YPixInc     dw            0            ;y inc for each pixel in character bit
  13521.  XRowInc     dw            0            ;x inc for each row in character bitma
  13522.  YRowInc     dw            1            ;y inc for each row in character bitma
  13523.  XColInc     dw            8            ;x inc for each column (8 bits) in cha
  13524.  YColInc     dw            0            ;y inc for each column (8 bits) in cha
  13525.  
  13526.  .CODE
  13527.  ;********************************************************************
  13528.  ; fl_SetBltDir - Sets pixel, row, and column step values for bltchar
  13529.  ;
  13530.  ;   BASIC CALL:
  13531.  ;        fl.SetBltDir XPixInc%, YPixInc%, XRowInc%, YRowInc%
  13532.  ;
  13533.  ;   Comments:
  13534.  ;        When bltchar is blt-ing a bitmap to allow the different
  13535.  ;        directions to be output it uses preset counter increments
  13536.  ;        for moving a pixel, to the next row, and to the next column
  13537.  ;        of the bitmap. The pixel and row increments are input to this
  13538.  ;        routine. The column increments are calculates as 8 times the
  13539.  ;        pixel increment.
  13540.  ;
  13541.  ;********************************************************************
  13542.  
  13543.  ; Parameters
  13544.  pXPixInc    equ     WORD PTR [bp+12]
  13545.  pYPixInc    equ     WORD PTR [bp+10]
  13546.  pXRowInc    equ     WORD PTR [bp+8]
  13547.  pYRowInc    equ     WORD PTR [bp+6]
  13548.  
  13549.              PUBLIC  fl_SetBltDir
  13550.  fl_SetBltDir PROC
  13551.  
  13552.              push    bp                ;Entry
  13553.              mov     bp,sp
  13554.  
  13555.              mov     ax,pXRowInc ;Save input parameters
  13556.              mov     XRowInc,ax
  13557.              mov     ax,pYRowInc
  13558.              mov     YRowInc,ax
  13559.  
  13560.              mov     ax,pXPixInc
  13561.              mov     XPixInc,ax
  13562.              mov     cl,3
  13563.              shl     ax,cl
  13564.              mov     XColInc,ax        ;Column increment = Pix Inc * 8
  13565.  
  13566.              mov     ax,pYPixInc
  13567.              mov     YPixInc,ax
  13568.              mov     cl,3
  13569.              shl     ax,cl
  13570.              mov     YColInc,ax        ;Column increment = Pix Inc * 8
  13571.  
  13572.              pop     bp                ;Exit
  13573.              ret     8
  13574.  fl_SetBltDir ENDP
  13575.  
  13576.  ;********************************************************************
  13577.  ; fl_SetBltColor - Sets the color of blt-ed characters
  13578.  ;
  13579.  ;   BASIC CALL:
  13580.  ;        fl.SetBltColor color
  13581.  ;
  13582.  ;********************************************************************
  13583.  
  13584.  ; Parameters
  13585.  pColor            EQU     WORD PTR [bp+6]
  13586.  
  13587.              PUBLIC  fl_SetBltColor
  13588.  fl_SetBltColor PROC
  13589.  
  13590.              push    bp                    ;Entry
  13591.              mov     bp,sp
  13592.  
  13593.              mov     ax,pColor            ;Save color in data block
  13594.              mov     CharColor,ax
  13595.  
  13596.              pop     bp                    ;Exit
  13597.              ret     2
  13598.  
  13599.  fl_SetBltColor        ENDP
  13600.  
  13601.  ;********************************************************************
  13602.  ; fl_SetBltParams - Sets font-related params for bltchar
  13603.  ;
  13604.  ;   BASIC CALL:
  13605.  ;        fl.SetBltParams HdrLen%, CharHgt%, FirstChar%, LastChar%, DefChar%
  13606.  ;
  13607.  ;********************************************************************
  13608.  
  13609.  ; Parameters
  13610.  pHdrLen     equ     WORD PTR [bp+14]
  13611.  pCharHgt    equ     WORD PTR [bp+12]
  13612.  pFirstChar  equ     WORD PTR [bp+10]
  13613.  pLastChar   equ     WORD PTR [bp+8]
  13614.  pDefChar    equ     WORD PTR [bp+6]
  13615.  
  13616.              PUBLIC  fl_SetBltParams
  13617.  fl_SetBltParams PROC
  13618.  
  13619.              push    bp                    ;Entry
  13620.              mov     bp,sp
  13621.  
  13622.              mov     ax,pHdrLen
  13623.              mov     HdrLen,ax
  13624.  
  13625.              mov     ax,pCharHgt
  13626.              mov     CharHeight,ax
  13627.  
  13628.              mov     ax,pFirstChar
  13629.              mov     FirstChar,ax
  13630.  
  13631.              mov     ax,pLastChar
  13632.              mov     LastChar,ax
  13633.  
  13634.              mov     ax,pDefChar
  13635.              mov     DefaultChar,ax
  13636.  
  13637.              pop     bp                    ;Exit
  13638.              ret     10
  13639.  
  13640.  fl_SetBltParams ENDP
  13641.  
  13642.  ;********************************************************************
  13643.  ; fl_BltChar - Outputs a character's bitmap to the screen
  13644.  ;
  13645.  ;   BASIC CALL:
  13646.  ;        fl.BltChar FontAddr(far), Char%, X%, Y%
  13647.  ;
  13648.  ;********************************************************************
  13649.  
  13650.  ; BASIC Procedures
  13651.  EXTRN            B$N1I2:far, B$PSTC:far
  13652.  
  13653.  ; Parameters
  13654.  pFASeg            equ     WORD PTR [bp+14]
  13655.  pFAOffset   equ     WORD PTR [bp+12]
  13656.  pChar            equ     WORD PTR [bp+10]
  13657.  pX            equ     WORD PTR [bp+8]
  13658.  pY            equ     WORD PTR [bp+6]
  13659.  
  13660.  ; Local Variables
  13661.  .RowX            equ     WORD PTR [bp-2]
  13662.  .RowY            equ     WORD PTR [bp-4]
  13663.  .CharWid    equ     WORD PTR [bp-6]
  13664.  .ColWid     equ     WORD PTR [bp-8]
  13665.  
  13666.              PUBLIC  fl_BltChar
  13667.  fl_BltChar  PROC
  13668.  
  13669.              push    bp                    ;Entry
  13670.              mov     bp,sp
  13671.              sub     sp,8            ;Make room for local variables
  13672.              push    di
  13673.              push    si
  13674.  
  13675.              ;Normalize font address (make offset as small as possible)
  13676.              mov     ax,pFAOffset
  13677.              mov     bx,pFASeg
  13678.              push    ax
  13679.              mov     cl,4
  13680.              shr     ax,cl            ;offset = offset div 16
  13681.              add     bx,ax            ;seg = seg + offset
  13682.              pop     ax
  13683.              and     ax,0Fh            ;offset = original offset mod 16
  13684.              mov     si,ax
  13685.              mov     es,bx
  13686.  
  13687.              ;Calculate character number
  13688.              mov     bx,pChar
  13689.              cmp     bx,LastChar
  13690.              ja            usedefchar            ;Char is > last char, use def
  13691.              sub     bx,FirstChar
  13692.              jnc     getsize            ;Char is > first char, is OK
  13693.  usedefchar: mov     bx,DefaultChar
  13694.  
  13695.              ;Get character width from character table in font
  13696.  getsize:    shl     bx,1
  13697.              shl     bx,1            ;char = char * 4
  13698.              add     bx,si            ;offset into char table
  13699.              mov     cx,es:[bx]            ;cx = character width
  13700.              mov     .CharWid,cx
  13701.  
  13702.              ;Calculate character bitmap address
  13703.              inc     bx                    ;move to next two bytes in char tab
  13704.              inc     bx
  13705.              mov     cx,es:[bx]
  13706.              add     si,cx            ;add bitmap offset into font index
  13707.              sub     si,HdrLen            ;subtract length of header
  13708.              dec     si                    ;decrement for use in output algori
  13709.  
  13710.              ;Blt character
  13711.              mov     cx,pX            ;cx = x coord
  13712.              mov     dx,pY            ;dx = y coord
  13713.  
  13714.              mov     bx,.CharWid
  13715.  
  13716.  colloop:    mov     .RowX,cx            ;save coordinates of this row
  13717.              mov     .RowY,dx
  13718.              push    bx                    ;save remaining bits in character
  13719.              cmp     bx,8            ;limit to 8 for this column
  13720.              jle     colloop2
  13721.              mov     bx,8
  13722.  
  13723.  colloop2:   mov     .ColWid,bx            ;save width of this column for othe
  13724.              mov     ax,CharHeight   ;counter for number of rows
  13725.  
  13726.  rowloop:    push    ax
  13727.              inc     si                    ;increment bitmap pointer
  13728.              mov     al,es:[si]            ;get byte from bitmap
  13729.  
  13730.  pixloop:    shl     al,1            ;check next bit (from left to right)
  13731.              jnc     nextpixel            ;skip this pixel
  13732.  
  13733.              push    ax                    ;save registers
  13734.              push    bx
  13735.              push    cx
  13736.              push    dx
  13737.              push    es
  13738.              push    si
  13739.  
  13740.              mov     ax,CharColor    ;set up params for pset call
  13741.              push    ax                    ;color
  13742.              push    cx                    ;x-coordinate
  13743.              push    dx                    ;y-coordinate
  13744.              call    B$N1I2            ;set graphics cursor location
  13745.              call    B$PSTC            ;call PSET
  13746.  
  13747.              pop     si                    ;restore registers
  13748.              pop     es
  13749.              pop     dx
  13750.              pop     cx
  13751.              pop     bx
  13752.              pop     ax
  13753.  
  13754.  nextpixel:  jz            nextrow            ;skip remaining zero bits
  13755.              add     cx,XPixInc            ;increment x and y coordinates
  13756.              add     dx,YPixInc
  13757.              dec     bx                    ;check for end of byte
  13758.              jnz     pixloop            ;go for another pixel
  13759.  
  13760.  nextrow:    mov     cx,.RowX            ;retrieve the start coord of this row
  13761.              mov     dx,.RowY
  13762.              add     cx,XRowInc            ;increment counters for next row
  13763.              add     dx,YRowInc
  13764.              mov     .RowX,cx            ;save 'em back again
  13765.              mov     .RowY,dx
  13766.              mov     bx,.ColWid            ;reset the column width
  13767.              pop     ax                    ;check for the end of this column
  13768.              dec     ax
  13769.              jnz     rowloop            ;repeat for another row
  13770.  
  13771.  nextcol:    mov     cx,pX            ;retrieve the start coord of this column
  13772.              mov     dx,pY
  13773.              add     cx,XColInc            ;increment coordinates for next col
  13774.              add     dx,YColInc
  13775.              mov     pX,cx            ;save coordinates to use after next colu
  13776.              mov     pY,dx
  13777.              pop     bx                    ;check for end of the bitmap
  13778.              sub     bx,8
  13779.              ja            colloop            ;repeat for another column
  13780.  
  13781.              ;Done
  13782.              mov     ax,.CharWid     ;return value
  13783.  
  13784.              pop     si                    ;Exit
  13785.              pop     di
  13786.              mov     sp,bp
  13787.              pop     bp
  13788.              ret     10
  13789.  fl_BltChar  ENDP
  13790.  
  13791.  ;********************************************************************
  13792.  ; fl_MovMem - Moves memory bytes
  13793.  ;
  13794.  ;   BASIC CALL:
  13795.  ;        fl.MovMem source, dest, nbytes
  13796.  ;
  13797.  ;********************************************************************
  13798.              PUBLIC  fl_MovMem
  13799.  fl_MovMem   PROC
  13800.              push    bp
  13801.              mov     bp,sp
  13802.              push    si
  13803.              push    ds
  13804.              push    di
  13805.  
  13806.              les     di,[bp+12]
  13807.              lds     si,[bp+8]
  13808.              mov     cx,[bp+6]
  13809.              rep            movsb
  13810.  
  13811.              pop     di
  13812.              pop     ds
  13813.              pop     si
  13814.              pop     bp
  13815.              ret     10
  13816.  fl_MovMem   ENDP
  13817.  
  13818.  ;********************************************************************
  13819.  ; fl_ansi - Converts IBM char to Windows ANSI mapping
  13820.  ;
  13821.  ;   BASIC CALL:
  13822.  ;        ansi_byte = fl_ansi (ibm_char%)
  13823.  ;
  13824.  ;********************************************************************
  13825.  .CODE
  13826.              PUBLIC  fl_ansi
  13827.  fl_ansi     PROC
  13828.              push    bp
  13829.              mov     bp,sp
  13830.  
  13831.              xor     ax,ax            ; zero ax
  13832.              mov     al,[bp+6]            ; move input byte to ax
  13833.              mov     bx,ax            ; copy byte to bx
  13834.              and     al,7FH            ; mask off high bit
  13835.              test    bl,80H            ; test bx to see it high bit set
  13836.              jz            fl_a_2            ; if so then byte < 128, no trans
  13837.  
  13838.              mov     bx,OFFSET _OemToAnsiTable
  13839.              xlat
  13840.  
  13841.  fl_a_2:     pop     bp
  13842.              ret     2
  13843.  fl_ansi     ENDP
  13844.  
  13845.  
  13846.  ;***************************************************************************
  13847.  ;   USA OEM/ANSI translation tables.                                       *
  13848.  ;***************************************************************************
  13849.  ;
  13850.  
  13851.  ; This translation table is used by U.S.A. and some European countries.
  13852.  ; The original IBM extended character set is now addressed as Code Page 437.
  13853.  ; With DOS 3.3 or later, IBM introduced Code Page 850 as the preeminent
  13854.  ; multilingual character set.
  13855.  
  13856.  ; this translates Oem codes >= 128 to ANSI.
  13857.  ; there are 128 entries.
  13858.  
  13859.  .DATA
  13860.  _OemToAnsiTable  label   byte
  13861.  
  13862.          db   0C7H     ; 80h  C cedilla
  13863.          db   0FCh     ; 81h  u umlaut
  13864.          db   0E9h     ; 82h  e acute
  13865.          db   0E2h     ; 83h  a circumflex
  13866.          db   0E4h     ; 84h  a umlaut
  13867.          db   0E0h     ; 85h  a grave
  13868.          db   0E5h     ; 86h  a ring
  13869.          db   0E7h     ; 87h  c cedilla
  13870.          db   0EAh     ; 88h  e circumflex
  13871.          db   0EBh     ; 89h  e umlaut
  13872.          db   0E8h     ; 8Ah  e grave
  13873.          db   0EFh     ; 8Bh  i umlaut
  13874.          db   0EEh     ; 8Ch  i circumflex
  13875.          db   0ECh     ; 8Dh  i grave
  13876.          db   0C4h     ; 8Eh  A umlaut
  13877.          db   0C5h     ; 8Fh  A ring
  13878.  
  13879.          db   0C9h     ; 90h  E acute
  13880.          db   0E6h     ; 91h  ae
  13881.          db   0C6h     ; 92h  AE
  13882.          db   0F4h     ; 93h  o circumflex
  13883.          db   0F6h     ; 94h  o umlaut
  13884.          db   0F2h     ; 95h  o grave
  13885.          db   0FBh     ; 96h  u circumflex
  13886.          db   0F9h     ; 97h  u grave
  13887.          db   0FFh     ; 98h  y umlaut
  13888.          db   0D6h     ; 99h  O umlaut
  13889.          db   0DCh     ; 9Ah  U umlaut
  13890.          db   0A2h     ; 9Bh  cent
  13891.          db   0A3h     ; 9Ch  british pound
  13892.          db   0A5h     ; 9Dh  yen
  13893.          db   070h     ; 9Eh  Pesetas
  13894.          db   066h     ; 9Fh  florin (dutch)
  13895.  
  13896.          db   0E1h     ; A0h  a acute
  13897.          db   0EDh     ; A1h  i acute
  13898.          db   0F3h     ; A2h  o acute
  13899.          db   0FAh     ; A3h  u acute
  13900.          db   0F1h     ; A4h  n tilde
  13901.          db   0D1h     ; A5h  N tilde
  13902.          db   0AAh     ; A6h  a underlined superscript
  13903.          db   0BAh     ; A7h  o underlined superscript
  13904.          db   0BFh     ; A8h  inverted question mark
  13905.          db   05Fh     ; A9h  left top corner
  13906.          db   0ACh     ; AAh  right top corner
  13907.          db   0BDh     ; ABh  1/2
  13908.          db   0BCh     ; ACh  1/4
  13909.          db   0A1h     ; ADh  inverted point
  13910.          db   0ABh     ; AEh  <<
  13911.          db   0BBh     ; AFh  >>
  13912.  
  13913.          db   05Fh     ; B0h  here begins semigraphic characters
  13914.          db   05Fh     ; B1h
  13915.          db   05Fh     ; B2h
  13916.          db   0A6h     ; B3h  Vertical bar
  13917.          db   05Fh     ; B4h
  13918.          db   05Fh     ; B5h
  13919.          db   05Fh     ; B6h
  13920.          db   05Fh     ; B7h
  13921.          db   05Fh     ; B8h
  13922.          db   05Fh     ; B9h
  13923.          db   05Fh     ; BAh
  13924.          db   05Fh     ; BBh
  13925.          db   05Fh     ; BCh
  13926.          db   05Fh     ; BDh
  13927.          db   05Fh     ; BEh
  13928.          db   05Fh     ; BFh
  13929.  
  13930.          db   05Fh     ; C0h
  13931.          db   05Fh     ; C1h
  13932.          db   05Fh     ; C2h
  13933.          db   05Fh     ; C3h
  13934.          db   05Fh     ; C4h
  13935.          db   05Fh     ; C5h
  13936.          db   05Fh     ; C6h
  13937.          db   05Fh     ; C7h
  13938.          db   05Fh     ; C8h
  13939.          db   05Fh     ; C9h
  13940.          db   05Fh     ; CAh
  13941.          db   05Fh     ; CBh
  13942.          db   05Fh     ; CCh
  13943.          db   05Fh     ; CDh
  13944.          db   05Fh     ; CEh
  13945.          db   05Fh     ; CFh
  13946.  
  13947.          db   05Fh     ; D0h
  13948.          db   05Fh     ; D1h
  13949.          db   05Fh     ; D2h
  13950.          db   05Fh     ; D3h
  13951.          db   05Fh     ; D4h
  13952.          db   05Fh     ; D5h
  13953.          db   05Fh     ; D6h
  13954.          db   05Fh     ; D7h
  13955.          db   05Fh     ; D8h
  13956.          db   05Fh     ; D9h
  13957.          db   05Fh     ; DAh
  13958.          db   05Fh     ; DBh
  13959.          db   05Fh     ; DCh
  13960.          db   05Fh     ; DDh
  13961.          db   05Fh     ; DEh
  13962.          db   05Fh     ; DFh  end of semigraphic characters
  13963.  
  13964.          db   05Fh     ; E0h  alpha
  13965.          db   0DFh     ; E1h  german sharp S or greek beta
  13966.          db   05Fh     ; E2h  lambda
  13967.          db   0B6h     ; E3h  pi
  13968.          db   05Fh     ; E4h  sigma uc
  13969.          db   05Fh     ; E5h  sigma lc
  13970.          db   0B5h     ; E6h  mu
  13971.          db   05Fh     ; E7h  tau
  13972.          db   05Fh     ; E8h  phi uc
  13973.          db   05Fh     ; E9h  theta
  13974.          db   05Fh     ; EAh  omega
  13975.          db   05Fh     ; EBh  delta
  13976.          db   05Fh     ; ECh  infinite
  13977.          db   0D8h     ; EDh  math empty set or phi lc
  13978.          db   05Fh     ; EEh  math own sign
  13979.          db   05Fh     ; EFh  math include sign
  13980.  
  13981.          db   05Fh     ; F0h  math equivalence sign
  13982.          db   0B1h     ; F1h  + underlined
  13983.          db   05Fh     ; F2h  greater equal
  13984.          db   05Fh     ; F3h  less equal
  13985.          db   05Fh     ; F4h  math integral upper part
  13986.          db   05Fh     ; F5h  math integral lower part
  13987.          db   05Fh     ; F6h  math divide
  13988.          db   05Fh     ; F7h  math approximately (~)
  13989.          db   0B0h     ; F8h  degree
  13990.          db   0B7h     ; F9h  period accent (bold)
  13991.          db   0B7h     ; FAh  period accent
  13992.          db   05Fh     ; FBh  math root
  13993.          db   06Eh     ; FCh  n superscript
  13994.          db   0B2h     ; FDh  2 superscript
  13995.          db   05Fh     ; FEh
  13996.          db   05Fh     ; FFh  blank
  13997.  
  13998.          END
  13999.  
  14000.  
  14001.  FONTB.BAS
  14002.  CD-ROM Disc Path:   \SAMPCODE\BASIC\FONTB.BAS
  14003.  
  14004.  '*** FONTB.BAS - Font Routines for the Presentation Graphics Toolbox in
  14005.  '           Microsoft BASIC 7.0, Professional Development System
  14006.  '              Copyright (C) 1987-1989, Microsoft Corporation
  14007.  '
  14008.  '  NOTE:  This sample source code toolbox is intended to demonstrate some
  14009.  '  of the extended capabilities of Microsoft BASIC 7.0 Professional Developme
  14010.  '  system that can help to leverage the professional developer's time more
  14011.  '  effectively.  While you are free to use, modify, or distribute the routine
  14012.  '  in this module in any way you find useful, it should be noted that these a
  14013.  '  examples only and should not be relied upon as a fully-tested "add-on"
  14014.  '  library.
  14015.  '
  14016.  '  PURPOSE:  These are the toolbox routines to handle graphics text using
  14017.  '            Windows format raster font files:
  14018.  '
  14019.  '  To create a library and QuickLib containing the font routines found
  14020.  '  in this file, follow these steps:
  14021.  '       BC /X/FS fontb.bas
  14022.  '       LIB fontb.lib + fontb + fontasm + qbx.lib;
  14023.  '       LINK /Q fontb.lib, fontb.qlb,,qbxqlb.lib;
  14024.  '  If you are going to use this FONTB.QLB QuickLib in conjunction with
  14025.  '  the charting source code (CHRTB.BAS) or the UI toobox source code
  14026.  '  (GENERAL.BAS, WINDOW.BAS, MENU.BAS and MOUSE.BAS), you need to
  14027.  '  include the assembly code routines referenced in these files.  For the
  14028.  '  charting routines, create FONTB.LIB as follows before you create the
  14029.  '  QuickLib:
  14030.  '       LIB fontb.lib + fontb + fontasm + chrtasm + qbx.lib;
  14031.  '  For the UI toolbox routines, create the library as follows:
  14032.  '       LIB fontb.lib + fontb + fontasm + uiasm + qbx.lib;
  14033.  '**************************************************************************
  14034.  
  14035.  ' $INCLUDE: 'QBX.BI'
  14036.  ' $INCLUDE: 'FONTB.BI'
  14037.  
  14038.  CONST cFALSE = 0              ' Logical False
  14039.  CONST cTRUE = NOT cFALSE      ' Logical True
  14040.  
  14041.  CONST cDefaultColor = 15      ' Default character color (white in all modes)
  14042.  CONST cDefaultDir = 0         ' Default character direction
  14043.  CONST cDefaultFont = 1        ' Default font selected in LoadFont
  14044.  
  14045.  CONST cMaxFaceName = 32       ' Maximum length of a font name
  14046.  CONST cMaxFileName = 66       ' Maximum length of a font file name
  14047.  CONST cFontResource = &H8008  ' Identifies a font resource
  14048.  CONST cBitMapType = 0         ' Bitmap font type
  14049.  
  14050.  CONST cFileFont = 0           ' Font comes from file
  14051.  CONST cMemFont = 1            ' Font comes from memory
  14052.  
  14053.  CONST cSizeFontHeader = 118   ' Size of Windows font header
  14054.  
  14055.  ' *********************************************************************
  14056.  ' Data Types:
  14057.  
  14058.  ' Some global variables used:
  14059.  TYPE GlobalParams
  14060.          MaxRegistered     AS INTEGER     ' Max number of registered fonts all
  14061.          MaxLoaded         AS INTEGER     ' Max number of loaded fonts allowed
  14062.          TotalRegistered   AS INTEGER     ' Number of fonts actually registere
  14063.          TotalLoaded       AS INTEGER     ' Number of fonts actually loaded
  14064.  
  14065.          NextDataBlock     AS INTEGER     ' Next available block in font buffe
  14066.  
  14067.          CurrentFont       AS INTEGER     ' Current font number in loaded font
  14068.          CHeight           AS INTEGER     ' Character height of current font
  14069.          FChar             AS INTEGER     ' First char in font
  14070.          LChar             AS INTEGER     ' Last char in font
  14071.          DChar             AS INTEGER     ' Default char for font
  14072.          DSeg              AS INTEGER     ' Segment of current font
  14073.          DOffset           AS INTEGER     ' Offset of current font
  14074.          FontSource        AS INTEGER     ' Source of current font (File or Me
  14075.  
  14076.          CharColorInit     AS INTEGER     ' cFALSE (0) means color not initial
  14077.          CharColor         AS INTEGER     ' Character color
  14078.          CharDirInit       AS INTEGER     ' cFALSE (0) means dir not initializ
  14079.          CharDir           AS INTEGER     ' Character direction
  14080.          CharSet           AS INTEGER     ' Character mappings to use
  14081.  
  14082.          XPixInc           AS INTEGER     ' X increment direction (0, 1, -1)
  14083.          YPixInc           AS INTEGER     ' Y increment direction (0, 1, -1)
  14084.  
  14085.          WindowSet         AS INTEGER     ' cTRUE if GTextWindow has been call
  14086.          WX1               AS SINGLE      ' Minimum WINDOW X
  14087.          WY1               AS SINGLE      ' Minimum WINDOW Y
  14088.          WX2               AS SINGLE      ' Maximum WINDOW X
  14089.          WY2               AS SINGLE      ' Maximum WINDOW Y
  14090.          WScrn             AS INTEGER     ' cTRUE means Y increases top to bot
  14091.  
  14092.  END TYPE
  14093.  
  14094.  ' The following 3 types are needed to read .FON files. They are documented
  14095.  ' in chapter 7 of the MS Windows Programmer's Reference:
  14096.  
  14097.  ' Windows font file header:
  14098.  TYPE WFHeader
  14099.          dfVersion         AS INTEGER
  14100.          dfSize            AS LONG
  14101.          dfCopyright       AS STRING * 60
  14102.          dfType            AS INTEGER
  14103.          dfPoints          AS INTEGER
  14104.          dfVertRes         AS INTEGER
  14105.          dfHorizRes        AS INTEGER
  14106.          dfAscent          AS INTEGER
  14107.          dfInternalLeading AS INTEGER
  14108.          dfExternalLeading AS INTEGER
  14109.          dfItalic          AS STRING * 1
  14110.          dfUnderline       AS STRING * 1
  14111.          dfStrikeOut       AS STRING * 1
  14112.          dfWeight          AS INTEGER
  14113.          dfCharSet         AS STRING * 1
  14114.          dfPixWidth        AS INTEGER
  14115.          dfPixHeight       AS INTEGER
  14116.          dfPitchAndFamily  AS STRING * 1
  14117.          dfAvgWidth        AS INTEGER
  14118.          dfMaxWidth        AS INTEGER
  14119.          dfFirstChar       AS STRING * 1
  14120.          dfLastChar        AS STRING * 1
  14121.          dfDefaultChar     AS STRING * 1
  14122.          dfBreakChar       AS STRING * 1
  14123.          dfWidthBytes      AS INTEGER
  14124.          dfDevice          AS LONG
  14125.          dfFace            AS LONG
  14126.          dfBitsPointer     AS LONG
  14127.          dfBitsOffset      AS LONG
  14128.          pad               AS STRING * 1  ' To ensure word boundry
  14129.  END TYPE
  14130.  
  14131.  ' Structure for reading resource type and number from a resource
  14132.  ' table:
  14133.  TYPE ResType
  14134.          TypeID            AS INTEGER
  14135.          NumResource       AS INTEGER
  14136.          Reserved          AS LONG
  14137.  END TYPE
  14138.  
  14139.  ' Structure for reading an actual resource entry:
  14140.  TYPE ResEntry
  14141.          AddrOffset        AS INTEGER
  14142.          Length            AS INTEGER
  14143.          ResourceKeywd     AS INTEGER
  14144.          ResID             AS INTEGER
  14145.          Reserved1         AS LONG
  14146.  END TYPE
  14147.  
  14148.  ' Internal font header data type:
  14149.  TYPE IFontInfo
  14150.          Status            AS INTEGER  ' Processing status. 0=unproc. else <>0
  14151.          FontHeader        AS WFHeader ' The Windows font header
  14152.          FaceName          AS STRING * cMaxFaceName   ' Font name
  14153.          FileName          AS STRING * cMaxFileName   ' File name
  14154.          FontSource        AS INTEGER  ' 0=file, 1=memory
  14155.          FileLoc           AS LONG     ' Location in resource file of font fil
  14156.          DataSeg           AS INTEGER  ' FontData index or Segment address of
  14157.          DataOffset        AS INTEGER  ' Offset  address of font if in memory
  14158.          BitsOffset        AS INTEGER  ' Offset from beginning of data to bitm
  14159.  END TYPE
  14160.  
  14161.  ' Type for selecting registered fonts via LoadFont:
  14162.  TYPE FontSpec
  14163.          FaceName    AS STRING * cMaxFaceName
  14164.          Pitch       AS STRING * 1
  14165.          PointSize   AS INTEGER     ' Fonts point size
  14166.          HorizRes    AS INTEGER     ' Horizontal resolution of font
  14167.          VertRes     AS INTEGER     ' Vertical resolution of font
  14168.          ScrnMode    AS INTEGER     ' Screen mode
  14169.          Height      AS INTEGER     ' Pixel height of font
  14170.  
  14171.          Best        AS INTEGER     ' "Best" flag (true/false)
  14172.  
  14173.          RegNum      AS INTEGER     ' Number of font in registered list
  14174.  
  14175.          InMemory    AS INTEGER     ' Whether font is in memory (true/false)
  14176.          HdrSeg      AS INTEGER     ' Segment of font in memory
  14177.          HdrOff      AS INTEGER     ' Offset of font in segment
  14178.          DataSeg     AS INTEGER     ' Segment of data in memory
  14179.          DataOff     AS INTEGER     ' Offset of data in segment
  14180.  END TYPE
  14181.  
  14182.  ' *********************************************************************
  14183.  ' Routine Declarations:
  14184.  
  14185.  DECLARE SUB flSetFontErr (ErrNum AS INTEGER)
  14186.  DECLARE SUB flClearFontErr ()
  14187.  DECLARE SUB flRegisterFont (FileName$, FileNum%)
  14188.  DECLARE SUB flReadFont (I%)
  14189.  DECLARE SUB flSizeFontBuffer (NFonts%)
  14190.  DECLARE SUB flInitSpec (Spec AS ANY)
  14191.  DECLARE SUB flClearFontStatus ()
  14192.  DECLARE SUB flGetCurrentScrnSize (XPixels%, YPixels%)
  14193.  DECLARE SUB flGetBASICScrnSize (ScrnMode%, XPixels%, YPixels%)
  14194.  DECLARE SUB flInitMask ()
  14195.  DECLARE SUB flPSET (X%, Y%, Colr%)
  14196.  DECLARE SUB flChkMax ()
  14197.  
  14198.  DECLARE FUNCTION flGetFonts! (NFonts%)
  14199.  DECLARE FUNCTION flMatchFont! (FSpec AS ANY)
  14200.  DECLARE FUNCTION flGetNum! (Txt$, ChPos%, Default!, ErrV!)
  14201.  DECLARE FUNCTION flGetNextSpec! (SpecTxt$, ChPos%, Spec AS ANY)
  14202.  DECLARE FUNCTION flDoNextResource! (Align%, FileName$, FileNum%)
  14203.  DECLARE FUNCTION flOutGChar% (X%, Y%, ChVal%)
  14204.  
  14205.  ' -- Assembly language routines
  14206.  DECLARE SUB flMovMem ALIAS "fl_MovMem" (SEG dest AS ANY, BYVAL SrcSeg AS INTE
  14207.  DECLARE FUNCTION flANSI% ALIAS "fl_ansi" (BYVAL I%)
  14208.  
  14209.  DECLARE SUB flSetBltDir ALIAS "fl_SetBltDir" (BYVAL XPixInc%, BYVAL YPixInc%,
  14210.  DECLARE SUB flSetBltColor ALIAS "fl_SetBltColor" (BYVAL CharColor%)
  14211.  DECLARE SUB flSetBltParams ALIAS "fl_SetBltParams" (BYVAL HdrLen%, BYVAL Char
  14212.  DECLARE FUNCTION flbltchar% ALIAS "fl_BltChar" (BYVAL FASeg%, BYVAL FAOffset%
  14213.  
  14214.  ' *********************************************************************
  14215.  ' Variable Definitions:
  14216.  
  14217.  ' The following arrays hold font headers and font data as fonts are
  14218.  ' registered and loaded. They are dynamically allocated so they can be
  14219.  ' changed in size to accomodate the number of fonts a program will be
  14220.  ' using:
  14221.  
  14222.  ' $DYNAMIC
  14223.  
  14224.  ' Array to hold header information for registered fonts:
  14225.  DIM SHARED FontHdrReg(1 TO 10)  AS IFontInfo
  14226.  
  14227.  ' Arrays to hold header information and registered font numbers
  14228.  ' for loaded fonts:
  14229.  DIM SHARED FontHdrLoaded(1 TO 10) AS IFontInfo
  14230.  DIM SHARED FontLoadList(1 TO 10) AS INTEGER
  14231.  
  14232.  ' Array to hold font data information:
  14233.  DIM SHARED FontData(1 TO 1) AS FontDataBlock
  14234.  
  14235.  ' $STATIC
  14236.  
  14237.  ' Structure holding global parameters:
  14238.  DIM SHARED FGP AS GlobalParams
  14239.  
  14240.  ' Error handler for flChkMax so these arrays will be dimensioned
  14241.  ' to 10 by default:
  14242.  SetMax:
  14243.          REDIM FontHdrLoaded(1 TO 10) AS IFontInfo
  14244.          REDIM FontHdrReg(1 TO 10) AS IFontInfo
  14245.          REDIM FontLoadList(1 TO 10) AS INTEGER
  14246.          RESUME
  14247.  
  14248.  ' Error handler for out of memory error:
  14249.  MemErr:
  14250.          flSetFontErr cNoFontMem
  14251.          RESUME NEXT
  14252.  
  14253.  ' Error handler for unexpected errors:
  14254.  UnexpectedErr:
  14255.          flSetFontErr cFLUnexpectedErr + ERR
  14256.          RESUME NEXT
  14257.  
  14258.  ' File not found error: RegisterFonts
  14259.  NoFileErr:
  14260.          flSetFontErr cFileNotFound
  14261.          RESUME NEXT
  14262.  
  14263.  '=== flChkMax - Makes sure that max font settings are correct and
  14264.  '                enforces default of 10 for max loaded and registered
  14265.  '
  14266.  '  Arguments:
  14267.  '     none
  14268.  '
  14269.  '  Return Values:
  14270.  '     none
  14271.  '
  14272.  '=================================================================
  14273.  SUB flChkMax STATIC
  14274.  SHARED FontHdrLoaded() AS IFontInfo
  14275.  SHARED FontHdrReg() AS IFontInfo
  14276.  SHARED FGP AS GlobalParams
  14277.  
  14278.  ' Make sure that GP.MaxLoaded and GP.MaxRegistered match array dimensions
  14279.  ' this will only happen if user hasn't used SetMaxFonts and allows Fontlib
  14280.  ' to set a default of 10 since that is what the arrays are first DIM'd
  14281.  ' to:
  14282.  
  14283.  ON ERROR GOTO SetMax
  14284.  FGP.MaxLoaded = UBOUND(FontHdrLoaded)
  14285.  FGP.MaxRegistered = UBOUND(FontHdrReg)
  14286.  ON ERROR GOTO UnexpectedErr
  14287.  
  14288.  END SUB
  14289.  
  14290.  '=== flClearFontErr - Sets the FontErr variable to 0
  14291.  '
  14292.  '  Arguments:
  14293.  '     none
  14294.  '
  14295.  '  Return Values:
  14296.  '     none
  14297.  '
  14298.  '=================================================================
  14299.  SUB flClearFontErr STATIC
  14300.  
  14301.          FontErr = 0
  14302.  
  14303.  END SUB
  14304.  
  14305.  '=== flClearFontStatus - Clears the status field in the registered font list
  14306.  '
  14307.  '  Arguments:
  14308.  '     none
  14309.  '
  14310.  '=================================================================
  14311.  SUB flClearFontStatus STATIC
  14312.  SHARED FGP AS GlobalParams
  14313.  SHARED FontHdrReg() AS IFontInfo
  14314.  
  14315.  FOR I% = 1 TO FGP.TotalRegistered
  14316.          FontHdrReg(I%).Status = 0
  14317.  NEXT I%
  14318.  
  14319.  END SUB
  14320.  
  14321.  '=== flDoNextResource - Processes resource from resource table:
  14322.  '
  14323.  '  Arguments:
  14324.  '     Align%      - Alignment shift count for finding resource data
  14325.  '
  14326.  '     FileName$   - Name of font file (passed to routine that actually
  14327.  '                   registers resource entry)
  14328.  '
  14329.  '     FileNum%    - File number for reading
  14330.  '
  14331.  '  Return Value:
  14332.  '     The number of fonts actually registered
  14333.  '
  14334.  '=================================================================
  14335.  FUNCTION flDoNextResource (Align%, FileName$, FileNum%) STATIC
  14336.  DIM ResID AS ResType, Entry AS ResEntry
  14337.  
  14338.  ' Get the first few bytes identifying the resource type and the number
  14339.  ' of this type:
  14340.  GET FileNum%, , ResID
  14341.  
  14342.  ' If this is not the last resource then process it:
  14343.  IF ResID.TypeID <> 0 THEN
  14344.  
  14345.          ' Loop through the entries of this resource and if an entry happens t
  14346.          ' a font resource then register it. The file location must be saved
  14347.          ' for each entry in the resource table since the flRegisterFont
  14348.          ' routine may go to some other part of the file to read the resource:
  14349.          FOR ResourceEntry = 1 TO ResID.NumResource
  14350.  
  14351.                  GET FileNum%, , Entry
  14352.                  NextResLoc# = SEEK(FileNum%)
  14353.                  IF ResID.TypeID = cFontResource THEN
  14354.  
  14355.                          ' Seek to font information, register it, then seek ba
  14356.                          ' the next resource table entry:
  14357.                          SEEK FileNum%, Entry.AddrOffset * 2 ^ Align% + 1
  14358.                          flRegisterFont FileName$, FileNum%
  14359.                          SEEK FileNum%, NextResLoc#
  14360.                          IF FontErr <> 0 THEN EXIT FUNCTION
  14361.  
  14362.                  END IF
  14363.  
  14364.          NEXT ResourceEntry
  14365.  END IF
  14366.  
  14367.  ' Return the current resource type so that RegisterFonts knows when the
  14368.  ' last resource has been read:
  14369.  flDoNextResource = ResID.TypeID
  14370.  
  14371.  END FUNCTION
  14372.  
  14373.  '=== flGetBASICScrnSize - Returns screen size for specified BASIC screen mode
  14374.  '
  14375.  '  Arguments:
  14376.  '
  14377.  '     ScrnMode%   -  BASIC screen mode
  14378.  '
  14379.  '     XPixels%    -  Number of pixels in horizontal direction
  14380.  '
  14381.  '     YPixels%    -  Number of pixels in vertical direction
  14382.  '
  14383.  '=================================================================
  14384.  SUB flGetBASICScrnSize (ScrnMode%, XPixels%, YPixels%) STATIC
  14385.          SELECT CASE ScrnMode%
  14386.                  CASE 1: XPixels% = 320: YPixels% = 200
  14387.                  CASE 2: XPixels% = 640: YPixels% = 200
  14388.                  CASE 3: XPixels% = 720: YPixels% = 348
  14389.                  CASE 4: XPixels% = 640: YPixels% = 400
  14390.                  CASE 7: XPixels% = 320: YPixels% = 200
  14391.                  CASE 8: XPixels% = 640: YPixels% = 200
  14392.                  CASE 9: XPixels% = 640: YPixels% = 350
  14393.                  CASE 10: XPixels% = 640: YPixels% = 350
  14394.                  CASE 11: XPixels% = 640: YPixels% = 480
  14395.                  CASE 12: XPixels% = 640: YPixels% = 480
  14396.                  CASE 13: XPixels% = 320: YPixels% = 200
  14397.                  CASE ELSE: XPixels% = 0: YPixels% = 0
  14398.          END SELECT
  14399.  END SUB
  14400.  
  14401.  '=== flGetCurrentScrnSize - Returns screen size for current screen mode
  14402.  '
  14403.  '  Arguments:
  14404.  '
  14405.  '     XPixels%    -  Number of pixels in horizontal direction
  14406.  '
  14407.  '     YPixels%    -  Number of pixels in vertical direction
  14408.  '
  14409.  '=================================================================
  14410.  SUB flGetCurrentScrnSize (XPixels%, YPixels%) STATIC
  14411.  DIM Regs AS RegType
  14412.  
  14413.  ' Use DOS interrupt to get current video display mode:
  14414.  Regs.ax = &HF00
  14415.  CALL INTERRUPT(&H10, Regs, Regs)
  14416.  
  14417.  ' Set screen size based on mode:
  14418.  SELECT CASE Regs.ax MOD 256
  14419.          CASE &H4: XPixels% = 320: YPixels% = 200
  14420.          CASE &H5: XPixels% = 320: YPixels% = 200
  14421.          CASE &H6: XPixels% = 640: YPixels% = 200
  14422.          CASE &H7: XPixels% = 720: YPixels% = 350
  14423.          CASE &H8: XPixels% = 720: YPixels% = 348     ' Hercules
  14424.          CASE &HD: XPixels% = 320: YPixels% = 200
  14425.          CASE &HE: XPixels% = 640: YPixels% = 200
  14426.          CASE &HF: XPixels% = 640: YPixels% = 350
  14427.          CASE &H10: XPixels% = 640: YPixels% = 350
  14428.          CASE &H11: XPixels% = 640: YPixels% = 480
  14429.          CASE &H12: XPixels% = 640: YPixels% = 480
  14430.          CASE &H13: XPixels% = 320: YPixels% = 200
  14431.          CASE &H40: XPixels% = 640: YPixels% = 400    ' Olivetti
  14432.          CASE ELSE: XPixels% = 0: YPixels = 0
  14433.  END SELECT
  14434.  END SUB
  14435.  
  14436.  '=== flGetFonts - Gets fonts specified in FontLoadList
  14437.  '
  14438.  '  Arguments:
  14439.  '     NFonts%  -  Number of fonts to load
  14440.  '
  14441.  '  Return Values:
  14442.  '     Number of fonts successfully loaded
  14443.  '
  14444.  '=================================================================
  14445.  FUNCTION flGetFonts (NFonts%) STATIC
  14446.  SHARED FGP AS GlobalParams
  14447.  SHARED FontHdrReg() AS IFontInfo
  14448.  SHARED FontHdrLoaded() AS IFontInfo
  14449.  SHARED FontLoadList() AS INTEGER
  14450.  
  14451.  ' Re-dimension font data buffer to fit all the fonts:
  14452.  flSizeFontBuffer (NFonts%)
  14453.  IF FontErr = cNoFontMem THEN EXIT FUNCTION
  14454.  
  14455.  ' Clear the font status variables then load the fonts (the status variable
  14456.  ' is used to record which ones have already been loaded so they aren't
  14457.  ' loaded more than once):
  14458.  flClearFontStatus
  14459.  FOR Font% = 1 TO NFonts%
  14460.          FontNum% = FontLoadList(Font%)
  14461.  
  14462.          ' If font already loaded then just copy the already-filled-out header
  14463.          ' to the new slot:
  14464.          IF FontHdrReg(FontNum%).Status <> 0 THEN
  14465.                  FontHdrLoaded(Font%) = FontHdrLoaded(FontHdrReg(FontNum%).Sta
  14466.  
  14467.          ' Otherwise, read the font and update status in registered version
  14468.          ' to point to the first slot it was loaded into (so we can go get
  14469.          ' an already-filled-out header from there):
  14470.          ELSE
  14471.                  FontHdrLoaded(Font%) = FontHdrReg(FontNum%)
  14472.  
  14473.                  ' Hold any existing errors:
  14474.                  HoldErr% = FontErr
  14475.                  flClearFontErr
  14476.  
  14477.                  flReadFont Font%
  14478.  
  14479.                  ' If there was an error in reading font, exit. Otherwise,
  14480.                  ' reset the error to what it was before and continue:
  14481.                  IF FontErr <> 0 THEN
  14482.                          flGetFonts = FontNum% - 1
  14483.                          EXIT FUNCTION
  14484.                  ELSE
  14485.                          flSetFontErr HoldErr%
  14486.                  END IF
  14487.  
  14488.                  FontHdrReg(FontNum%).Status = Font%
  14489.          END IF
  14490.  NEXT Font%
  14491.  
  14492.  flGetFonts = NFonts%
  14493.  END FUNCTION
  14494.  
  14495.  '=== flGetNextSpec - Parses the next spec from the spec string
  14496.  '
  14497.  '  Arguments:
  14498.  '     SpecTxt$ -  String containing font specifications
  14499.  '
  14500.  '     ChPos%   -  Current position in string (updated in this routine)
  14501.  '
  14502.  '     Spec     -  Structure to contain parsed values
  14503.  '
  14504.  '
  14505.  '  Return Values:
  14506.  '     0    -  Spec was found
  14507.  '
  14508.  '     1    -  No spec found
  14509.  '
  14510.  '     2    -  Invalid spec found
  14511.  '=================================================================
  14512.  FUNCTION flGetNextSpec (SpecTxt$, ChPos%, Spec AS FontSpec) STATIC
  14513.  
  14514.  ' Initialize some things:
  14515.  SpecErr = cFALSE
  14516.  SpecLen% = LEN(SpecTxt$)
  14517.  
  14518.  ' If character pos starts past end of spec then we're done:
  14519.  IF ChPos% > SpecLen% THEN
  14520.          flGetNextSpec = 1
  14521.          EXIT FUNCTION
  14522.  END IF
  14523.  
  14524.  DO UNTIL ChPos% > SpecLen%
  14525.  
  14526.          Param$ = UCASE$(MID$(SpecTxt$, ChPos%, 1))
  14527.          ChPos% = ChPos% + 1
  14528.  
  14529.          SELECT CASE Param$
  14530.  
  14531.                  ' Skip blanks:
  14532.                  CASE " ":
  14533.  
  14534.                  ' Font title:
  14535.                  CASE "T":
  14536.  
  14537.                          ' Scan for font title until blank or end of string:
  14538.                          StartPos% = ChPos%
  14539.                          DO UNTIL ChPos% > SpecLen%
  14540.                                  Char$ = MID$(SpecTxt$, ChPos%, 1)
  14541.                                  ChPos% = ChPos% + 1
  14542.                          LOOP
  14543.  
  14544.                          ' Extract the title:
  14545.                          TitleLen% = ChPos% - StartPos%
  14546.                          IF TitleLen% <= 0 THEN
  14547.                                  SpecErr = cTRUE
  14548.                          ELSE
  14549.                                  Spec.FaceName = MID$(SpecTxt$, StartPos%, Tit
  14550.                          END IF
  14551.  
  14552.                  ' Fixed or Proportional font:
  14553.                  CASE "F", "P":
  14554.                          Spec.Pitch = Param$
  14555.  
  14556.                  ' Font Size (default to 12 points):
  14557.                  CASE "S":
  14558.                          Spec.PointSize = flGetNum(SpecTxt$, ChPos%, 12, SpecE
  14559.  
  14560.                  ' Screen Mode:
  14561.                  CASE "M":
  14562.                          Spec.ScrnMode = flGetNum(SpecTxt$, ChPos%, -1, SpecEr
  14563.  
  14564.                  ' Pixel Height:
  14565.                  CASE "H":
  14566.                          Spec.Height = flGetNum(SpecTxt$, ChPos%, 0, SpecErr)
  14567.  
  14568.                  ' Best fit:
  14569.                  CASE "B":
  14570.                          Spec.Best = cTRUE
  14571.  
  14572.                  ' Registered font number:
  14573.                  CASE "N":
  14574.                          Spec.RegNum = flGetNum(SpecTxt$, ChPos%, 0, SpecErr)
  14575.  
  14576.                  ' Font in memory:
  14577.                  CASE "R":
  14578.                          Spec.InMemory = cTRUE
  14579.  
  14580.                  ' Spec separator:
  14581.                  CASE "/":
  14582.                          EXIT DO
  14583.  
  14584.                  ' Anything else is an error:
  14585.                  CASE ELSE:
  14586.                          SpecErr = cTRUE
  14587.                          ChPos% = ChPos% + 1
  14588.          END SELECT
  14589.  LOOP
  14590.  
  14591.  ' Spec is parsed, make sure a valid screen mode has been specified and
  14592.  ' adjust point sizes for 320x200 screens if necessary:
  14593.  IF Spec.PointSize <> 0 THEN
  14594.  
  14595.          ' Get screen size for specified mode (with "M" param) or current
  14596.          ' screen mode:
  14597.          IF Spec.ScrnMode < 0 THEN
  14598.                  flGetCurrentScrnSize XPixels%, YPixels%
  14599.          ELSE
  14600.                  flGetBASICScrnSize Spec.ScrnMode, XPixels%, YPixels%
  14601.          END IF
  14602.  
  14603.          ' If this isn't a graphics mode then set an error and skip the rest:
  14604.          IF XPixels% = 0 THEN
  14605.                  SpecErr = cTRUE
  14606.                  Spec.PointSize = 0
  14607.  
  14608.          ' If this is a 320x200 screen mode adjust point sizes to the
  14609.          ' equivalent EGA font point sizes. Also set the horizontal
  14610.          ' a vertical resolutions to search for in fonts (horizontal is
  14611.          ' 96 for all modes, vertical varies):
  14612.          ELSE
  14613.  
  14614.                  ' Use a horizontal resolution of 96 for all screens:
  14615.                  Spec.HorizRes = 96
  14616.  
  14617.                  IF XPixels% = 320 THEN
  14618.                          Spec.VertRes = 72
  14619.  
  14620.                          ' In a 320x200 mode scale point sizes to their equiva
  14621.                          ' EGA fonts (special case 14 and 24 point fonts to ma
  14622.                          ' to the closest EGA font otherwise multiply point si
  14623.                          ' 2/3:
  14624.                          SELECT CASE Spec.PointSize
  14625.                                  CASE 14: Spec.PointSize = 10
  14626.                                  CASE 24: Spec.PointSize = 18
  14627.                                  CASE ELSE: Spec.PointSize = Spec.PointSize *
  14628.                          END SELECT
  14629.  
  14630.                  ELSE
  14631.  
  14632.                          ' Other screen modes vary only in vertical resolution
  14633.                          SELECT CASE YPixels%
  14634.                                  CASE 200: Spec.VertRes = 48
  14635.                                  CASE 350: Spec.VertRes = 72
  14636.                                  CASE 480: Spec.VertRes = 96
  14637.                          END SELECT
  14638.                  END IF
  14639.          END IF
  14640.  END IF
  14641.  
  14642.  ' If an error was found somewhere then pass it on and set-up to load
  14643.  ' first font:
  14644.  IF SpecErr THEN
  14645.          flGetNextSpec = 2
  14646.          Spec.RegNum = 1
  14647.  ELSE
  14648.          flGetNextSpec = 0
  14649.  END IF
  14650.  
  14651.  END FUNCTION
  14652.  
  14653.  '=== flGetNum - Parses number from string
  14654.  '
  14655.  '  Arguments:
  14656.  '     Txt$     -  String from which to parse number
  14657.  '
  14658.  '     ChPos%   -  Character position on which to start
  14659.  '
  14660.  '     Default  -  Default value if number not found
  14661.  '
  14662.  '     ErrV     -  Returns error as cTrue or cFalse
  14663.  '
  14664.  '  Return Values:
  14665.  '     Returns value found or default
  14666.  '
  14667.  '  Notes:
  14668.  '     Simple state machine:
  14669.  '        state 0: Looking for first char
  14670.  '        state 1: Found start (+, -, or digit)
  14671.  '        state 2: Done
  14672.  '        state 3: Error
  14673.  '
  14674.  '=================================================================
  14675.  FUNCTION flGetNum (Txt$, ChPos%, Default, ErrV) STATIC
  14676.  
  14677.  ' Start in state 0
  14678.  State = 0
  14679.  
  14680.  ' Loop until done
  14681.  DO
  14682.          Char$ = MID$(Txt$, ChPos%, 1)
  14683.          SELECT CASE Char$
  14684.  
  14685.                  ' Plus and minus are only OK at the beginning:
  14686.                  CASE "+", "-":
  14687.                          SELECT CASE State
  14688.                                  CASE 0: Start% = ChPos%: State = 1
  14689.                                  CASE ELSE: State = 3
  14690.                          END SELECT
  14691.  
  14692.                  ' Digits are OK at the beginning of after plus and minus:
  14693.                  CASE "0" TO "9":
  14694.                          SELECT CASE State
  14695.                                  CASE 0: Start% = ChPos%: State = 1
  14696.                                  CASE ELSE:
  14697.                          END SELECT
  14698.  
  14699.                  ' Spaces are skipped:
  14700.                  CASE " ":
  14701.  
  14702.                  ' Anything else is an error at the beginning or marks the end
  14703.                  CASE ELSE:
  14704.                          SELECT CASE State
  14705.                                  CASE 0: State = 3
  14706.                                  CASE 1: State = 2
  14707.                          END SELECT
  14708.          END SELECT
  14709.  
  14710.          ' Go to next character:
  14711.          ChPos% = ChPos% + 1
  14712.  LOOP UNTIL State = 2 OR State = 3
  14713.  
  14714.  ' Scanning is complete; adjust ChPos% to mark last character processed:
  14715.  ChPos% = ChPos% - 1
  14716.  
  14717.  ' If error then set default number:
  14718.  IF State = 3 THEN
  14719.          flGetNum = Default
  14720.          ErrV = cTRUE
  14721.  
  14722.  ' Otherwise, extract number and get its value:
  14723.  ELSE
  14724.          EndPos% = ChPos% - 1
  14725.          flGetNum = VAL(MID$(Txt$, Start%, EndPos%))
  14726.          ErrV = cFALSE
  14727.  END IF
  14728.  END FUNCTION
  14729.  
  14730.  '=== flInitSpec - Initializes font specification structure
  14731.  '
  14732.  '  Arguments:
  14733.  '     Spec     -  FontSpec variable to initialize
  14734.  '
  14735.  '=================================================================
  14736.  SUB flInitSpec (Spec AS FontSpec) STATIC
  14737.  
  14738.          Spec.FaceName = ""
  14739.          Spec.Pitch = ""
  14740.          Spec.PointSize = 0
  14741.          Spec.ScrnMode = -1
  14742.          Spec.Height = 0
  14743.          Spec.Best = cFALSE
  14744.          Spec.RegNum = 0
  14745.          Spec.InMemory = cFALSE
  14746.  
  14747.  END SUB
  14748.  
  14749.  '=== flMatchFont - Finds first registered font that matches FontSpec
  14750.  '
  14751.  '  Arguments:
  14752.  '     FSpec -  FontSpec variable containing specification to match
  14753.  '
  14754.  '  Return Values:
  14755.  '     Number of registered font matched, -1 if no match.
  14756.  '
  14757.  '=================================================================
  14758.  FUNCTION flMatchFont (FSpec AS FontSpec) STATIC
  14759.  SHARED FGP AS GlobalParams
  14760.  SHARED FontHdrReg() AS IFontInfo
  14761.  
  14762.  ' Match a specific registered font:
  14763.  IF FSpec.RegNum > 0 AND FSpec.RegNum <= FGP.TotalRegistered THEN
  14764.          flMatchFont = FSpec.RegNum
  14765.          EXIT FUNCTION
  14766.  END IF
  14767.  
  14768.  ' If this is an invalid spec. then no fonts matched:
  14769.  IF FontErr <> 0 THEN
  14770.          flMatchFont = -1
  14771.          EXIT FUNCTION
  14772.  END IF
  14773.  
  14774.  ' Scan font for first one that matches the rest of the specs:
  14775.  SelectedFont% = -1
  14776.  BestSizeDiff = 3.402823E+38
  14777.  BestFontNum% = -1
  14778.  FOR FontNum% = 1 TO FGP.TotalRegistered
  14779.  
  14780.          ' Match a font from memory:
  14781.          MemOK% = cTRUE
  14782.          IF FSpec.InMemory AND FontHdrReg(FontNum%).FontSource <> cMemFont THE
  14783.                  MemOK% = cFALSE
  14784.          END IF
  14785.  
  14786.          ' Match name:
  14787.          IF FSpec.FaceName = FontHdrReg(FontNum%).FaceName OR LTRIM$(FSpec.Fac
  14788.                  NameOK% = cTRUE
  14789.          ELSE
  14790.                  NameOK% = cFALSE
  14791.          END IF
  14792.  
  14793.          ' Match pitch (fixed or proportional):
  14794.          Pitch$ = "F"
  14795.          IF FontHdrReg(FontNum%).FontHeader.dfPixWidth = 0 THEN Pitch$ = "P"
  14796.          IF FSpec.Pitch = Pitch$ OR FSpec.Pitch = " " THEN
  14797.                  PitchOK% = cTRUE
  14798.          ELSE
  14799.                  PitchOK% = cFALSE
  14800.          END IF
  14801.  
  14802.          ' Match font size (if neither point or pixel size specified then
  14803.          ' this font is OK):
  14804.          IF FSpec.PointSize = 0 AND FSpec.Height = 0 THEN
  14805.                  SizeOK% = cTRUE
  14806.  
  14807.          ' Otherwise, if point size specified (note that point size overrides
  14808.          ' the pixel height if they were both specified)...
  14809.          ELSEIF FSpec.PointSize <> 0 THEN
  14810.  
  14811.                  ' Make sure the font resolution matches the screen resolution
  14812.                  ' (pass over this font if not):
  14813.                  IF FSpec.HorizRes <> FontHdrReg(FontNum%).FontHeader.dfHorizR
  14814.                          SizeOK% = cFALSE
  14815.                  ELSEIF FSpec.VertRes <> FontHdrReg(FontNum%).FontHeader.dfVer
  14816.                          SizeOK% = cFALSE
  14817.  
  14818.                  ' Font has made it past the resolution check, now try to matc
  14819.                  ELSE
  14820.                          SizeDiff = ABS(FSpec.PointSize - FontHdrReg(FontNum%)
  14821.                          IF SizeDiff = 0 THEN
  14822.                                  SizeOK% = cTRUE
  14823.                          ELSE
  14824.                                  SizeOK% = cFALSE
  14825.                          END IF
  14826.                  END IF
  14827.  
  14828.  
  14829.          ' Now, the case where height was specified and not point size:
  14830.          ELSEIF FSpec.Height <> 0 THEN
  14831.                  SizeDiff = ABS(FSpec.Height - FontHdrReg(FontNum%).FontHeader
  14832.                  IF SizeDiff = 0 THEN
  14833.                          SizeOK% = cTRUE
  14834.                  ELSE
  14835.                          SizeOK% = cFALSE
  14836.                  END IF
  14837.          END IF
  14838.  
  14839.          ' Do record keeping if best-fit was specified:
  14840.          IF NOT SizeOK% AND PitchOK% AND FSpec.Best AND SizeDiff < BestSizeDif
  14841.                  BestSizeDiff = SizeDiff
  14842.                  BestFontNum% = FontNum%
  14843.          END IF
  14844.  
  14845.          ' See if this font is OK:
  14846.          IF MemOK% AND NameOK% AND PitchOK% AND SizeOK% THEN
  14847.                  SelectedFont% = FontNum%
  14848.                  EXIT FOR
  14849.          END IF
  14850.  NEXT FontNum%
  14851.  
  14852.  ' If no font was matched and best-fit was specified then select the
  14853.  ' best font:
  14854.  IF SelectedFont% < 0 AND FSpec.Best THEN SelectedFont% = BestFontNum%
  14855.  
  14856.  ' Return the font matched:
  14857.  flMatchFont = SelectedFont%
  14858.  
  14859.  END FUNCTION
  14860.  
  14861.  '=== flReadFont - Reads font data and sets up font header
  14862.  '
  14863.  '  Arguments:
  14864.  '     I%    -  Slot in loaded fonts to process
  14865.  '
  14866.  '=================================================================
  14867.  SUB flReadFont (I%) STATIC
  14868.  SHARED FGP AS GlobalParams
  14869.  SHARED FontHdrLoaded() AS IFontInfo
  14870.  SHARED FontData() AS FontDataBlock
  14871.  
  14872.  ON ERROR GOTO UnexpectedErr
  14873.  
  14874.  ' If memory font then it's already in memory:
  14875.  IF FontHdrLoaded(I%).FontSource = cMemFont THEN
  14876.          EXIT SUB
  14877.  
  14878.  ' For a font from a file, read it in:
  14879.  ELSE
  14880.          DataSize# = FontHdrLoaded(I%).FontHeader.dfSize - cSizeFontHeader
  14881.          NumBlocks% = -INT(-DataSize# / cFontBlockSize)
  14882.          FontHdrLoaded(I%).DataSeg = FGP.NextDataBlock
  14883.  
  14884.          ' Get next available file number and open file:
  14885.          FileNum% = FREEFILE
  14886.          OPEN FontHdrLoaded(I%).FileName FOR BINARY AS FileNum%
  14887.  
  14888.          ' Read blocks from the font file:
  14889.          DataLoc# = FontHdrLoaded(I%).FileLoc + cSizeFontHeader
  14890.          SEEK FileNum%, DataLoc#
  14891.          FOR BlockNum% = 0 TO NumBlocks% - 1
  14892.                  GET FileNum%, , FontData(FGP.NextDataBlock + BlockNum%)
  14893.          NEXT BlockNum%
  14894.  
  14895.          ' Close the file:
  14896.          CLOSE FileNum%
  14897.  
  14898.          ' Update the next data block pointer:
  14899.          FGP.NextDataBlock = FGP.NextDataBlock + NumBlocks%
  14900.  END IF
  14901.  
  14902.  END SUB
  14903.  
  14904.  '=== flRegisterFont - Actually registers a font resource:
  14905.  '
  14906.  '  Arguments:
  14907.  '     FileName$   - Name of font file (passed to routine that actually
  14908.  '                   registers resource entry)
  14909.  '
  14910.  '     FileNum%    - File number for reading
  14911.  '
  14912.  '=================================================================
  14913.  SUB flRegisterFont (FileName$, FileNum%) STATIC
  14914.  SHARED FGP AS GlobalParams
  14915.  SHARED FontHdrReg() AS IFontInfo
  14916.  
  14917.  DIM Byte AS STRING * 1, FontHeader AS WFHeader
  14918.  
  14919.  ' Read the font header:
  14920.  FontLoc# = SEEK(FileNum%)
  14921.  GET FileNum%, , FontHeader
  14922.  
  14923.  ' Only register vector fonts:
  14924.  IF FontHeader.dfType AND &H1 <> cBitMapType THEN EXIT SUB
  14925.  
  14926.  ' See that we're still within MaxRegistered limits:
  14927.  IF FGP.TotalRegistered >= FGP.MaxRegistered THEN
  14928.          flSetFontErr cTooManyFonts
  14929.          EXIT SUB
  14930.  END IF
  14931.  
  14932.  ' Go to next "registered" font slot:
  14933.  FGP.TotalRegistered = FGP.TotalRegistered + 1
  14934.  
  14935.  ' Set font source and save the header and file location:
  14936.  FontHdrReg(FGP.TotalRegistered).FontSource = cFileFont
  14937.  FontHdrReg(FGP.TotalRegistered).FontHeader = FontHeader
  14938.  FontHdrReg(FGP.TotalRegistered).FileLoc = FontLoc#
  14939.  
  14940.  ' Get the face name (scan characters until zero byte):
  14941.  SEEK FileNum%, FontLoc# + FontHeader.dfFace
  14942.  FaceName$ = ""
  14943.  FOR Char% = 0 TO cMaxFaceName - 1
  14944.          GET FileNum%, , Byte
  14945.          IF ASC(Byte) = 0 THEN EXIT FOR
  14946.          FaceName$ = FaceName$ + Byte
  14947.  NEXT Char%
  14948.  FontHdrReg(FGP.TotalRegistered).FaceName = FaceName$
  14949.  
  14950.  ' Finally, save the file name:
  14951.  FontHdrReg(FGP.TotalRegistered).FileName = FileName$
  14952.  
  14953.  END SUB
  14954.  
  14955.  '=== flSetFontErr - Sets the FontErr variable to an error value:
  14956.  '
  14957.  '  Arguments:
  14958.  '     ErrNum   -  The error number to set FontErr variable to
  14959.  '
  14960.  '=================================================================
  14961.  SUB flSetFontErr (ErrNum AS INTEGER) STATIC
  14962.  
  14963.           FontErr = ErrNum
  14964.  
  14965.  END SUB
  14966.  
  14967.  '=== flSizeFontBuffer - Calculate the FontBuffer size required for all fonts
  14968.  '
  14969.  '  Arguments:
  14970.  '     NFonts%  -  Number of font to be loaded
  14971.  '
  14972.  '  Notes:
  14973.  '     The use of -INT(-N) in the following code rounds N to the next
  14974.  '     larger integer
  14975.  '
  14976.  '=================================================================
  14977.  SUB flSizeFontBuffer (NFonts%) STATIC
  14978.  SHARED FGP AS GlobalParams
  14979.  SHARED FontHdrReg() AS IFontInfo
  14980.  SHARED FontLoadList() AS INTEGER
  14981.  SHARED FontData() AS FontDataBlock
  14982.  
  14983.  
  14984.  ON ERROR GOTO UnexpectedErr
  14985.  IF NFonts% = 0 THEN EXIT SUB
  14986.  
  14987.  ' Clear font status variables so we know what has been processed:
  14988.  flClearFontStatus
  14989.  
  14990.  ' Add sizes of all unique fonts together to get total size (each font
  14991.  ' begins on a new font block so the size of each font is calculated in
  14992.  ' terms of the number of font blocks it will take up):
  14993.  Size = 0
  14994.  FOR I% = 1 TO NFonts%
  14995.          FontNum% = FontLoadList(I%)
  14996.          IF FontHdrReg(FontNum%).Status = 0 THEN
  14997.                  FontSize = FontHdrReg(FontNum%).FontHeader.dfSize - cSizeFont
  14998.                  Size = Size - INT(-FontSize / cFontBlockSize)
  14999.                  FontHdrReg(FontNum%).Status = 1
  15000.          END IF
  15001.  NEXT I%
  15002.  
  15003.  ' Dimension the FontData array to hold everything:
  15004.  ON ERROR GOTO MemErr
  15005.  REDIM FontData(1 TO Size) AS FontDataBlock
  15006.  ON ERROR GOTO UnexpectedErr
  15007.  
  15008.  ' Set the next font block to the start for when flReadFont begins
  15009.  ' putting data in the font buffer:
  15010.  FGP.NextDataBlock = 1
  15011.  
  15012.  END SUB
  15013.  
  15014.  '=== GetFontInfo - Returns useful information about current font
  15015.  '
  15016.  '  Arguments:
  15017.  '     FI    -  FontInfo type variable to receive info
  15018.  '
  15019.  '=================================================================
  15020.  SUB GetFontInfo (FI AS FontInfo) STATIC
  15021.  SHARED FGP AS GlobalParams
  15022.  SHARED FontHdrLoaded() AS IFontInfo
  15023.  
  15024.  ON ERROR GOTO UnexpectedErr
  15025.  
  15026.  ' Clear outstanding font errors:
  15027.  flClearFontErr
  15028.  
  15029.  ' Check that some fonts are loaded:
  15030.  IF FGP.TotalLoaded <= 0 THEN
  15031.          flSetFontErr cNoFonts
  15032.          EXIT SUB
  15033.  END IF
  15034.  
  15035.  ' All OK, assign values from internal font header:
  15036.  FI.FontNum = FGP.CurrentFont
  15037.  FI.Ascent = FontHdrLoaded(FGP.CurrentFont).FontHeader.dfAscent
  15038.  FI.Points = FontHdrLoaded(FGP.CurrentFont).FontHeader.dfPoints
  15039.  FI.PixWidth = FontHdrLoaded(FGP.CurrentFont).FontHeader.dfPixWidth
  15040.  FI.PixHeight = FontHdrLoaded(FGP.CurrentFont).FontHeader.dfPixHeight
  15041.  FI.Leading = FontHdrLoaded(FGP.CurrentFont).FontHeader.dfInternalLeading
  15042.  FI.MaxWidth = FontHdrLoaded(FGP.CurrentFont).FontHeader.dfMaxWidth
  15043.  FI.AvgWidth = FontHdrLoaded(FGP.CurrentFont).FontHeader.dfAvgWidth
  15044.  FI.FileName = FontHdrLoaded(FGP.CurrentFont).FileName
  15045.  FI.FaceName = FontHdrLoaded(FGP.CurrentFont).FaceName
  15046.  
  15047.  END SUB
  15048.  
  15049.  '=== GetGTextLen - Returns bit length of string
  15050.  '
  15051.  '  Arguments:
  15052.  '     Text$ -  String for which to return length
  15053.  '
  15054.  '  Return Values:
  15055.  '     -1    -  Error (No fonts loaded, probably)
  15056.  '
  15057.  '     >=0   -  Length of string
  15058.  '
  15059.  '=================================================================
  15060.  FUNCTION GetGTextLen% (Text$) STATIC
  15061.  SHARED FGP AS GlobalParams
  15062.  SHARED FontHdrLoaded() AS IFontInfo
  15063.  SHARED FontData() AS FontDataBlock
  15064.  
  15065.  ON ERROR GOTO UnexpectedErr
  15066.  
  15067.  ' Clear outstanding font errors:
  15068.  flClearFontErr
  15069.  
  15070.  ' Make sure some fonts are loaded:
  15071.  IF FGP.TotalLoaded <= 0 THEN
  15072.          flSetFontErr cNoFonts
  15073.          GetGTextLen = -1
  15074.          EXIT FUNCTION
  15075.  END IF
  15076.  
  15077.  ' Assume this is a memory font (may override this later):
  15078.  CharTblPtr% = FontHdrLoaded(FGP.CurrentFont).DataOffset
  15079.  CharTblSeg% = FontHdrLoaded(FGP.CurrentFont).DataSeg
  15080.  
  15081.  ' Index into font data array:
  15082.  CharTable% = FontHdrLoaded(FGP.CurrentFont).DataSeg
  15083.  
  15084.  ' Add together the character lengths from the character table:
  15085.  TextLen% = 0
  15086.  FOR I% = 1 TO LEN(Text$)
  15087.  
  15088.          ' Get character code and translate to Ansi if IBM char set is specifi
  15089.          ChVal% = ASC(MID$(Text$, I%, 1))
  15090.          IF FGP.CharSet = cIBMChars THEN ChVal% = flANSI(ChVal%)
  15091.  
  15092.          ' Convert to default char if out of range:
  15093.          IF ChVal% < FGP.FChar OR ChVal% > FGP.LChar THEN ChVal% = FGP.DChar%
  15094.  
  15095.          ' Offset into character table for length word:
  15096.          CharOffset% = (ChVal% - FGP.FChar) * 4
  15097.  
  15098.          ' Peek the data and add it to the text length:
  15099.          IF FontHdrLoaded(FGP.CurrentFont).FontSource = cFileFont THEN
  15100.                  CharTblPtr% = VARPTR(FontData(CharTable%))
  15101.                  CharTblSeg% = VARSEG(FontData(CharTable%))
  15102.          END IF
  15103.          DEF SEG = CharTblSeg%
  15104.          CharLen% = PEEK(CharTblPtr% + CharOffset%) + PEEK(CharTblPtr% + CharO
  15105.          TextLen% = TextLen% + CharLen%
  15106.  NEXT I%
  15107.  
  15108.  GetGTextLen = TextLen%
  15109.  
  15110.  END FUNCTION
  15111.  
  15112.  '=== GetMaxFonts - Gets the maximum number of fonts that can be registered
  15113.  '                  and loaded by the font library:
  15114.  '
  15115.  '  Arguments:
  15116.  '     Registered  -  The maximum number of fonts that can be registered
  15117.  '                    by the font library
  15118.  '
  15119.  '     Loaded      -  The maximum number of fonts that can be loaded by
  15120.  '                    by the font library
  15121.  '
  15122.  '=================================================================
  15123.  SUB GetMaxFonts (Registered AS INTEGER, Loaded AS INTEGER)
  15124.  SHARED FGP AS GlobalParams
  15125.  
  15126.  ON ERROR GOTO UnexpectedErr
  15127.  
  15128.  ' Clear outstanding font errors:
  15129.  flClearFontErr
  15130.  
  15131.  ' If SetMaxFonts hasn't been called then make sure the default is
  15132.  ' correct:
  15133.  flChkMax
  15134.  
  15135.  ' Simply return the values of the internal variables for maximum
  15136.  ' fonts registered and loaded:
  15137.  Registered = FGP.MaxRegistered
  15138.  Loaded = FGP.MaxLoaded
  15139.  
  15140.  END SUB
  15141.  
  15142.  '=== GetFontInfo - Returns useful information about current font
  15143.  '
  15144.  '  Arguments:
  15145.  '     Font  -  Font number (in list of registered fonts) on which to get
  15146.  '              information
  15147.  '
  15148.  '     FI    -  FontInfo type variable to receive info
  15149.  '
  15150.  '=================================================================
  15151.  SUB GetRFontInfo (Font AS INTEGER, FI AS FontInfo) STATIC
  15152.  SHARED FontHdrReg() AS IFontInfo
  15153.  
  15154.  ON ERROR GOTO UnexpectedErr
  15155.  
  15156.  ' Clear outstanding font errors:
  15157.  flClearFontErr
  15158.  
  15159.  ' See that they've specified a valid font:
  15160.  IF Font < 0 OR Font > FGP.TotalRegistered THEN
  15161.          flSetFontErr cBadFontNumber
  15162.          EXIT SUB
  15163.  END IF
  15164.  
  15165.  ' All OK, assign values from internal font header:
  15166.  FI.FontNum = Font
  15167.  FI.Ascent = FontHdrReg(Font).FontHeader.dfAscent
  15168.  FI.Points = FontHdrReg(Font).FontHeader.dfPoints
  15169.  FI.PixWidth = FontHdrReg(Font).FontHeader.dfPixWidth
  15170.  FI.PixHeight = FontHdrReg(Font).FontHeader.dfPixHeight
  15171.  FI.Leading = FontHdrReg(Font).FontHeader.dfInternalLeading
  15172.  FI.MaxWidth = FontHdrReg(Font).FontHeader.dfMaxWidth
  15173.  FI.AvgWidth = FontHdrReg(Font).FontHeader.dfAvgWidth
  15174.  FI.FileName = FontHdrReg(Font).FileName
  15175.  FI.FaceName = FontHdrReg(Font).FaceName
  15176.  
  15177.  END SUB
  15178.  
  15179.  '=== GetTotalFonts - Gets the total number of fonts that currently registered
  15180.  '                    and loaded by the font library:
  15181.  '
  15182.  '  Arguments:
  15183.  '     Registered  -  The total number of fonts registered by the font
  15184.  '                    library
  15185.  '
  15186.  '     Loaded      -  The total number of fonts loaded by the font library
  15187.  '
  15188.  '=================================================================
  15189.  SUB GetTotalFonts (Registered AS INTEGER, Loaded AS INTEGER)
  15190.  SHARED FGP AS GlobalParams
  15191.  
  15192.  ON ERROR GOTO UnexpectedErr
  15193.  
  15194.  ' Clear outstanding font errors:
  15195.  flClearFontErr
  15196.  
  15197.  ' Simply return the values of internal variables:
  15198.  Registered = FGP.TotalRegistered
  15199.  Loaded = FGP.TotalLoaded
  15200.  
  15201.  END SUB
  15202.  
  15203.  '=== GTextWindow - Communicates the current WINDOW to fontlib
  15204.  '
  15205.  '  Arguments:
  15206.  '     X1    -  Minimum X value
  15207.  '
  15208.  '     Y1    -  Minimum Y value
  15209.  '
  15210.  '     X2    -  Maximum X value
  15211.  '
  15212.  '     Y2    -  Maximum Y value
  15213.  '
  15214.  '     Scrn% -  cTRUE means that window Y values increase top to bottom
  15215.  '
  15216.  '  Remarks:
  15217.  '     Calling this with X1=X2 or Y1=Y2 will clear the current
  15218.  '     window.
  15219.  '
  15220.  '=================================================================
  15221.  SUB GTextWindow (X1 AS SINGLE, Y1 AS SINGLE, X2 AS SINGLE, Y2 AS SINGLE, Scrn
  15222.  SHARED FGP AS GlobalParams
  15223.  
  15224.  ON ERROR GOTO UnexpectedErr
  15225.  
  15226.  ' Clear outstanding font errors:
  15227.  flClearFontErr
  15228.  
  15229.  ' Save the window values in global variable:
  15230.  FGP.WX1 = X1
  15231.  FGP.WY1 = Y1
  15232.  FGP.WX2 = X2
  15233.  FGP.WY2 = Y2
  15234.  FGP.WScrn = Scrn%
  15235.  
  15236.  ' If window is valid then flag it as set:
  15237.  FGP.WindowSet = ((X2 - X1) <> 0) AND ((Y2 - Y1) <> 0)
  15238.  
  15239.  END SUB
  15240.  
  15241.  '=== LoadFont - Loads one or more fonts according to specification string
  15242.  '
  15243.  '  Arguments:
  15244.  '     SpecTxt$ -  String containing parameters specifying one or more
  15245.  '                 fonts to load (see notes below)
  15246.  '
  15247.  '  Return Values:
  15248.  '     The number of fonts loaded
  15249.  '
  15250.  '  Notes:
  15251.  '     A spec. can contain the following parameters in any order.
  15252.  '     Parameters are each one character immediately followed by a value
  15253.  '     if called for. Multiple specifications may be entered separated
  15254.  '     by slash (/) characters. Loadfont will search for the FIRST font in
  15255.  '     the list of registered fonts that matches each spec. and load it. If
  15256.  '     no font matches a specification registered font number one will be
  15257.  '     used. If a given font is selected by more than one spec in the list
  15258.  '     it will only be loaded once. When this routine is called all
  15259.  '     previous fonts will be discarded:
  15260.  '
  15261.  '        T  -  followed by a blank-terminated name loads font by
  15262.  '              specified name
  15263.  '
  15264.  '        F  -  No value. Selects only fixed pitch fonts
  15265.  '
  15266.  '        P  -  No value. Selects only proportional fonts
  15267.  '
  15268.  '        S  -  Followed by number specifies desired point size
  15269.  '
  15270.  '        M  -  Followed by number specifies the screen mode font will be
  15271.  '              used on. This is used in conjunction with the "S" parameter
  15272.  '              above to select appropriately sized font.
  15273.  '
  15274.  '        H  -  Followed by number specifies the pixel height of
  15275.  '              font to select. "S" overrides this.
  15276.  '
  15277.  '        N  -  Followed by number selects specific font number
  15278.  '              from the list of currently registered fonts.
  15279.  '
  15280.  '        R  -  Selects font stored in RAM memory
  15281.  '
  15282.  '=================================================================
  15283.  FUNCTION LoadFont% (SpecTxt$) STATIC
  15284.  SHARED FGP AS GlobalParams
  15285.  DIM FSpec AS FontSpec
  15286.  
  15287.  ON ERROR GOTO UnexpectedErr
  15288.  
  15289.  ' Clear outstanding errors and check for valid max limits:
  15290.  flClearFontErr
  15291.  
  15292.  flChkMax
  15293.  
  15294.  ' Make sure there's room to load a font:
  15295.  IF FGP.TotalLoaded >= FGP.MaxLoaded THEN
  15296.          flSetFontErr cTooManyFonts
  15297.          EXIT FUNCTION
  15298.  END IF
  15299.  
  15300.  ' Make sure there are some registered fonts to look through:
  15301.  IF FGP.TotalRegistered <= 0 THEN
  15302.          flSetFontErr cNoFonts
  15303.          EXIT FUNCTION
  15304.  END IF
  15305.  
  15306.  ' Process each spec in the spec string:
  15307.  Slot% = 1
  15308.  ChPos% = 1
  15309.  DO UNTIL Slot% > FGP.MaxLoaded
  15310.  
  15311.          ' Initialize the spec structure:
  15312.          flInitSpec FSpec
  15313.  
  15314.          ' Get next spec from string (Found will be false if no spec found):
  15315.          SpecStatus% = flGetNextSpec(SpecTxt$, ChPos%, FSpec)
  15316.          SELECT CASE SpecStatus%
  15317.                  CASE 0:
  15318.                  CASE 1: EXIT DO
  15319.                  CASE 2: flSetFontErr cBadFontSpec
  15320.          END SELECT
  15321.  
  15322.          ' Try to match font. Set font to one if none match:
  15323.          FontNum% = flMatchFont(FSpec)
  15324.          IF FontNum% < 1 THEN
  15325.                  flSetFontErr cFontNotFound
  15326.                  FontNum% = 1
  15327.          END IF
  15328.  
  15329.          ' Record font in font load list:
  15330.          FontLoadList(Slot%) = FontNum%
  15331.          Slot% = Slot% + 1
  15332.  LOOP
  15333.  
  15334.  ' Now actually get the fonts in the load list:
  15335.  FGP.TotalLoaded = flGetFonts(Slot% - 1)
  15336.  FGP.CurrentFont = 1
  15337.  
  15338.  ' Select the first font by default (pass outstanding font errors around
  15339.  ' it):
  15340.  HoldErr% = FontErr
  15341.  SelectFont cDefaultFont
  15342.  IF HoldErr% <> 0 THEN flSetFontErr HoldErr%
  15343.  
  15344.  LoadFont = FGP.TotalLoaded
  15345.  
  15346.  END FUNCTION
  15347.  
  15348.  '=== OutGText - Outputs graphics text to the screen
  15349.  '
  15350.  '  Arguments:
  15351.  '     X        -  X location of upper left of char box
  15352.  '
  15353.  '     Y        -  Y location of upper left of char box
  15354.  '
  15355.  '     Text$    -  Text string to output
  15356.  '
  15357.  '  Return Values:
  15358.  '     Length of text output, Values of X and Y are updated
  15359.  '
  15360.  '=================================================================
  15361.  FUNCTION OutGText% (X AS SINGLE, Y AS SINGLE, Text$) STATIC
  15362.  SHARED FGP AS GlobalParams
  15363.  SHARED FontHdrLoaded() AS IFontInfo
  15364.  
  15365.  ON ERROR GOTO UnexpectedErr
  15366.  
  15367.  ' Clear outstanding font errors:
  15368.  flClearFontErr
  15369.  
  15370.  ' Make sure fonts are loaded:
  15371.  IF FGP.TotalLoaded <= 0 THEN
  15372.          flSetFontErr cNoFonts
  15373.          EXIT FUNCTION
  15374.  END IF
  15375.  
  15376.  IF NOT FGP.CharColorInit THEN SetGTextColor cDefaultColor
  15377.  IF NOT FGP.CharDirInit THEN SetGTextDir cDefaultDir
  15378.  
  15379.  ' Make sure a graphic mode is set:
  15380.  flGetCurrentScrnSize XP%, YP%
  15381.  IF XP% = 0 THEN EXIT FUNCTION
  15382.  
  15383.  ' Save input location to working variables and erase any window setting:
  15384.  IX% = PMAP(X, 0)
  15385.  IY% = PMAP(Y, 1)
  15386.  WINDOW
  15387.  
  15388.  ' Map chars to valid ones and output them adding their lengths:
  15389.  TextLen% = 0
  15390.  FOR Char% = 1 TO LEN(Text$)
  15391.          ChVal% = ASC(MID$(Text$, Char%, 1))
  15392.          IF FGP.CharSet = cIBMChars THEN ChVal% = flANSI(ChVal%)
  15393.  
  15394.          IF FGP.FontSource = cFileFont THEN
  15395.                  BitMapPtr% = VARPTR(FontData(FGP.DSeg))
  15396.                  BitMapSeg% = VARSEG(FontData(FGP.DSeg))
  15397.          ELSE
  15398.                  BitMapPtr% = FGP.DOffset
  15399.                  BitMapSeg% = FGP.DSeg
  15400.          END IF
  15401.  
  15402.          CharLen% = flbltchar%(BitMapSeg%, BitMapPtr%, ChVal%, IX%, IY%)
  15403.  
  15404.          IX% = IX% + FGP.XPixInc * CharLen%
  15405.          IY% = IY% + FGP.YPixInc * CharLen%
  15406.  
  15407.          TextLen% = TextLen% + CharLen%
  15408.  NEXT Char%
  15409.  
  15410.  ' Reset window:
  15411.  IF FGP.WindowSet THEN
  15412.          IF FGP.WScrn% THEN
  15413.                  WINDOW SCREEN (FGP.WX1, FGP.WY1)-(FGP.WX2, FGP.WY2)
  15414.          ELSE
  15415.                  WINDOW (FGP.WX1, FGP.WY1)-(FGP.WX2, FGP.WY2)
  15416.          END IF
  15417.  END IF
  15418.  
  15419.  ' Update locations
  15420.  X = PMAP(IX%, 2)
  15421.  Y = PMAP(IY%, 3)
  15422.  
  15423.  ' Return total character length:
  15424.  OutGText = TextLen%
  15425.  
  15426.  END FUNCTION
  15427.  
  15428.  '=== RegisterFonts - Loads header information from font resources:
  15429.  '
  15430.  '  Arguments:
  15431.  '     FileName$   -  Path name for font file to register
  15432.  '
  15433.  '  Return Value:
  15434.  '     The number of fonts actually registered
  15435.  '
  15436.  '  Notes:
  15437.  '     Offsets documented in Windows document assume the file's first
  15438.  '     byte is byte 0 (zero) and GET assumes the first byte is byte 1 so
  15439.  '     many GET locations are expressed in the following code as
  15440.  '     a documented offset + 1.
  15441.  '
  15442.  '=================================================================
  15443.  FUNCTION RegisterFonts% (FileName$) STATIC
  15444.  SHARED FGP AS GlobalParams
  15445.  DIM Byte AS STRING * 1
  15446.  
  15447.  ON ERROR GOTO UnexpectedErr
  15448.  
  15449.  ' Clear errors and make sure things are initialized:
  15450.  flClearFontErr
  15451.  
  15452.  flChkMax
  15453.  
  15454.  ' Get next available file number:
  15455.  FileNum% = FREEFILE
  15456.  
  15457.  ' Try to open the file for input first to make sure the file exists. This
  15458.  ' is done to avoid creating a zero length file if the file doesn't exist.
  15459.  ON ERROR GOTO NoFileErr
  15460.  OPEN FileName$ FOR INPUT AS FileNum%
  15461.  ON ERROR GOTO UnexpectedErr
  15462.  IF FontErr <> 0 THEN
  15463.          RegisterFonts = 0
  15464.          EXIT FUNCTION
  15465.  END IF
  15466.  CLOSE FileNum%
  15467.  
  15468.  ' File seems to exist, so open it in binary mode:
  15469.  OPEN FileName$ FOR BINARY ACCESS READ AS FileNum%
  15470.  
  15471.  ' Get the byte that indicates whether this file has a new-style
  15472.  ' header on it. If not, then error:
  15473.  GET FileNum%, &H18 + 1, Byte
  15474.  IF ASC(Byte) <> &H40 THEN
  15475.          flSetFontErr cBadFontFile
  15476.          CLOSE FileNum%
  15477.          EXIT FUNCTION
  15478.  END IF
  15479.  
  15480.  ' Save the number of fonts currently registered for use later in
  15481.  ' calculating the number of fonts registered by this call:
  15482.  OldTotal = FGP.TotalRegistered
  15483.  
  15484.  ' Get the pointer to the new-style header:
  15485.  GET FileNum%, &H3C + 1, Word%
  15486.  NewHdr% = Word%
  15487.  
  15488.  ' Get pointer to resource table:
  15489.  GET FileNum%, Word% + &H22 + 1, Word%
  15490.  ResourceEntry# = NewHdr% + Word% + 1
  15491.  
  15492.  ' Get the alignment shift count from beginning of table:
  15493.  GET FileNum%, ResourceEntry#, Align%
  15494.  
  15495.  ' Loop, registering font resources until they have run out:
  15496.  DO
  15497.          ResType% = flDoNextResource(Align%, FileName$, FileNum%)
  15498.          IF FontErr <> 0 THEN EXIT DO
  15499.  LOOP UNTIL ResType% = 0
  15500.  
  15501.  CLOSE FileNum%
  15502.  
  15503.  ' Finally, return number of fonts actually registered:
  15504.  RegisterFonts = FGP.TotalRegistered - OldTotal
  15505.  
  15506.  END FUNCTION
  15507.  
  15508.  '=== RegisterMemFont - Loads header information from a memory-resident font
  15509.  '
  15510.  '  Arguments:
  15511.  '     FontSeg%    -  Segment address of font to register
  15512.  '
  15513.  '     FontOffset% -  Offset address of font to register
  15514.  '
  15515.  '  Return Value:
  15516.  '     The number of fonts actually registered (0 or 1)
  15517.  '
  15518.  '  Notes:
  15519.  '     Memory resident fonts cannot be stored in BASIC relocatable data
  15520.  '     structures (like arrays or non-fixed strings).
  15521.  '
  15522.  '=================================================================
  15523.  FUNCTION RegisterMemFont% (FontSeg AS INTEGER, FontOffset AS INTEGER) STATIC
  15524.  SHARED FGP AS GlobalParams
  15525.  SHARED FontHdrReg() AS IFontInfo
  15526.  DIM FontHeader AS WFHeader
  15527.  
  15528.  ON ERROR GOTO UnexpectedErr
  15529.  
  15530.  ' Clear error and check max limits:
  15531.  flClearFontErr
  15532.  flChkMax
  15533.  
  15534.  ' Get the font header:
  15535.  flMovMem FontHeader, FontSeg, FontOffset, cSizeFontHeader
  15536.  
  15537.  ' Only register vector fonts:
  15538.  IF FontHeader.dfType AND &H1 <> cBitMapType THEN
  15539.          flSetFontErr cBadFontType
  15540.          RegisterMemFont = 0
  15541.          EXIT FUNCTION
  15542.  END IF
  15543.  
  15544.  ' See that we're still within MaxRegistered limits:
  15545.  IF FGP.TotalRegistered >= FGP.MaxRegistered THEN
  15546.          flSetFontErr cTooManyFonts
  15547.          RegisterMemFont = 0
  15548.          EXIT FUNCTION
  15549.  END IF
  15550.  
  15551.  ' Go to next "registered" font slot:
  15552.  FGP.TotalRegistered = FGP.TotalRegistered + 1
  15553.  
  15554.  ' Set font source and save the header:
  15555.  FontHdrReg(FGP.TotalRegistered).FontSource = cMemFont
  15556.  FontHdrReg(FGP.TotalRegistered).FontHeader = FontHeader
  15557.  
  15558.  ' Set font location in memory:
  15559.  FontHdrReg(FGP.TotalRegistered).DataSeg = FontSeg
  15560.  FontHdrReg(FGP.TotalRegistered).DataOffset = FontOffset + cSizeFontHeader
  15561.  
  15562.  ' Get the face name (scan characters until zero byte):
  15563.  FaceLoc% = FontOffset + FontHeader.dfFace
  15564.  FaceName$ = ""
  15565.  DEF SEG = FontSeg
  15566.  FOR Char% = 0 TO cMaxFaceName - 1
  15567.          Byte% = PEEK(FaceLoc% + Char%)
  15568.          IF Byte% = 0 THEN EXIT FOR
  15569.          FaceName$ = FaceName$ + CHR$(Byte%)
  15570.  NEXT Char%
  15571.  FontHdrReg(FGP.TotalRegistered).FaceName = FaceName$
  15572.  
  15573.  ' Finally, return number of fonts actually registered:
  15574.  RegisterMemFont = 1
  15575.  
  15576.  END FUNCTION
  15577.  
  15578.  '=== SelectFont - Selects current font from among loaded fonts
  15579.  '
  15580.  '  Arguments:
  15581.  '     FontNum% -  Font number to select
  15582.  '
  15583.  '=================================================================
  15584.  SUB SelectFont (FontNum AS INTEGER) STATIC
  15585.  SHARED FGP AS GlobalParams
  15586.  
  15587.  ON ERROR GOTO UnexpectedErr
  15588.  
  15589.  ' Clear outstanding font errors:
  15590.  flClearFontErr
  15591.  
  15592.  ' If no fonts are loaded then error:
  15593.  IF FGP.TotalLoaded <= 0 THEN
  15594.          flSetFontErr cNoFonts
  15595.          EXIT SUB
  15596.  END IF
  15597.  
  15598.  ' Now, map the font number to an acceptable one and select it:
  15599.  IF FontNum <= 0 THEN
  15600.          FGP.CurrentFont = 1
  15601.  ELSE
  15602.          FGP.CurrentFont = (ABS(FontNum - 1) MOD (FGP.TotalLoaded)) + 1
  15603.  END IF
  15604.  
  15605.  ' Get First, Last and Default character params from header:
  15606.  FGP.FChar = ASC(FontHdrLoaded(FGP.CurrentFont).FontHeader.dfFirstChar)
  15607.  FGP.LChar = ASC(FontHdrLoaded(FGP.CurrentFont).FontHeader.dfLastChar)
  15608.  FGP.DChar = ASC(FontHdrLoaded(FGP.CurrentFont).FontHeader.dfDefaultChar)
  15609.  FGP.CHeight = FontHdrLoaded(FGP.CurrentFont).FontHeader.dfPixHeight
  15610.  flSetBltParams cSizeFontHeader, FGP.CHeight, FGP.FChar, FGP.LChar, FGP.DChar
  15611.  
  15612.  ' Set some other commonly used elements of font info:
  15613.  FGP.DSeg = FontHdrLoaded(FGP.CurrentFont).DataSeg
  15614.  FGP.DOffset = FontHdrLoaded(FGP.CurrentFont).DataOffset
  15615.  FGP.FontSource = FontHdrLoaded(FGP.CurrentFont).FontSource
  15616.  
  15617.  END SUB
  15618.  
  15619.  '=== SetGCharset - Specifies IBM or Windows char set
  15620.  '
  15621.  '  Arguments:
  15622.  '     Charset%    -  cIBMChars for IBM character mappings
  15623.  '                    cWindowsChars for Windows character mappings
  15624.  '
  15625.  '=================================================================
  15626.  SUB SetGCharset (CharSet AS INTEGER) STATIC
  15627.  SHARED FGP AS GlobalParams
  15628.  
  15629.  ON ERROR GOTO UnexpectedErr
  15630.  
  15631.  ' Clear outstanding font errors:
  15632.  flClearFontErr
  15633.  
  15634.  IF CharSet = cWindowsChars THEN
  15635.          FGP.CharSet = cWindowsChars
  15636.  ELSE
  15637.          FGP.CharSet = cIBMChars
  15638.  END IF
  15639.  
  15640.  END SUB
  15641.  
  15642.  '=== SetGTextColor - Sets color for drawing characters
  15643.  '
  15644.  '  Arguments:
  15645.  '     FColor   -  Color number
  15646.  '
  15647.  '=================================================================
  15648.  SUB SetGTextColor (FColor AS INTEGER) STATIC
  15649.  SHARED FGP AS GlobalParams
  15650.  
  15651.  ON ERROR GOTO UnexpectedErr
  15652.  
  15653.  ' Clear outstanding font errors:
  15654.  flClearFontErr
  15655.  
  15656.  FGP.CharColor = ABS(FColor)
  15657.  flSetBltColor FGP.CharColor
  15658.  FGP.CharColorInit = cTRUE
  15659.  
  15660.  END SUB
  15661.  
  15662.  '=== SetGTextDir - Sets character direction for OutGText
  15663.  '
  15664.  '  Arguments:
  15665.  '     Dir   -  Character direction:
  15666.  '              0 = Horizontal-Right
  15667.  '              1 = Vertical-Up
  15668.  '              2 = Horizontal-Left
  15669.  '              3 = Vertical-Down
  15670.  '
  15671.  '=================================================================
  15672.  SUB SetGTextDir (Dir AS INTEGER) STATIC
  15673.  SHARED FGP AS GlobalParams
  15674.  
  15675.  ON ERROR GOTO UnexpectedErr
  15676.  
  15677.  ' Clear outstanding font errors:
  15678.  flClearFontErr
  15679.  
  15680.  SELECT CASE Dir
  15681.  
  15682.          ' Vertical - up
  15683.          CASE 1:  FGP.XPixInc% = 0
  15684.                                  FGP.YPixInc% = -1
  15685.                                  XRowInc% = 1
  15686.                                  YRowInc% = 0
  15687.                                  FGP.CharDir = 1
  15688.  
  15689.          ' Horizontal -left
  15690.          CASE 2:  FGP.XPixInc% = -1
  15691.                                  FGP.YPixInc% = 0
  15692.                                  XRowInc% = 0
  15693.                                  YRowInc% = -1
  15694.                                  FGP.CharDir = 2
  15695.  
  15696.          ' Vertical - down
  15697.          CASE 3:  FGP.XPixInc% = 0
  15698.                                  FGP.YPixInc% = 1
  15699.                                  XRowInc% = -1
  15700.                                  YRowInc% = 0
  15701.                                  FGP.CharDir = 3
  15702.  
  15703.          ' Horizontal - right
  15704.          CASE ELSE:  FGP.XPixInc% = 1
  15705.                                          FGP.YPixInc% = 0
  15706.                                          XRowInc% = 0
  15707.                                          YRowInc% = 1
  15708.                                          FGP.CharDir = 0
  15709.          END SELECT
  15710.  
  15711.          ' Call routine to set these increments in the char output routine
  15712.          flSetBltDir FGP.XPixInc%, FGP.YPixInc%, XRowInc%, YRowInc%
  15713.          FGP.CharDirInit = cTRUE
  15714.  
  15715.  END SUB
  15716.  
  15717.  '=== SetMaxFonts - Sets the maximum number of fonts that can be registered
  15718.  '                  and loaded by the font library:
  15719.  '
  15720.  '  Arguments:
  15721.  '     Registered  -  The maximum number of fonts that can be registered
  15722.  '                    by the font library
  15723.  '
  15724.  '     Loaded      -  The maximum number of fonts that can be loaded by
  15725.  '                    by the font library
  15726.  '
  15727.  '  Return Values:
  15728.  '     Sets error if values are not positive. Adjusts MaxReg and MaxLoad
  15729.  '     internal values and resets the length of FontHdrReg and FontHdrLoad
  15730.  '     arrays if the new value is different from previous one
  15731.  '
  15732.  '=================================================================
  15733.  SUB SetMaxFonts (Registered AS INTEGER, Loaded AS INTEGER) STATIC
  15734.  SHARED FGP AS GlobalParams
  15735.  SHARED FontHdrReg() AS IFontInfo
  15736.  SHARED FontHdrLoaded() AS IFontInfo
  15737.  SHARED FontLoadList() AS INTEGER
  15738.  SHARED FontData() AS FontDataBlock
  15739.  
  15740.  ON ERROR GOTO UnexpectedErr
  15741.  
  15742.  ' Clear errors:
  15743.  flClearFontErr
  15744.  
  15745.  ' Check to see that values are within range:
  15746.  IF Registered <= 0 OR Loaded <= 0 THEN
  15747.          flSetFontErr cBadFontLimit
  15748.          EXIT SUB
  15749.  END IF
  15750.  
  15751.  ' Values are ostensibly OK. Reset values and redimension arrays:
  15752.  ' Reset values for registered fonts:
  15753.  FGP.TotalRegistered = 0
  15754.  FGP.MaxRegistered = Registered
  15755.  
  15756.  ON ERROR GOTO MemErr
  15757.  REDIM FontHdrReg(1 TO FGP.MaxRegistered) AS IFontInfo
  15758.  ON ERROR GOTO UnexpectedErr
  15759.  
  15760.  ' Reset values for loaded fonts:
  15761.  FGP.TotalLoaded = 0
  15762.  FGP.MaxLoaded = Loaded
  15763.  
  15764.  ON ERROR GOTO MemErr
  15765.  REDIM FontLoadList(1 TO FGP.MaxLoaded) AS INTEGER
  15766.  REDIM FontHdrLoaded(1 TO FGP.MaxLoaded) AS IFontInfo
  15767.  ON ERROR GOTO UnexpectedErr
  15768.  
  15769.  ' Clear font data array:
  15770.  ERASE FontData
  15771.  
  15772.  END SUB
  15773.  
  15774.  '=== UnRegisterFonts - Erases registered font header array and resets
  15775.  '                      total registered fonts to 0:
  15776.  '
  15777.  '  Arguments:
  15778.  '     ErrNum   -  The error number to set FontErr variable to
  15779.  '
  15780.  '=================================================================
  15781.  SUB UnRegisterFonts STATIC
  15782.  SHARED FontHdrReg() AS IFontInfo, FGP AS GlobalParams
  15783.  
  15784.  ON ERROR GOTO UnexpectedErr
  15785.  
  15786.  ' Clear outstanding font errors:
  15787.  flClearFontErr
  15788.  
  15789.  REDIM FontHdrReg(1 TO 1)  AS IFontInfo
  15790.  FGP.MaxRegistered = UBOUND(FontHdrReg, 1)
  15791.  FGP.TotalRegistered = 0
  15792.  
  15793.  END SUB
  15794.  
  15795.  
  15796.  
  15797.  FONTDEMO.BAS
  15798.  CD-ROM Disc Path:   \SAMPCODE\BASIC\FONTDEMO.BAS
  15799.  
  15800.  '       FONTDEMO.BAS - FONTB demonstration program.
  15801.  '
  15802.  '  Copyright (C) 1989 Microsoft Corporation, All Rights Reserved
  15803.  '
  15804.  '  This program demonstrates some of the capabilities of the fonts
  15805.  '  toolbox.  It loads font files found in the current directory and
  15806.  '  and allows you to select a font for display.
  15807.  '
  15808.  '  The following font files are provided with BASIC 7.0:
  15809.  '     - Raster fonts designed for screen resolution of 640x200
  15810.  '           COURA.FON
  15811.  '           HELVA.FON
  15812.  '           TMSRA.FON
  15813.  '     - Raster fonts designed for screen resolution of 640x350
  15814.  '           COURB.FON
  15815.  '           HELVB.FON
  15816.  '           TMSRB.FON
  15817.  '     - Raster fonts designed for screen resolution of 640x480
  15818.  '           COURE.FON
  15819.  '           HELVE.FON
  15820.  '           TMSRE.FON
  15821.  '
  15822.  ' $INCLUDE: 'FONTB.BI'
  15823.  CONST TRUE = -1
  15824.  CONST FALSE = 0
  15825.  DECLARE SUB DriveScreen ()
  15826.  DECLARE SUB GetFiles ()
  15827.  DECLARE SUB GetModes ()
  15828.  DECLARE SUB ShowScreen ()
  15829.  
  15830.  DIM SHARED FI AS FontInfo
  15831.  DIM SHARED totalmodes AS INTEGER
  15832.  DIM SHARED modes(1 TO 13) AS INTEGER
  15833.  DIM SHARED fontfiles(1 TO 18) AS STRING
  15834.  DIM SHARED totalfonts AS INTEGER
  15835.  DIM SHARED currentfont AS INTEGER
  15836.  DIM SHARED currentmode AS INTEGER
  15837.  GetModes
  15838.  GetFiles
  15839.  currentfont = 1
  15840.  DO
  15841.      DriveScreen
  15842.      ShowScreen
  15843.  LOOP
  15844.  END
  15845.  
  15846.  '
  15847.  'DriveScreen displays the lists of available fonts and screen modes and
  15848.  'scrolls through them with arrow keys.
  15849.  '
  15850.  SUB DriveScreen STATIC
  15851.  IF init% = 0 THEN
  15852.      set$ = "f"
  15853.      max% = totalfonts
  15854.      posit% = currentfont
  15855.      modedim$ = "320x200640x200720x348640x400              320x200"
  15856.      modedim$ = modedim$ + "640x200640x350640x350640x480640x480320x200"
  15857.  
  15858.      'Check if monitor supports color or mono.
  15859.  
  15860.      SELECT CASE modes(1)
  15861.          CASE 13, 9, 8, 7
  15862.              mode$ = "color"
  15863.          CASE 3, 4, 10
  15864.              mode$ = "mono"
  15865.          CASE 2
  15866.              IF modes(2) = 1 THEN
  15867.                  mode$ = "color"
  15868.              ELSE
  15869.                  mode$ = "mono"
  15870.              END IF
  15871.      END SELECT
  15872.      FOR i% = 1 TO totalmodes
  15873.          IF modes(i%) = 4 THEN mode$ = "mono"
  15874.      NEXT i%
  15875.  
  15876.      'Set colors based on type of monitor.
  15877.  
  15878.      SELECT CASE mode$
  15879.          CASE "color"
  15880.              listfore% = 7
  15881.              listback% = 0
  15882.              titleon% = 15
  15883.              titleoff% = 7
  15884.              titleback% = 1
  15885.              back% = 1
  15886.              high% = 15
  15887.          CASE "mono"
  15888.              listfore% = 7
  15889.              listback% = 0
  15890.              titleon% = 0
  15891.              titleoff% = 2
  15892.              titleback% = 7
  15893.              back% = 0
  15894.              high% = 7
  15895.      END SELECT
  15896.      init% = 1
  15897.  END IF
  15898.  
  15899.  'Display the screen with the current selections.
  15900.  
  15901.  SCREEN 0
  15902.  WIDTH 80, 25
  15903.  LOCATE , , 0: COLOR 0, back%
  15904.  PRINT SPACE$(1920)
  15905.  LOCATE 2, 1: COLOR high%, back%
  15906.  PRINT "  Font Toolbox Demo"
  15907.  COLOR titleoff%, back%
  15908.  PRINT "  Copyright (C) 1989 Microsoft Corporation"
  15909.  LOCATE 22, 1: COLOR titleoff%, back%
  15910.  PRINT SPC(55); "<CR> to view fontfile"
  15911.  PRINT SPC(55); "ESC to exit"
  15912.  
  15913.  GOSUB swaptitles
  15914.  GOSUB swaptitles
  15915.  FOR i% = 1 TO totalfonts
  15916.      LOCATE 5 + i%, 20
  15917.      COLOR listfore%, listback%
  15918.      PRINT LEFT$(fontfiles(i%) + "       ", 12)
  15919.  NEXT i%
  15920.  LOCATE 5 + currentfont, 20
  15921.  COLOR listback%, listfore%
  15922.  PRINT LEFT$(fontfiles(currentfont) + "       ", 12)
  15923.  
  15924.  FOR i% = 1 TO totalmodes
  15925.      LOCATE 5 + i%, 50
  15926.      COLOR listfore%, listback%
  15927.      PRINT LEFT$(STR$(modes(i%)) + "   ", 4) + MID$(modedim$, 7 * modes(i%) -
  15928.  NEXT i%
  15929.  LOCATE 5 + currentmode, 50
  15930.  COLOR listback%, listfore%
  15931.  PRINT LEFT$(STR$(modes(currentmode)) + "   ", 4) + MID$(modedim$, 7 * modes(c
  15932.  
  15933.  'Scroll through choices
  15934.  
  15935.  DO
  15936.      SELECT CASE INKEY$
  15937.          CASE CHR$(0) + CHR$(72)
  15938.              GOSUB upone
  15939.          CASE CHR$(0) + CHR$(80)
  15940.              GOSUB downone
  15941.          CASE CHR$(9), CHR$(0) + CHR$(15), CHR$(0) + CHR$(75), CHR$(0) + CHR$(
  15942.              GOSUB swaptitles
  15943.          CASE CHR$(13), CHR$(32): EXIT DO
  15944.          CASE CHR$(27)
  15945.            COLOR 15, 0
  15946.            CLS
  15947.            END
  15948.      END SELECT
  15949.  LOOP
  15950.  EXIT SUB
  15951.  
  15952.  swaptitles:
  15953.      IF set$ = "f" THEN
  15954.          set$ = "m"
  15955.          max% = totalmodes
  15956.          posit% = currentmode
  15957.          LOCATE 5, 20: COLOR titleoff%, back%
  15958.          PRINT "Font files:"
  15959.          LOCATE 5, 50: COLOR titleon%, titleback%
  15960.          PRINT "Screen Modes:"
  15961.      ELSEIF set$ = "m" THEN
  15962.          set$ = "f"
  15963.          max% = totalfonts
  15964.          posit% = currentfont
  15965.          LOCATE 5, 20: COLOR titleon%, titleback%
  15966.          PRINT "Font files:"
  15967.          LOCATE 5, 50: COLOR titleoff%, back%
  15968.          PRINT "Screen Modes:"
  15969.      END IF
  15970.  RETURN
  15971.  
  15972.  upone:
  15973.      oldpos% = posit%
  15974.      posit% = (posit% + max% - 2) MOD max% + 1
  15975.      GOSUB redraw
  15976.  RETURN
  15977.  
  15978.  downone:
  15979.      oldpos% = posit%
  15980.      posit% = posit% MOD max% + 1
  15981.      GOSUB redraw
  15982.  RETURN
  15983.  
  15984.  redraw:
  15985.      IF set$ = "f" THEN
  15986.          LOCATE 5 + oldpos%, 20
  15987.          COLOR listfore%, listback%
  15988.          PRINT LEFT$(fontfiles(oldpos%) + "       ", 12)
  15989.          LOCATE 5 + posit%, 20
  15990.          COLOR listback%, listfore%
  15991.          PRINT LEFT$(fontfiles(posit%) + "       ", 12)
  15992.          currentfont = posit%
  15993.      ELSE
  15994.          LOCATE 5 + oldpos%, 50
  15995.          COLOR listfore%, listback%
  15996.          PRINT LEFT$(STR$(modes(oldpos%)) + "   ", 4) + MID$(modedim$, 7 * mod
  15997.          LOCATE 5 + posit%, 50
  15998.          COLOR listback%, listfore%
  15999.          PRINT LEFT$(STR$(modes(posit%)) + "   ", 4) + MID$(modedim$, 7 * mode
  16000.          currentmode = posit%
  16001.      END IF
  16002.  RETURN
  16003.  
  16004.  END SUB
  16005.  
  16006.  '
  16007.  'GetFiles finds all *.fon files in the current working directory and checks
  16008.  'if they are legitimate.  If the files are ok, they are added to files list.
  16009.  '
  16010.  SUB GetFiles
  16011.  SCREEN 0
  16012.  WIDTH 80, 25
  16013.  tryagain:
  16014.  CLS
  16015.  PRINT "Checking fontfiles..."
  16016.  totalfonts = 0
  16017.  X$ = DIR$("*.fon")
  16018.  IF X$ = "" THEN
  16019.      PRINT "No font files found in current directory."
  16020.      PRINT "Push a shell to change directories? [yn]"
  16021.      try$ = "a"
  16022.      DO UNTIL INSTR(1, "NYny", try$)
  16023.          try$ = INPUT$(1)
  16024.      LOOP
  16025.      SELECT CASE UCASE$(try$)
  16026.          CASE "Y"
  16027.              PRINT "Type 'EXIT' to return to demo."
  16028.              SHELL
  16029.              GOTO tryagain
  16030.          CASE "N"
  16031.              END
  16032.      END SELECT
  16033.  ELSE
  16034.      DO WHILE X$ <> ""
  16035.          PRINT "   "; UCASE$(X$); "--";
  16036.          SetMaxFonts 10, 10
  16037.          Reg% = RegisterFonts(X$)
  16038.          IF Reg% = 0 THEN
  16039.              PRINT "bad font file"
  16040.          ELSE
  16041.              totalfonts = totalfonts + 1
  16042.              fontfiles(totalfonts) = UCASE$(X$)
  16043.              PRINT "OK"
  16044.              IF totalfonts = 18 THEN EXIT DO
  16045.          END IF
  16046.          X$ = DIR$
  16047.      LOOP
  16048.  END IF
  16049.  SLEEP 1
  16050.  END SUB
  16051.  
  16052.  '
  16053.  'GetModes tries all screen modes from 1-13 to see if they are supported.
  16054.  'If a mode is supported, it is added to the list of available modes.
  16055.  '
  16056.  SUB GetModes
  16057.  ON LOCAL ERROR GOTO badmode
  16058.  nextactive% = 1
  16059.  totalmodes = 0
  16060.  FOR i% = 13 TO 1 STEP -1
  16061.      good% = TRUE
  16062.      SCREEN i%
  16063.      IF good% THEN
  16064.          modes(nextactive%) = i%
  16065.          nextactive% = nextactive% + 1
  16066.          totalmodes = totalmodes + 1
  16067.      END IF
  16068.  NEXT i%
  16069.  IF totalmodes = 0 THEN
  16070.      PRINT "No graphics modes available"
  16071.      END
  16072.  END IF
  16073.  
  16074.  IF modes(1) = 13 THEN
  16075.      currentmode = 2
  16076.  ELSE
  16077.      currentmode = 1
  16078.  END IF
  16079.  EXIT SUB
  16080.  badmode:
  16081.      good% = FALSE
  16082.      RESUME NEXT
  16083.  END SUB
  16084.  
  16085.  '
  16086.  'ShowScreen displays all the fonts in the current font file and current
  16087.  'graphics mode.
  16088.  '
  16089.  SUB ShowScreen
  16090.      SetMaxFonts 10, 10
  16091.      TotalReg% = RegisterFonts(fontfiles(currentfont))
  16092.      SCREEN modes(currentmode)
  16093.      PRINT "Please wait..."
  16094.  
  16095.      IF FontErr THEN
  16096.          CLS
  16097.          PRINT "Unable to continue, FontErr ="; FontErr
  16098.          C$ = INPUT$(1)
  16099.          EXIT SUB
  16100.      END IF
  16101.      IF TotalReg% > 10 THEN TotalReg% = 10
  16102.  
  16103.      StrLen% = TotalReg% * 3 - 1
  16104.      IF TotalReg% > 9 THEN StrLen% = StrLen% + TotalReg% - 9
  16105.      LoadStr$ = LEFT$("N1/N2/N3/N4/N5/N6/N7/N8/N9/N10", StrLen%)
  16106.      TotalLoad% = LoadFont(LoadStr$)
  16107.  
  16108.      SELECT CASE modes(currentmode)
  16109.          CASE 1: XS% = 160: YS% = 100
  16110.          CASE 2: XS% = 320: YS% = 100
  16111.          CASE 3: XS% = 360: YS% = 174
  16112.          CASE 4: XS% = 320: YS% = 200
  16113.          CASE 7: XS% = 160: YS% = 100
  16114.          CASE 8: XS% = 320: YS% = 100
  16115.          CASE 9: XS% = 320: YS% = 175
  16116.          CASE 10: XS% = 320: YS% = 175
  16117.          CASE 11: XS% = 320: YS% = 240
  16118.          CASE 12: XS% = 320: YS% = 240
  16119.          CASE 13: XS% = 160: YS% = 100
  16120.      END SELECT
  16121.  
  16122.      prompt$ = "Press any key."
  16123.      FOR i% = 1 TO TotalLoad%
  16124.          CLS
  16125.          SelectFont INT(i%)
  16126.          GetFontInfo FI
  16127.          SetGTextDir 0
  16128.          SetGTextColor 14
  16129.          Length% = OutGText(1, 1, RTRIM$(FI.FaceName))
  16130.          Length% = OutGText(1, 1 + FI.PixHeight, LTRIM$(STR$(FI.Points) + " Po
  16131.          FOR Dir% = 0 TO 3
  16132.              SetGTextDir Dir%
  16133.              SetGTextColor 15 - Dir%
  16134.              SELECT CASE Dir%
  16135.                  CASE 0: X% = XS%: Y% = YS% - FI.PixHeight
  16136.                  CASE 1: X% = XS% - FI.PixHeight: Y% = YS%
  16137.                  CASE 2: X% = XS%: Y% = YS% + FI.PixHeight
  16138.                  CASE 3: X% = XS% + FI.PixHeight: Y% = YS%
  16139.              END SELECT
  16140.              Length% = OutGText(CSNG(X%), CSNG(Y%), "Microsoft")
  16141.          NEXT Dir%
  16142.          SelectFont 2
  16143.          GetFontInfo FI
  16144.          SetGTextColor 14
  16145.          SetGTextDir 0
  16146.          IF i% = TotalLoad% THEN prompt$ = "Press ESC to go on."
  16147.          Length% = GetGTextLen(prompt$)
  16148.          Length% = OutGText(2 * XS% - Length% - 10, 2 * YS% - FI.PixHeight - 1
  16149.          IF i% = TotalLoad% THEN
  16150.              DO UNTIL INKEY$ = CHR$(27): LOOP
  16151.          ELSE
  16152.              a$ = INPUT$(1)
  16153.          END IF
  16154.      NEXT i%
  16155.  END SUB
  16156.  
  16157.  
  16158.  
  16159.  GENERAL.BAS
  16160.  CD-ROM Disc Path:   \SAMPCODE\BASIC\GENERAL.BAS
  16161.  
  16162.  '============================================================================
  16163.  '
  16164.  '     GENERAL.BAS - General Routines for the User Interface Toolbox in
  16165.  '           Microsoft BASIC 7.0, Professional Development System
  16166.  '              Copyright (C) 1987-1989, Microsoft Corporation
  16167.  '
  16168.  '  NOTE:    This sample source code toolbox is intended to demonstrate some
  16169.  '           of the extended capabilities of Microsoft BASIC 7.0 Professional
  16170.  '           Development system that can help to leverage the professional
  16171.  '           developer's time more effectively.  While you are free to use,
  16172.  '           modify, or distribute the routines in this module in any way you
  16173.  '           find useful, it should be noted that these are examples only and
  16174.  '           should not be relied upon as a fully-tested "add-on" library.
  16175.  '
  16176.  '  PURPOSE: These are the general purpose routines needed by the other
  16177.  '           modules in the user interface toolbox.
  16178.  '
  16179.  '  To create a library and QuickLib containing the routines found
  16180.  '  in this file, follow these steps:
  16181.  '       BC /X/FS general.bas
  16182.  '       LIB general.lib + general + uiasm + qbx.lib;
  16183.  '       LINK /Q general.lib, general.qlb,,qbxqlb.lib;
  16184.  '  Creating a library and QuickLib for any of the other UI toolbox files
  16185.  '  (WINDOW.BAS, MENU.BAS and MOUSE.BAS) is done this way also.
  16186.  '
  16187.  '  To create a library and QuickLib containing all routines from
  16188.  '  the User Interface toolbox follow these steps:
  16189.  '       BC /X/FS general.bas
  16190.  '       BC /X/FS window.bas
  16191.  '       BC /X/FS mouse.bas
  16192.  '       BC /X/FS menu.bas
  16193.  '       LIB uitb.lib + general + window + mouse + menu + uiasm + qbx.lib;
  16194.  '       LINK /Q uitb.lib, uitb.qlb,,qbxqlb.lib;
  16195.  '  If you are going to use this QuickLib in conjunction with the font source
  16196.  '  code (FONTB.BAS) or the charting source code (CHRTB.BAS), you need to
  16197.  '  include the assembly code routines referenced in these files.  For the fon
  16198.  '  routines, perform the following LIB command after creating the library but
  16199.  '  before creating the QuickLib as described above:
  16200.  '       LIB uitb.lib + fontasm;
  16201.  '  For the charting routines, perform the following LIB command after creatin
  16202.  '  the library but before creating the QuickLib as described above:
  16203.  '       LIB uitb.lib + chrtasm;
  16204.  '
  16205.  '============================================================================
  16206.  
  16207.  DEFINT A-Z
  16208.  
  16209.  '$INCLUDE: 'general.bi'
  16210.  '$INCLUDE: 'mouse.bi'
  16211.  
  16212.  FUNCTION AltToASCII$ (kbd$)
  16213.      ' =======================================================================
  16214.      ' Converts Alt+A to A,Alt+B to B, etc.  You send it a string.  The right
  16215.      ' most character is compared to the string below, and is converted to
  16216.      ' the proper character.
  16217.      ' =======================================================================
  16218.      index = INSTR("xyz{|}~Çü !" + CHR$(34) + "#$%&,-./012éâ", RI
  16219.  
  16220.      IF index = 0 THEN
  16221.          AltToASCII = ""
  16222.      ELSE
  16223.          AltToASCII = MID$("1234567890QWERTYUIOPASDFGHJKLZXCVBNM-=", index, 1)
  16224.      END IF
  16225.  
  16226.  END FUNCTION
  16227.  
  16228.  SUB Box (row1, col1, row2, col2, fore, back, border$, fillFlag) STATIC
  16229.  
  16230.      '=======================================================================
  16231.      '  Use default border if an illegal border$ is passed
  16232.      '=======================================================================
  16233.  
  16234.      IF LEN(border$) < 9 THEN
  16235.          t$ = "┌─┐│ │└─┘"
  16236.      ELSE
  16237.          t$ = border$
  16238.      END IF
  16239.  
  16240.      ' =======================================================================
  16241.      ' Check coordinates for validity, then draw box
  16242.      ' =======================================================================
  16243.  
  16244.      IF col1 <= (col2 - 2) AND row1 <= (row2 - 2) AND col1 >= MINCOL AND row1
  16245.          MouseHide
  16246.          BoxWidth = col2 - col1 + 1
  16247.          BoxHeight = row2 - row1 + 1
  16248.          LOCATE row1, col1
  16249.          COLOR fore, back
  16250.          PRINT LEFT$(t$, 1); STRING$(BoxWidth - 2, MID$(t$, 2, 1)); MID$(t$, 3
  16251.          LOCATE row2, col1
  16252.          PRINT MID$(t$, 7, 1); STRING$(BoxWidth - 2, MID$(t$, 8, 1)); MID$(t$,
  16253.  
  16254.          FOR a = row1 + 1 TO row1 + BoxHeight - 2
  16255.              LOCATE a, col1
  16256.              PRINT MID$(t$, 4, 1);
  16257.  
  16258.              IF fillFlag THEN
  16259.                  PRINT STRING$(BoxWidth - 2, MID$(t$, 5, 1));
  16260.              ELSE
  16261.                  LOCATE a, col1 + BoxWidth - 1
  16262.              END IF
  16263.  
  16264.              PRINT MID$(t$, 6, 1);
  16265.          NEXT a
  16266.          LOCATE row1 + 1, col1 + 1
  16267.          MouseShow
  16268.      END IF
  16269.  
  16270.  END SUB
  16271.  
  16272.  SUB GetBackground (row1, col1, row2, col2, buffer$) STATIC
  16273.  
  16274.      ' =======================================================================
  16275.      ' Create enough space in buffer$ to hold the screen info behind the box
  16276.      ' Then, call GetCopyBox to store the background in buffer$
  16277.      ' =======================================================================
  16278.  
  16279.      IF row1 >= 1 AND row2 <= MAXROW AND col1 >= 1 AND col2 <= MAXCOL THEN
  16280.          Wid = col2 - col1 + 1
  16281.          Hei = row2 - row1 + 1
  16282.          size = 4 + (2 * Wid * Hei)
  16283.          buffer$ = SPACE$(size)
  16284.  
  16285.          CALL GetCopyBox(row1, col1, row2, col2, buffer$)
  16286.      END IF
  16287.  
  16288.  END SUB
  16289.  
  16290.  FUNCTION GetShiftState (bit)
  16291.  
  16292.      ' =======================================================================
  16293.      ' Returns the shift state after calling interrupt 22
  16294.      '    bit 0 : right shift
  16295.      '        1 : left shift
  16296.      '        2 : ctrl key
  16297.      '        3 : alt key
  16298.      '        4 : scroll lock
  16299.      '        5 : num lock
  16300.      '        6 : caps lock
  16301.      '        7 : insert state
  16302.      ' =======================================================================
  16303.  
  16304.      IF bit >= 0 AND bit <= 7 THEN
  16305.          DIM regs AS RegType
  16306.          regs.ax = 2 * 256
  16307.          INTERRUPT 22, regs, regs
  16308.  
  16309.          IF regs.ax AND 2 ^ bit THEN
  16310.              GetShiftState = TRUE
  16311.          ELSE
  16312.              GetShiftState = FALSE
  16313.          END IF
  16314.      ELSE
  16315.          GetShiftState = FALSE
  16316.      END IF
  16317.  
  16318.  END FUNCTION
  16319.  
  16320.  SUB PutBackground (row, col, buffer$)
  16321.  
  16322.      ' =======================================================================
  16323.      ' This sub checks the boundries before executing the put command
  16324.      ' =======================================================================
  16325.  
  16326.      IF row >= 1 AND row <= MAXROW AND col >= 1 AND col <= MAXCOL THEN
  16327.          CALL PutCopyBox(row, col, buffer$)
  16328.      END IF
  16329.  
  16330.  END SUB
  16331.  
  16332.  SUB scroll (row1, col1, row2, col2, lines, attr)
  16333.  
  16334.      ' =======================================================================
  16335.      ' Make sure coordinates are in proper order
  16336.      ' =======================================================================
  16337.  
  16338.      IF row1 > row2 THEN
  16339.          SWAP row1, row2
  16340.      END IF
  16341.  
  16342.      IF col1 > col2 THEN
  16343.          SWAP col1, col2
  16344.      END IF
  16345.  
  16346.       ' ======================================================================
  16347.       ' If coordinates are valid, prepare registers, and call interrupt
  16348.       ' ======================================================================
  16349.  
  16350.      IF row1 >= MINROW AND row2 <= MAXROW AND col1 >= MINCOL AND col2 <= MAXCO
  16351.          DIM regs AS RegType
  16352.  
  16353.          IF lines < 0 THEN
  16354.              regs.ax = 256 * 7 + (-lines)
  16355.              regs.bx = 256 * attr
  16356.              regs.cx = 256 * (row1 - 1) + (col1 - 1)
  16357.              regs.dx = 256 * (row2 - 1) + (col2 - 1)
  16358.          ELSE
  16359.              regs.ax = 256 * 6 + lines
  16360.              regs.bx = 256 * (attr MOD 8) * 16
  16361.              regs.cx = 256 * (row1 - 1) + (col1 - 1)
  16362.              regs.dx = 256 * (row2 - 1) + (col2 - 1)
  16363.          END IF
  16364.  
  16365.          INTERRUPT 16, regs, regs
  16366.      END IF
  16367.  
  16368.  END SUB
  16369.  
  16370.  
  16371.  
  16372.  INDEX.BAS
  16373.  CD-ROM Disc Path:   \SAMPCODE\BASIC\INDEX.BAS
  16374.  
  16375.  DEFINT A-Z
  16376.  
  16377.  ' Define the symbolic constants used globally in the program:
  16378.  CONST FALSE = 0, TRUE = NOT FALSE
  16379.  
  16380.  ' Define a record structure for random-file records:
  16381.  TYPE StockItem
  16382.          PartNumber AS STRING * 6
  16383.          Description AS STRING * 20
  16384.          UnitPrice AS SINGLE
  16385.          Quantity AS INTEGER
  16386.  END TYPE
  16387.  
  16388.  ' Define a record structure for each element of the index:
  16389.  TYPE IndexType
  16390.          RecordNumber AS INTEGER
  16391.          PartNumber AS STRING * 6
  16392.  END TYPE
  16393.  
  16394.  ' Declare procedures that will be called:
  16395.  DECLARE FUNCTION Filter$ (Prompt$)
  16396.  DECLARE FUNCTION FindRecord% (PartNumber$, RecordVar AS StockItem)
  16397.  
  16398.  DECLARE SUB AddRecord (RecordVar AS StockItem)
  16399.  DECLARE SUB InputRecord (RecordVar AS StockItem)
  16400.  DECLARE SUB PrintRecord (RecordVar AS StockItem)
  16401.  DECLARE SUB SortIndex ()
  16402.  DECLARE SUB ShowPartNumbers ()
  16403.  ' Define a buffer (using the StockItem type)
  16404.  ' and define and dimension the index array:
  16405.  DIM StockRecord AS StockItem, index(1 TO 100) AS IndexType
  16406.  
  16407.  ' Open the random-access file:
  16408.  OPEN "STOCK.DAT" FOR RANDOM AS #1 LEN = LEN(StockRecord)
  16409.  
  16410.  ' Calculate number of records in the file:
  16411.  NumberOfRecords = LOF(1) \ LEN(StockRecord)
  16412.  
  16413.  ' If there are records, read them and build the index:
  16414.  IF NumberOfRecords <> 0 THEN
  16415.          FOR RecordNumber = 1 TO NumberOfRecords
  16416.  
  16417.                  ' Read the data from a new record in the file:
  16418.                  GET #1, RecordNumber, StockRecord
  16419.  
  16420.                  ' Place part number and record number in index:
  16421.                  index(RecordNumber).RecordNumber = RecordNumber
  16422.                  index(RecordNumber).PartNumber = StockRecord.PartNumber
  16423.          NEXT
  16424.  
  16425.          SortIndex            ' Sort index in part-number order.
  16426.  END IF
  16427.  
  16428.  DO                      ' Main-menu loop.
  16429.          CLS
  16430.          PRINT "(A)dd records."
  16431.          PRINT "(L)ook up records."
  16432.          PRINT "(Q)uit program."
  16433.          PRINT
  16434.          LOCATE , , 1
  16435.          PRINT "Type your choice (A, L, or Q) here: ";
  16436.  
  16437.          ' Loop until user presses, A, L, or Q:
  16438.          DO
  16439.                  Choice$ = UCASE$(INPUT$(1))
  16440.          LOOP WHILE INSTR("ALQ", Choice$) = 0
  16441.  
  16442.          ' Branch according to choice:
  16443.          SELECT CASE Choice$
  16444.                  CASE "A"
  16445.           AddRecord StockRecord
  16446.                  CASE "L"
  16447.           IF NumberOfRecords = 0 THEN
  16448.                   PRINT : PRINT "No records in file yet. ";
  16449.                   PRINT "Press any key to continue.";
  16450.                   Pause$ = INPUT$(1)
  16451.           ELSE
  16452.                   InputRecord StockRecord
  16453.           END IF
  16454.                  CASE "Q"          ' End program.
  16455.          END SELECT
  16456.  LOOP UNTIL Choice$ = "Q"
  16457.  
  16458.  CLOSE #1                ' All done, close file and end.
  16459.  END
  16460.  ' ======================== ADDRECORD ======================
  16461.  ' Adds records to the file from input typed at the keyboard
  16462.  ' =========================================================
  16463.  ' ========================= FILTER ========================
  16464.  '       Filters all non-numeric characters from a string
  16465.  '       and returns the filtered string
  16466.  ' =========================================================
  16467.  ' ======================= FINDRECORD ===================
  16468.  '  Uses a binary search to locate a record in the index
  16469.  ' ======================================================
  16470.  ' ======================= PRINTRECORD =====================
  16471.  '                Prints a record on the screen
  16472.  ' =========================================================
  16473.  ' ===================== SHOWPARTNUMBERS ===================
  16474.  ' Prints an index of all the part numbers in the upper part
  16475.  ' of the screen
  16476.  ' =========================================================
  16477.  ' ========================= SORTINDEX =====================
  16478.  '                Sorts the index by part number
  16479.  ' =========================================================
  16480.  
  16481.  
  16482.  SUB AddRecord (RecordVar AS StockItem) STATIC
  16483.          SHARED index() AS IndexType, NumberOfRecords
  16484.          DO
  16485.                  CLS
  16486.                  INPUT "Part Number: ", RecordVar.PartNumber
  16487.                  INPUT "Description: ", RecordVar.Description
  16488.  
  16489.                  ' Call the Filter$ FUNCTION to input price & quantity:
  16490.                  RecordVar.UnitPrice = VAL(Filter$("Unit Price : "))
  16491.                  RecordVar.Quantity = VAL(Filter$("Quantity   : "))
  16492.  
  16493.                  NumberOfRecords = NumberOfRecords + 1
  16494.  
  16495.                  PUT #1, NumberOfRecords, RecordVar
  16496.  
  16497.                  index(NumberOfRecords).RecordNumber = NumberOfRecords
  16498.                  index(NumberOfRecords).PartNumber = RecordVar.PartNumber
  16499.                  PRINT : PRINT "Add another? ";
  16500.                  OK$ = UCASE$(INPUT$(1))
  16501.          LOOP WHILE OK$ = "Y"
  16502.  
  16503.          SortIndex            ' Sort index file again.
  16504.  END SUB
  16505.  
  16506.  FUNCTION Filter$ (Prompt$) STATIC
  16507.          ValTemp2$ = ""
  16508.          PRINT Prompt$;                    ' Print the prompt passed.
  16509.          INPUT "", ValTemp1$               ' Input a number as
  16510.  
  16511.          StringLength = LEN(ValTemp1$)     ' Get the string's length.
  16512.          FOR I% = 1 TO StringLength        ' Go through the string,
  16513.                  Char$ = MID$(ValTemp1$, I%, 1) ' one character at a time.
  16514.  
  16515.                  ' Is the character a valid part of a number (i.e.,
  16516.                  ' a digit or a decimal point)?  If yes, add it to
  16517.                  ' the end of a new string:
  16518.                  IF INSTR(".0123456789", Char$) > 0 THEN
  16519.                          ValTemp2$ = ValTemp2$ + Char$
  16520.  
  16521.                  ' Otherwise, check to see if it's a lowercase "l",
  16522.                  ' since typewriter users may enter a one that way:
  16523.                  ELSEIF Char$ = "l" THEN
  16524.                          ValTemp2$ = ValTemp2$ + "1" ' Change the "l" to a "1"
  16525.                  END IF
  16526.          NEXT I%
  16527.  
  16528.          Filter$ = ValTemp2$               ' Return filtered string.
  16529.  
  16530.  END FUNCTION
  16531.  
  16532.  FUNCTION FindRecord% (Part$, RecordVar AS StockItem) STATIC
  16533.          SHARED index() AS IndexType, NumberOfRecords
  16534.  
  16535.          ' Set top and bottom bounds of search:
  16536.          TopRecord = NumberOfRecords
  16537.          BottomRecord = 1
  16538.  
  16539.          ' Search until top of range is less than bottom:
  16540.          DO UNTIL (TopRecord < BottomRecord)
  16541.  
  16542.                  ' Choose midpoint:
  16543.                  Midpoint = (TopRecord + BottomRecord) \ 2
  16544.  
  16545.                  ' Test to see if it's the one wanted (RTRIM$()
  16546.                  ' trims trailing blanks from a fixed string):
  16547.                  Test$ = RTRIM$(index(Midpoint).PartNumber)
  16548.  
  16549.                  ' If it is, exit loop:
  16550.                  IF Test$ = Part$ THEN
  16551.           EXIT DO
  16552.  
  16553.                  ' Otherwise, if what we're looking for is greater,
  16554.                  ' move bottom up:
  16555.                  ELSEIF Part$ > Test$ THEN
  16556.           BottomRecord = Midpoint + 1
  16557.  
  16558.                  ' Otherwise, move the top down:
  16559.                  ELSE
  16560.           TopRecord = Midpoint - 1
  16561.                  END IF
  16562.          LOOP
  16563.  
  16564.          ' If part was found, input record from file using
  16565.          ' pointer in index and set FindRecord% to TRUE:
  16566.          IF Test$ = Part$ THEN
  16567.                  GET #1, index(Midpoint).RecordNumber, RecordVar
  16568.                  FindRecord% = TRUE
  16569.  
  16570.          ' Otherwise, if part was not found, set FindRecord%
  16571.          ' to FALSE:
  16572.          ELSE
  16573.                  FindRecord% = FALSE
  16574.          END IF
  16575.  END FUNCTION
  16576.  
  16577.  ' ======================= INPUTRECORD =====================
  16578.  '    First, INPUTRECORD calls SHOWPARTNUMBERS, which prints
  16579.  '    a menu of part numbers on the top of the screen. Next,
  16580.  '    INPUTRECORD prompts the user to enter a part number.
  16581.  '    Finally, it calls the FINDRECORD and PRINTRECORD
  16582.  '    procedures to find and print the given record.
  16583.  ' =========================================================
  16584.  SUB InputRecord (RecordVar AS StockItem) STATIC
  16585.          CLS
  16586.          ShowPartNumbers      ' Call the ShowPartNumbers SUB.
  16587.  
  16588.          ' Print data from specified records
  16589.          ' on the bottom part of the screen:
  16590.          DO
  16591.                  PRINT "Type a part number listed above ";
  16592.                  INPUT "(or Q to quit) and press <ENTER>: ", Part$
  16593.                  IF UCASE$(Part$) <> "Q" THEN
  16594.           IF FindRecord(Part$, RecordVar) THEN
  16595.                   PrintRecord RecordVar
  16596.           ELSE
  16597.                   PRINT "Part not found."
  16598.           END IF
  16599.                  END IF
  16600.                  PRINT STRING$(40, "_")
  16601.          LOOP WHILE UCASE$(Part$) <> "Q"
  16602.  
  16603.          VIEW PRINT   ' Restore the text viewport to entire screen.
  16604.  END SUB
  16605.  
  16606.  SUB PrintRecord (RecordVar AS StockItem) STATIC
  16607.          PRINT "Part Number: "; RecordVar.PartNumber
  16608.          PRINT "Description: "; RecordVar.Description
  16609.          PRINT USING "Unit Price :$$###.##"; RecordVar.UnitPrice
  16610.          PRINT "Quantity   :"; RecordVar.Quantity
  16611.  END SUB
  16612.  
  16613.  SUB ShowPartNumbers STATIC
  16614.          SHARED index() AS IndexType, NumberOfRecords
  16615.  
  16616.          CONST NUMCOLS = 8, COLWIDTH = 80 \ NUMCOLS
  16617.  
  16618.          ' At the top of the screen, print a menu indexing all
  16619.          ' the part numbers for records in the file. This menu is
  16620.          ' printed in columns of equal length (except possibly the
  16621.          ' last column, which may be shorter than the others):
  16622.          ColumnLength = NumberOfRecords
  16623.          DO WHILE ColumnLength MOD NUMCOLS
  16624.                  ColumnLength = ColumnLength + 1
  16625.          LOOP
  16626.          ColumnLength = ColumnLength \ NUMCOLS
  16627.          Column = 1
  16628.          RecordNumber = 1
  16629.  DO UNTIL RecordNumber > NumberOfRecords
  16630.                  FOR Row = 1 TO ColumnLength
  16631.           LOCATE Row, Column
  16632.           PRINT index(RecordNumber).PartNumber
  16633.           RecordNumber = RecordNumber + 1
  16634.           IF RecordNumber > NumberOfRecords THEN EXIT FOR
  16635.                  NEXT Row
  16636.                  Column = Column + COLWIDTH
  16637.          LOOP
  16638.  
  16639.          LOCATE ColumnLength + 1, 1
  16640.          PRINT STRING$(80, "_")       ' Print separator line.
  16641.  
  16642.          ' Scroll information about records below the part-number
  16643.          ' menu (this way, the part numbers are not erased):
  16644.          VIEW PRINT ColumnLength + 2 TO 24
  16645.  END SUB
  16646.  
  16647.  SUB SortIndex STATIC
  16648.          SHARED index() AS IndexType, NumberOfRecords
  16649.  
  16650.          ' Set comparison offset to half the number of records
  16651.          ' in index:
  16652.          Offset = NumberOfRecords \ 2
  16653.  
  16654.          ' Loop until offset gets to zero:
  16655.          DO WHILE Offset > 0
  16656.                  Limit = NumberOfRecords - Offset
  16657.                  DO
  16658.  
  16659.           ' Assume no switches at this offset:
  16660.           Switch = FALSE
  16661.  
  16662.           ' Compare elements and switch ones out of order:
  16663.           FOR I = 1 TO Limit
  16664.                   IF index(I).PartNumber > index(I + Offset).PartNumber THEN
  16665.                           SWAP index(I), index(I + Offset)
  16666.                           Switch = I
  16667.                   END IF
  16668.           NEXT I
  16669.  
  16670.           ' Sort on next pass only to where
  16671.           ' last switch was made:
  16672.           Limit = Switch
  16673.                  LOOP WHILE Switch
  16674.  
  16675.                  ' No switches at last offset, try one half as big:
  16676.                  Offset = Offset \ 2
  16677.          LOOP
  16678.  END SUB
  16679.  
  16680.  
  16681.  
  16682.  MANDEL.BAS
  16683.  CD-ROM Disc Path:   \SAMPCODE\BASIC\MANDEL.BAS
  16684.  
  16685.  DEFINT A-Z           ' Default variable type is integer.
  16686.  
  16687.  DECLARE        SUB ShiftPalette ()
  16688.  DECLARE        SUB WindowVals (WL%, WR%, WT%, WB%)
  16689.  DECLARE        SUB ScreenTest (EM%, CR%, VL%, VR%, VT%, VB%)
  16690.  
  16691.  CONST FALSE = 0, TRUE = NOT FALSE ' Boolean constants
  16692.  
  16693.  ' Set maximum number of iterations per point:
  16694.  CONST MAXLOOP =        30, MAXSIZE = 1000000
  16695.  
  16696.  DIM PaletteArray(15)
  16697.  FOR I =        0 TO 15: PaletteArray(I) = I: NEXT I
  16698.  
  16699.  ' Call WindowVals to get coordinates of window corners:
  16700.  WindowVals WLeft, WRight, WTop,        WBottom
  16701.  
  16702.  ' Call ScreenTest to find out if this is an EGA machine
  16703.  ' and get coordinates of viewport corners:
  16704.  ScreenTest EgaMode, ColorRange,        VLeft, VRight, VTop, VBottom
  16705.  
  16706.  ' Define viewport and corresponding window:
  16707.  VIEW (VLeft, VTop)-(VRight, VBottom), 0, ColorRange
  16708.  WINDOW (WLeft, WTop)-(WRight, WBottom)
  16709.  
  16710.  LOCATE 24, 10 : PRINT "Press any key to quit.";
  16711.  
  16712.  XLength        = VRight - VLeft
  16713.  YLength        = VBottom - VTop
  16714.  ColorWidth = MAXLOOP \ ColorRange
  16715.  
  16716.  ' Loop through each pixel in viewport and calculate
  16717.  ' whether or not it is in the Mandelbrot Set:
  16718.  FOR Y =        0 TO YLength           ' Loop through every line
  16719.                             ' in the viewport.
  16720.     LogicY = PMAP(Y, 3)           ' Get the pixel's view
  16721.                             ' y-coordinate.
  16722.     PSET        (WLeft,        LogicY)           ' Plot leftmost pixel in the
  16723.     OldColor = 0                   ' Start with background color.
  16724.  
  16725.     FOR X = 0 TO        XLength           ' Loop through every pixel
  16726.                             ' in the line.
  16727.        LogicX = PMAP(X, 2)  ' Get the pixel's view
  16728.                             ' x-coordinate.
  16729.        MandelX& = LogicX
  16730.        MandelY& = LogicY
  16731.        ' Do the calculations to see if this point
  16732.        ' is in the Mandelbrot Set:
  16733.        FOR I = 1        TO MAXLOOP
  16734.           RealNum& = MandelX& * MandelX&
  16735.           ImagNum& = MandelY& * MandelY&
  16736.           IF (RealNum& + ImagNum&) >= MAXSIZE THEN EXIT FOR
  16737.           MandelY& = (MandelX& * MandelY&) \ 250 + LogicY
  16738.           MandelX& = (RealNum& - ImagNum&) \ 500 + LogicX
  16739.        NEXT I
  16740.  
  16741.        '        Assign a color to the point:
  16742.        PColor = I \ ColorWidth
  16743.  
  16744.        '        If color has changed, draw a line from
  16745.        ' the last point referenced to the new point,
  16746.        '        using the old color:
  16747.        IF PColor        <> OldColor THEN
  16748.           LINE -(LogicX, LogicY), (ColorRange - OldColor)
  16749.           OldColor = PColor
  16750.        END IF
  16751.  
  16752.        IF INKEY$        <> "" THEN END
  16753.     NEXT        X
  16754.  
  16755.     ' Draw the last line        segment        to the right edge
  16756.     ' of the viewport:
  16757.     LINE        -(LogicX, LogicY), (ColorRange - OldColor)
  16758.  
  16759.     ' If        this is        an EGA machine,        shift the palette after
  16760.     ' drawing each line:
  16761.     IF EgaMode THEN ShiftPalette
  16762.  NEXT Y
  16763.  
  16764.  DO
  16765.     ' Continue shifting the palette
  16766.     ' until the user presses a key:
  16767.     IF EgaMode THEN ShiftPalette
  16768.  LOOP WHILE INKEY$ = ""
  16769.  
  16770.  SCREEN 0, 0                ' Restore the screen to text mode,
  16771.  WIDTH 80                ' 80 columns.
  16772.  END
  16773.  
  16774.  BadScreen:                ' Error handler that is invoked if
  16775.     EgaMode = FALSE        ' there is no EGA graphics card
  16776.     RESUME NEXT
  16777.  ' ====================== ShiftPalette =====================
  16778.  '    Rotates the palette by one each time it is called
  16779.  ' =========================================================
  16780.  
  16781.  SUB ShiftPalette STATIC
  16782.     SHARED PaletteArray(), ColorRange
  16783.  
  16784.     FOR I = 1 TO        ColorRange
  16785.        PaletteArray(I) =        (PaletteArray(I) MOD ColorRange) + 1
  16786.     NEXT        I
  16787.     PALETTE USING PaletteArray(0)
  16788.  
  16789.  END SUB
  16790.  ' ======================= ScreenTest ======================
  16791.  '    Uses a SCREEN 8 statement as a test to see if user has
  16792.  '    EGA hardware. If this causes an error, the EM flag is
  16793.  '    set to FALSE, and the screen is set with SCREEN 1.
  16794.  
  16795.  '    Also sets values for corners of viewport (VL = left,
  16796.  '    VR = right, VT = top, VB = bottom), scaled with the
  16797.  '    correct aspect ratio so viewport is a perfect square.
  16798.  ' =========================================================
  16799.  
  16800.  SUB ScreenTest (EM, CR,        VL, VR,        VT, VB) STATIC
  16801.     EM =        TRUE
  16802.     ON ERROR GOTO BadScreen
  16803.     SCREEN 8, 1
  16804.     ON ERROR GOTO 0
  16805.  
  16806.     IF EM THEN                        ' No error, SCREEN 8 is OK.
  16807.        VL = 110: VR = 529
  16808.        VT = 5: VB = 179
  16809.        CR = 15                        ' 16 colors (0 - 15)
  16810.  
  16811.     ELSE                                ' Error, so use SCREEN 1.
  16812.        SCREEN 1,        1
  16813.        VL = 55: VR = 264
  16814.        VT = 5: VB = 179
  16815.        CR = 3                        ' 4 colors (0 - 3)
  16816.     END IF
  16817.  
  16818.  END SUB
  16819.  ' ======================= WindowVals ======================
  16820.  '     Gets window corners as input from the user, or sets
  16821.  '     values for the corners if there is no input
  16822.  ' =========================================================
  16823.  
  16824.  SUB WindowVals (WL, WR,        WT, WB)        STATIC
  16825.     CLS
  16826.     PRINT "This program prints the graphic representation of"
  16827.     PRINT "the complete Mandelbrot Set. The default window"
  16828.     PRINT "is from (-1000,625) to (250,-625). To zoom in on"
  16829.     PRINT "part of the figure, input coordinates inside"
  16830.     PRINT "this window."
  16831.     PRINT "Press <ENTER> to see the default window or"
  16832.     PRINT "any other key to input window coordinates: ";
  16833.     LOCATE , , 1
  16834.     Resp$ = INPUT$(1)
  16835.  
  16836.     ' User didn't press ENTER, so input window corners:
  16837.     IF Resp$ <> CHR$(13)        THEN
  16838.        PRINT
  16839.        INPUT "x-coordinate of upper-left corner: ", WL
  16840.        DO
  16841.           INPUT "x-coordinate of lower-right corner: ", WR
  16842.           IF WR <= WL THEN
  16843.              PRINT "Right corner must be greater than left corner."
  16844.           END IF
  16845.        LOOP WHILE WR <= WL
  16846.        INPUT "y-coordinate of upper-left corner: ", WT
  16847.        DO
  16848.           INPUT "y-coordinate of lower-right corner: ", WB
  16849.           IF WB >= WT THEN
  16850.              PRINT "Bottom corner must be less than top corner."
  16851.           END IF
  16852.        LOOP WHILE WB >= WT
  16853.  
  16854.     ' User pressed ENTER, so set default values:
  16855.     ELSE
  16856.        WL = -1000
  16857.        WR = 250
  16858.        WT = 625
  16859.        WB = -625
  16860.     END IF
  16861.  END SUB
  16862.  
  16863.  
  16864.  
  16865.  MATB.BAS
  16866.  CD-ROM Disc Path:   \SAMPCODE\BASIC\MATB.BAS
  16867.  
  16868.  '*** MATB.BAS - Matrix Math Routines for the Matrix Math Toolbox in
  16869.  '           Microsoft BASIC 7.0, Professional Development System
  16870.  '              Copyright (C) 1987-1989, Microsoft Corporation
  16871.  '
  16872.  '  NOTE:  This sample source code toolbox is intended to demonstrate some
  16873.  '  of the extended capabilities of Microsoft BASIC 7.0 Professional Developme
  16874.  '  system that can help to leverage the professional developer's time more
  16875.  '  effectively.  While you are free to use, modify, or distribute the routine
  16876.  '  in this module in any way you find useful, it should be noted that these a
  16877.  '  examples only and should not be relied upon as a fully-tested "add-on"
  16878.  '  library.
  16879.  '
  16880.  '  Purpose:
  16881.  'This toolbox contains routines which perform elementary operations on system
  16882.  'of linear equations represented as matrices.  The functions return integer
  16883.  'error codes in the name and results in the parameter list.  The functions
  16884.  'matbs?% and matlu?% found in this module are intended for internal use only.
  16885.  'Error codes returned:
  16886.  '     0  no error                     -1  matrix not invertible
  16887.  '    -2  matrix not square            -3  inner dimensions different
  16888.  '    -4  matrix dimensions different  -5  result matrix dimensioned incorrect
  16889.  '    any other codes returned are standard BASIC errors
  16890.  '
  16891.  '-------------------------------------------------------------------
  16892.  'MatDet, MatSEqn, and MatInv all use LU-decomposition to implement Gaussian
  16893.  'elimination.  A brief explanation of what is meant by an LU matrix is given
  16894.  'below, followed by simplified versions of the two internal routines used to
  16895.  'do all elimination.
  16896.  '
  16897.  'What is meant by an LU matrix:
  16898.  'An upper triangle matrix (one with all nonzero entries on or above the main
  16899.  'diagonal) can be solved immediately.  The goal of Gaussian elimination is to
  16900.  'transform a non upper triangle system into an equivalent triangular one.
  16901.  '
  16902.  'Given a system of equations represented in matrix form by Ax=b, we need a
  16903.  'linear transformation L such that LA=U where U is and upper triangular matri
  16904.  'Then Ux=LAx=Lb and Ux=Lb is an upper triangular system.
  16905.  '
  16906.  'This library explicitly calculates U, but L is never saved in its own array.
  16907.  'When we do a row operation to create a zero below the main diagonal, we no
  16908.  'longer need to save that value because we know it is zero.  This leaves the
  16909.  'space available to save the multiplier used in the row operation.  When
  16910.  'elimination is completed (ie, when the matrix is upper triangular), these
  16911.  'multipliers give us a complete record of what we did to A to make it upper
  16912.  'triangular.  This is equivalent to saying the multipliers represent L.  We n
  16913.  'have a U and an L stored in the same matrix!  This type of matrix will be
  16914.  'referred to as an LU matrix, or just LU.
  16915.  '
  16916.  'The following code fragments get LU and backsolve Ux=Lb.  The actual routine
  16917.  'used in the toolbox are much more involved because they implement total
  16918.  'pivoting and implicit row scaling to reduce round off errors.  However, all
  16919.  'extras (pivoting, scaling, error checking) are extraneous to the main routin
  16920.  'which total only 20 lines.  If you are unfamilar with this type of matrix ma
  16921.  'gaining an understanding of these 20 lines is a very good introduction.  Try
  16922.  'working through a 2x2 or 3x3 example by hand to see what is happening.  The
  16923.  'numerical techniques used to reduce round off error will not be discussed.
  16924.  '
  16925.  '-------------------------------------------------------------------
  16926.  'Given the coefficient matrix A(1 TO N, 1 TO N) and the vector b(1 TO N),
  16927.  'the following fragments will find x(1 TO N) satisfying Ax=b using Gaussian
  16928.  'elimination.
  16929.  '
  16930.  'matlu:
  16931.  'Perform row operations to get all zeroes below the main diagonal.
  16932.  'Define Rj(1 TO N) to be the vector corresponding to the jth row of A.
  16933.  'Let Rrow = Rrow + m*Rpvt where m = -Rrow(pvt)/Rpvt(pvt).
  16934.  'Then A(row, pvt)=0.
  16935.  '
  16936.  '** FOR pvt = 1 TO (N - 1)
  16937.  '**    FOR row = (pvt + 1) TO N
  16938.  '**       'Save m for later use in the space just made 0.
  16939.  '**       A(row, pvt) = -A(row, pvt) / A(pvt, pvt)
  16940.  '**       'Do the row operation.
  16941.  '**       FOR col = (pvt + 1) TO N
  16942.  '**          A(row, col) = A(row, col) + A(row, pvt) * A(pvt, col)
  16943.  '**       NEXT col
  16944.  '**    NEXT row
  16945.  '** NEXT pvt
  16946.  '
  16947.  'matbs:
  16948.  'Do the same row operations on b using the multipliers saved in A.
  16949.  '
  16950.  '** FOR pvt = 1 TO (N - 1)
  16951.  '**    FOR row = (pvt + 1) TO N
  16952.  '**       b(row) = b(row) + A(row, pvt) * b(pvt)
  16953.  '**    NEXT row
  16954.  '** NEXT pvt
  16955.  '
  16956.  'Backsolve Ux=Lb to find x.
  16957.  '                               N
  16958.  'For r = N to 1, x(r) = [b(r) - Σ (A(r,c)*x(c))]/A(r,r)
  16959.  '                              c=r+1
  16960.  '** FOR row = N TO 1 STEP -1
  16961.  '**    x(row) = b(row)
  16962.  '**    FOR col = (row + 1) TO N
  16963.  '**       x(row) = x(row) - A(row, col) * x(col)
  16964.  '**    NEXT col
  16965.  '**    x(row) = x(row) / A(row, row)
  16966.  '** NEXT row
  16967.  '
  16968.  '===================================================================
  16969.  '$INCLUDE: 'matb.bi'
  16970.  DECLARE FUNCTION matbsD% (A() AS DOUBLE, b() AS DOUBLE, x() AS DOUBLE)
  16971.  DECLARE FUNCTION matbsS% (A() AS SINGLE, b() AS SINGLE, x() AS SINGLE)
  16972.  DECLARE FUNCTION matluD% (A() AS DOUBLE)
  16973.  DECLARE FUNCTION matluS% (A() AS SINGLE)
  16974.  DIM SHARED lo AS INTEGER, up AS INTEGER
  16975.  DIM SHARED continue AS INTEGER, count AS INTEGER
  16976.  DIM SHARED rpvt(lo TO up) AS INTEGER, cpvt(lo TO up) AS INTEGER
  16977.  END
  16978.  
  16979.  '=======================MatAddC%====================================
  16980.  'MatAddC% adds two currency type matrices and places the sum in
  16981.  'the first.
  16982.  '
  16983.  'Parameters: matrices Alpha,Beta
  16984.  '
  16985.  'Returns: Alpha() = Alpha() + Beta()
  16986.  '===================================================================
  16987.  FUNCTION MatAddC% (Alpha() AS CURRENCY, Beta() AS CURRENCY)
  16988.  ON LOCAL ERROR GOTO cadderr: MatAddC% = 0
  16989.  'check if Alpha, Beta have same dimensions if not, exit and send back error
  16990.  IF (LBOUND(Alpha, 1) <> LBOUND(Beta, 1)) OR (UBOUND(Alpha, 1) <> UBOUND(Beta,
  16991.  'loop through and add elements
  16992.  FOR row% = LBOUND(Alpha, 1) TO UBOUND(Alpha, 1)
  16993.     FOR col% = LBOUND(Alpha, 2) TO UBOUND(Alpha, 2)
  16994.        Alpha(row%, col%) = Alpha(row%, col%) + Beta(row%, col%)
  16995.     NEXT col%
  16996.  NEXT row%
  16997.  caddexit:
  16998.  EXIT FUNCTION
  16999.  cadderr:
  17000.     MatAddC% = (ERR + 5) MOD 200 - 5
  17001.     RESUME caddexit
  17002.  END FUNCTION
  17003.  
  17004.  '=======================MatAddD%====================================
  17005.  'MatAddD% adds two double precision matrices and places the sum in
  17006.  'the first.
  17007.  '
  17008.  'Parameters: matrices Alpha,Beta
  17009.  '
  17010.  'Returns: Alpha() = Alpha() + Beta()
  17011.  '===================================================================
  17012.  FUNCTION MatAddD% (Alpha() AS DOUBLE, Beta() AS DOUBLE)
  17013.  ON LOCAL ERROR GOTO dadderr: MatAddD% = 0
  17014.  'check if Alpha, Beta have same dimensions if not, exit and send back error
  17015.  IF (LBOUND(Alpha, 1) <> LBOUND(Beta, 1)) OR (UBOUND(Alpha, 1) <> UBOUND(Beta,
  17016.  'loop through and add elements
  17017.  FOR row% = LBOUND(Alpha, 1) TO UBOUND(Alpha, 1)
  17018.     FOR col% = LBOUND(Alpha, 2) TO UBOUND(Alpha, 2)
  17019.        Alpha(row%, col%) = Alpha(row%, col%) + Beta(row%, col%)
  17020.     NEXT col%
  17021.  NEXT row%
  17022.  daddexit:
  17023.  EXIT FUNCTION
  17024.  dadderr:
  17025.     MatAddD% = (ERR + 5) MOD 200 - 5
  17026.     RESUME daddexit
  17027.  END FUNCTION
  17028.  
  17029.  '=======================MatAddI%====================================
  17030.  'MatAddI% adds two integer matrices and places the sum in
  17031.  'the first.
  17032.  '
  17033.  'Parameters: matrices Alpha,Beta
  17034.  '
  17035.  'Returns: Alpha() = Alpha() + Beta()
  17036.  '===================================================================
  17037.  FUNCTION MatAddI% (Alpha() AS INTEGER, Beta() AS INTEGER)
  17038.  ON LOCAL ERROR GOTO iadderr: MatAddI% = 0
  17039.  'check if Alpha, Beta have same dimensions if not, exit and send back error
  17040.  IF (LBOUND(Alpha, 1) <> LBOUND(Beta, 1)) OR (UBOUND(Alpha, 1) <> UBOUND(Beta,
  17041.  'loop through and add elements
  17042.  FOR row% = LBOUND(Alpha, 1) TO UBOUND(Alpha, 1)
  17043.     FOR col% = LBOUND(Alpha, 2) TO UBOUND(Alpha, 2)
  17044.        Alpha(row%, col%) = Alpha(row%, col%) + Beta(row%, col%)
  17045.     NEXT col%
  17046.  NEXT row%
  17047.  iaddexit:
  17048.  EXIT FUNCTION
  17049.  iadderr:
  17050.     MatAddI% = (ERR + 5) MOD 200 - 5
  17051.     RESUME iaddexit
  17052.  END FUNCTION
  17053.  
  17054.  '=======================MatAddL%====================================
  17055.  'MatAddL% adds two long integer matrices and places the sum in
  17056.  'the first.
  17057.  '
  17058.  'Parameters: matrices Alpha,Beta
  17059.  '
  17060.  'Returns: Alpha() = Alpha() + Beta()
  17061.  '===================================================================
  17062.  FUNCTION MatAddL% (Alpha() AS LONG, Beta() AS LONG)
  17063.  ON LOCAL ERROR GOTO ladderr: MatAddL% = 0
  17064.  'check if Alpha, Beta have same dimensions if not, exit and send back error
  17065.  IF (LBOUND(Alpha, 1) <> LBOUND(Beta, 1)) OR (UBOUND(Alpha, 1) <> UBOUND(Beta,
  17066.  'loop through and add elements
  17067.  FOR row% = LBOUND(Alpha, 1) TO UBOUND(Alpha, 1)
  17068.     FOR col% = LBOUND(Alpha, 2) TO UBOUND(Alpha, 2)
  17069.        Alpha(row%, col%) = Alpha(row%, col%) + Beta(row%, col%)
  17070.     NEXT col%
  17071.  NEXT row%
  17072.  laddexit:
  17073.  EXIT FUNCTION
  17074.  ladderr:
  17075.     MatAddL% = (ERR + 5) MOD 200 - 5
  17076.     RESUME laddexit
  17077.  END FUNCTION
  17078.  
  17079.  '=======================MatAddS%====================================
  17080.  'MatAddS% adds two single precision matrices and places the sum in
  17081.  'the first.
  17082.  '
  17083.  'Parameters: matrices Alpha,Beta
  17084.  '
  17085.  'Returns: Alpha() = Alpha() + Beta()
  17086.  '===================================================================
  17087.  FUNCTION MatAddS% (Alpha() AS SINGLE, Beta() AS SINGLE)
  17088.  ON LOCAL ERROR GOTO sadderr: MatAddS% = 0
  17089.  'check if Alpha, Beta have same dimensions if not, exit and send back error
  17090.  IF (LBOUND(Alpha, 1) <> LBOUND(Beta, 1)) OR (UBOUND(Alpha, 1) <> UBOUND(Beta,
  17091.  'loop through and add elements
  17092.  FOR row% = LBOUND(Alpha, 1) TO UBOUND(Alpha, 1)
  17093.     FOR col% = LBOUND(Alpha, 2) TO UBOUND(Alpha, 2)
  17094.        Alpha(row%, col%) = Alpha(row%, col%) + Beta(row%, col%)
  17095.     NEXT col%
  17096.  NEXT row%
  17097.  saddexit:
  17098.  EXIT FUNCTION
  17099.  sadderr:
  17100.     MatAddS% = (ERR + 5) MOD 200 - 5
  17101.     RESUME saddexit
  17102.  END FUNCTION
  17103.  
  17104.  '========================matbsD=====================================
  17105.  'matbsD% takes a matrix in LU form, found by matluD%, and a vector b
  17106.  'and solves the system Ux=Lb for x. matrices A,b,x are double precision.
  17107.  '
  17108.  'Parameters: LU matrix in A, corresponding pivot vectors in rpvt and cpvt,
  17109.  '            right side in b
  17110.  '
  17111.  'Returns: solution in x, b is modified, rest unchanged
  17112.  '===================================================================
  17113.  FUNCTION matbsD% (A() AS DOUBLE, b() AS DOUBLE, x() AS DOUBLE)
  17114.  ON LOCAL ERROR GOTO dbserr: matbsD% = 0
  17115.  'do row operations on b using the multipliers in L to find Lb
  17116.  FOR pvt% = lo TO (up - 1)
  17117.     c% = cpvt(pvt%)
  17118.     FOR row% = (pvt% + 1) TO up
  17119.        r% = rpvt(row%)
  17120.        b(r%) = b(r%) + A(r%, c%) * b(rpvt(pvt%))
  17121.     NEXT row%
  17122.  NEXT pvt%
  17123.  'backsolve Ux=Lb to find x
  17124.  FOR row% = up TO lo STEP -1
  17125.     c% = cpvt(row%)
  17126.     r% = rpvt(row%)
  17127.     x(c%) = b(r%)
  17128.     FOR col% = (row% + 1) TO up
  17129.        x(c%) = x(c%) - A(r%, cpvt(col%)) * x(cpvt(col%))
  17130.     NEXT col%
  17131.     x(c%) = x(c%) / A(r%, c%)
  17132.  NEXT row%
  17133.  dbsexit:
  17134.  EXIT FUNCTION
  17135.  dbserr:
  17136.     matbsD% = ERR
  17137.     RESUME dbsexit
  17138.  END FUNCTION
  17139.  
  17140.  '========================matbsS=====================================
  17141.  'matbsS% takes a matrix in LU form, found by matluS%, and a vector b
  17142.  'and solves the system Ux=Lb for x. matrices A,b,x are single precision.
  17143.  '
  17144.  'Parameters: LU matrix in A, corresponding pivot vectors in rpvt and cpvt,
  17145.  '            right side in b
  17146.  '
  17147.  'Returns: solution in x, b is modified, rest unchanged
  17148.  '===================================================================
  17149.  FUNCTION matbsS% (A() AS SINGLE, b() AS SINGLE, x() AS SINGLE)
  17150.  ON LOCAL ERROR GOTO sbserr: matbsS% = 0
  17151.  'do row operations on b using the multipliers in L to find Lb
  17152.  FOR pvt% = lo TO (up - 1)
  17153.     c% = cpvt(pvt%)
  17154.     FOR row% = (pvt% + 1) TO up
  17155.        r% = rpvt(row%)
  17156.        b(r%) = b(r%) + A(r%, c%) * b(rpvt(pvt%))
  17157.     NEXT row%
  17158.  NEXT pvt%
  17159.  'backsolve Ux=Lb to find x
  17160.  FOR row% = up TO lo STEP -1
  17161.     c% = cpvt(row%)
  17162.     r% = rpvt(row%)
  17163.     x(c%) = b(r%)
  17164.     FOR col% = (row% + 1) TO up
  17165.        x(c%) = x(c%) - A(r%, cpvt(col%)) * x(cpvt(col%))
  17166.     NEXT col%
  17167.     x(c%) = x(c%) / A(r%, c%)
  17168.  NEXT row%
  17169.  sbsexit:
  17170.  EXIT FUNCTION
  17171.  sbserr:
  17172.     matbsS% = ERR
  17173.     RESUME sbsexit
  17174.  END FUNCTION
  17175.  
  17176.  '========================MatDetC%===================================
  17177.  'MatDetC% finds the determinant of a square, currency type matrix
  17178.  '
  17179.  'Parameters: A(n x n) matrix, det@ to return the determinant
  17180.  '
  17181.  'Returns: matrix A in LU form, determinant
  17182.  '===================================================================
  17183.  FUNCTION MatDetC% (A() AS CURRENCY, det@)
  17184.  ON LOCAL ERROR GOTO cdeterr: errcode% = 0
  17185.  lo = LBOUND(A, 1)
  17186.  up = UBOUND(A, 1)
  17187.  REDIM rpvt(lo TO up) AS INTEGER, cpvt(lo TO up) AS INTEGER
  17188.  'make temporary double precision matrix to find pivots
  17189.  DIM Tmp(lo TO up, LBOUND(A, 2) TO UBOUND(A, 2)) AS DOUBLE
  17190.  FOR row% = lo TO up
  17191.     FOR col% = LBOUND(A, 2) TO UBOUND(A, 2)
  17192.        Tmp(row%, col%) = CDBL(A(row%, col%))
  17193.     NEXT col%
  17194.  NEXT row%
  17195.  errcode% = matluD%(Tmp())              'Get LU matrix
  17196.  IF NOT continue THEN
  17197.     IF errcode% = 199 THEN det@ = 0@
  17198.     ERROR errcode%
  17199.  ELSE
  17200.     detD# = 1#                          '+/- determinant = product of the pivo
  17201.     FOR pvt% = lo TO up
  17202.        detD# = detD# * Tmp(rpvt(pvt%), cpvt(pvt%))
  17203.     NEXT pvt%                           'count contains the total number of ro
  17204.     det@ = (-1@) ^ count * CCUR(detD#)  'and column switches due to pivoting.
  17205.     IF errcode% THEN ERROR errcode%     'multiply the determinant by -1 for
  17206.  END IF                                 'each switch.
  17207.  cdetexit:
  17208.  ERASE rpvt, cpvt, Tmp
  17209.  MatDetC% = errcode%
  17210.  EXIT FUNCTION
  17211.  cdeterr:
  17212.     errcode% = (ERR + 5) MOD 200 - 5
  17213.     RESUME cdetexit
  17214.  END FUNCTION
  17215.  
  17216.  '========================MatDetD%===================================
  17217.  'MatDetD% finds the determinant of a square, double precision matrix
  17218.  '
  17219.  'Parameters: A(n x n) matrix, det# to return the determinant
  17220.  '
  17221.  'Returns: matrix A in LU form, determinant
  17222.  '===================================================================
  17223.  FUNCTION MatDetD% (A() AS DOUBLE, det#)
  17224.  ON LOCAL ERROR GOTO ddeterr: errcode% = 0
  17225.  lo = LBOUND(A, 1)
  17226.  up = UBOUND(A, 1)
  17227.  REDIM rpvt(lo TO up) AS INTEGER, cpvt(lo TO up) AS INTEGER
  17228.  errcode% = matluD%(A())             'Get LU matrix
  17229.  IF NOT continue THEN
  17230.     IF errcode% = 199 THEN det# = 0#
  17231.     ERROR errcode%
  17232.  ELSE
  17233.     det# = 1#                        '+/- determinant = product of the pivots
  17234.     FOR pvt% = lo TO up
  17235.        det# = det# * A(rpvt(pvt%), cpvt(pvt%))
  17236.     NEXT pvt%                         'count contains the total number of row
  17237.     det# = (-1) ^ count * det#        'and column switches due to pivoting.
  17238.     IF errcode% THEN ERROR errcode%   'multiply the determinant by -1 for
  17239.  END IF                               'each switch
  17240.  ddetexit:
  17241.  ERASE rpvt, cpvt
  17242.  MatDetD% = errcode%
  17243.  EXIT FUNCTION
  17244.  ddeterr:
  17245.     errcode% = (ERR + 5) MOD 200 - 5
  17246.     RESUME ddetexit
  17247.  END FUNCTION
  17248.  
  17249.  '========================MatDetI%===================================
  17250.  'MatDetI% finds the determinant of a square, integer matrix
  17251.  '
  17252.  'Parameters: A(n x n) matrix, det% to return the determinant
  17253.  '
  17254.  'Returns: matrix A unchanged, determinant
  17255.  '===================================================================
  17256.  FUNCTION MatDetI% (A() AS INTEGER, det%)
  17257.  ON LOCAL ERROR GOTO ideterr: errcode% = 0
  17258.  lo = LBOUND(A, 1)
  17259.  up = UBOUND(A, 1)
  17260.  REDIM rpvt(lo TO up) AS INTEGER, cpvt(lo TO up) AS INTEGER
  17261.  'make temporary single precision matrix to find pivots
  17262.  DIM Tmp(lo TO up, LBOUND(A, 2) TO UBOUND(A, 2)) AS SINGLE
  17263.  FOR row% = lo TO up
  17264.     FOR col% = LBOUND(A, 2) TO UBOUND(A, 2)
  17265.        Tmp(row%, col%) = CSNG(A(row%, col%))
  17266.     NEXT col%
  17267.  NEXT row%
  17268.  errcode% = matluS%(Tmp())              'Get LU matrix
  17269.  IF NOT continue THEN
  17270.     IF errcode% = 199 THEN det% = 0
  17271.     ERROR errcode%
  17272.  ELSE
  17273.     detS! = 1!                          '+/- determinant = product of the pivo
  17274.     FOR pvt% = lo TO up
  17275.        detS! = detS! * Tmp(rpvt(pvt%), cpvt(pvt%))
  17276.     NEXT pvt%                           'count contains the total number of ro
  17277.     det% = (-1) ^ count * CINT(detS!)   'and column switches due to pivoting.
  17278.     IF errcode% THEN ERROR errcode%     'multiply the determinant by -1 for
  17279.  END IF                                 'each switch
  17280.  idetexit:
  17281.  ERASE rpvt, cpvt, Tmp
  17282.  MatDetI% = errcode%
  17283.  EXIT FUNCTION
  17284.  ideterr:
  17285.     errcode% = (ERR + 5) MOD 200 - 5
  17286.     RESUME idetexit
  17287.  END FUNCTION
  17288.  
  17289.  '========================MatDetL%===================================
  17290.  'MatDetL% finds the determinant of a square, long integer matrix
  17291.  '
  17292.  'Parameters: A(n x n) matrix, det& to return the determinant
  17293.  '
  17294.  'Returns: matrix A unchanged, determinant
  17295.  '===================================================================
  17296.  FUNCTION MatDetL% (A() AS LONG, det&)
  17297.  ON LOCAL ERROR GOTO ldeterr: errcode% = 0
  17298.  lo = LBOUND(A, 1)
  17299.  up = UBOUND(A, 1)
  17300.  REDIM rpvt(lo TO up) AS INTEGER, cpvt(lo TO up) AS INTEGER
  17301.  'make temporary double precision matrix to find pivots
  17302.  DIM Tmp(lo TO up, LBOUND(A, 2) TO UBOUND(A, 2)) AS DOUBLE
  17303.  FOR row% = lo TO up
  17304.     FOR col% = LBOUND(A, 2) TO UBOUND(A, 2)
  17305.        Tmp(row%, col%) = CDBL(A(row%, col%))
  17306.     NEXT col%
  17307.  NEXT row%
  17308.  errcode% = matluD%(Tmp())              'Get LU matrix
  17309.  IF NOT continue THEN
  17310.     IF errcode% = 199 THEN det& = 0&
  17311.     ERROR errcode%
  17312.  ELSE
  17313.     detD# = 1#                          '+/- determinant = product of the pivo
  17314.     FOR pvt% = lo TO up
  17315.        detD# = detD# * Tmp(rpvt(pvt%), cpvt(pvt%))
  17316.     NEXT pvt%                           'count contains the total number of ro
  17317.     det& = (-1&) ^ count * CLNG(detD#)  'and column switches due to pivoting.
  17318.     IF errcode% THEN ERROR errcode%     'multiply the determinant by -1 for
  17319.  END IF                                 'each switch
  17320.  ldetexit:
  17321.  ERASE rpvt, cpvt, Tmp
  17322.  MatDetL% = errcode%
  17323.  EXIT FUNCTION
  17324.  ldeterr:
  17325.     errcode% = (ERR + 5) MOD 200 - 5
  17326.     RESUME ldetexit
  17327.  END FUNCTION
  17328.  
  17329.  '========================MatDetS%===================================
  17330.  'MatDetS% finds the determinant of a square, single precision matrix
  17331.  '
  17332.  'Parameters: A(n x n) matrix, det! to return the determinant
  17333.  '
  17334.  'Returns: matrix A in LU form, determinant
  17335.  '===================================================================
  17336.  FUNCTION MatDetS% (A() AS SINGLE, det!)
  17337.  ON LOCAL ERROR GOTO sdeterr: errcode% = 0
  17338.  lo = LBOUND(A, 1)
  17339.  up = UBOUND(A, 1)
  17340.  REDIM rpvt(lo TO up) AS INTEGER, cpvt(lo TO up) AS INTEGER
  17341.  errcode% = matluS%(A())                'Get LU matrix
  17342.  IF NOT continue THEN
  17343.     IF errcode% = 199 THEN det! = 0!
  17344.     ERROR errcode%
  17345.  ELSE
  17346.     det! = 1!                           '+/- determinant = product of the pivo
  17347.     FOR pvt% = lo TO up
  17348.        det! = det! * A(rpvt(pvt%), cpvt(pvt%))
  17349.     NEXT pvt%                           'count contains the total number of ro
  17350.     det! = (-1) ^ count * det!          'and column switches due to pivoting.
  17351.     IF errcode% THEN ERROR errcode%     'multiply the determinant by -1 for
  17352.  END IF                                 'each switch
  17353.  sdetexit:
  17354.  ERASE rpvt, cpvt
  17355.  MatDetS% = errcode%
  17356.  EXIT FUNCTION
  17357.  sdeterr:
  17358.     errcode% = (ERR + 5) MOD 200 - 5
  17359.     RESUME sdetexit
  17360.  END FUNCTION
  17361.  
  17362.  '========================MatInvC%===================================
  17363.  'MatInvC% uses the matluD% and matbsD procedures to invert a square, currency
  17364.  'type matrix.  Let e(N) contain all zeroes except for the jth position, which
  17365.  'is 1.  Then the jth column of A^-1 is x, where Ax=e.
  17366.  '
  17367.  'Parameters: A(n x n) matrix
  17368.  '
  17369.  'Returns: A^-1
  17370.  '===================================================================
  17371.  FUNCTION MatInvC% (A() AS CURRENCY)
  17372.  ON LOCAL ERROR GOTO cinverr: errcode% = 0
  17373.  lo = LBOUND(A, 1)
  17374.  up = UBOUND(A, 1)
  17375.  REDIM rpvt(lo TO up) AS INTEGER, cpvt(lo TO up) AS INTEGER
  17376.  'duplicate A() in a double precision work matrix, Tmp()
  17377.  DIM Tmp(lo TO up, LBOUND(A, 2) TO UBOUND(A, 2)) AS DOUBLE
  17378.  DIM e(lo TO up) AS DOUBLE, x(lo TO up) AS DOUBLE
  17379.  FOR row% = lo TO up
  17380.     FOR col% = LBOUND(A, 2) TO UBOUND(A, 2)
  17381.        Tmp(row%, col%) = CDBL(A(row%, col%))
  17382.     NEXT col%
  17383.  NEXT row%
  17384.  errcode% = matluD%(Tmp())                    'Put LU in Tmp
  17385.  IF NOT continue THEN ERROR errcode%
  17386.  FOR col% = lo TO up                          'Find A^-1 one column at a time
  17387.     e(col%) = 1#
  17388.     bserrcode% = matbsD%(Tmp(), e(), x())
  17389.     IF bserrcode% THEN ERROR bserrcode%
  17390.     FOR row% = lo TO up
  17391.        A(row%, col%) = CCUR(x(row%))          'Put the column into A
  17392.        e(row%) = 0#
  17393.     NEXT row%
  17394.  NEXT col%
  17395.  IF errcode% THEN ERROR errcode%
  17396.  cinvexit:
  17397.  ERASE Tmp, e, x, rpvt, cpvt
  17398.  MatInvC% = errcode%
  17399.  EXIT FUNCTION
  17400.  cinverr:
  17401.     errcode% = (ERR + 5) MOD 200 - 5
  17402.     RESUME cinvexit
  17403.  END FUNCTION
  17404.  
  17405.  '========================MatInvD%===================================
  17406.  'MatInvD% uses the matluD% and matbsD procedures to invert a square, double
  17407.  'precision matrix.  Let e(N) contain all zeroes except for the jth position,
  17408.  'which is 1.  Then the jth column of A^-1 is x, where Ax=e.
  17409.  '
  17410.  'Parameters: A(n x n) matrix
  17411.  '
  17412.  'Returns: A^-1
  17413.  '===================================================================
  17414.  FUNCTION MatInvD% (A() AS DOUBLE)
  17415.  ON LOCAL ERROR GOTO dinverr: errcode% = 0
  17416.  lo = LBOUND(A, 1)
  17417.  up = UBOUND(A, 1)
  17418.  DIM Ain(lo TO up, lo TO up) AS DOUBLE
  17419.  DIM e(lo TO up) AS DOUBLE, x(lo TO up) AS DOUBLE
  17420.  REDIM rpvt(lo TO up) AS INTEGER, cpvt(lo TO up) AS INTEGER
  17421.  errcode% = matluD%(A())                     'Get LU matrix
  17422.  IF NOT continue THEN ERROR errcode%
  17423.  FOR col% = lo TO up                         'Find A^-1 one column at a time
  17424.     e(col%) = 1#
  17425.     bserrcode% = matbsD%(A(), e(), x())
  17426.     IF bserrcode% THEN ERROR bserrcode%
  17427.     FOR row% = lo TO up
  17428.        Ain(row%, col%) = x(row%)
  17429.        e(row%) = 0#
  17430.     NEXT row%
  17431.  NEXT col%
  17432.  FOR col% = lo TO up                         'Put A^-1 in A
  17433.     FOR row% = lo TO up
  17434.        A(row%, col%) = Ain(row%, col%)
  17435.     NEXT row%
  17436.  NEXT col%
  17437.  IF errcode% THEN ERROR errcode%
  17438.  dinvexit:
  17439.  ERASE e, x, Ain, rpvt, cpvt
  17440.  MatInvD% = errcode%
  17441.  EXIT FUNCTION
  17442.  dinverr:
  17443.     errcode% = (ERR + 5) MOD 200 - 5
  17444.     RESUME dinvexit
  17445.  END FUNCTION
  17446.  
  17447.  '========================MatInvS%===================================
  17448.  'MatInvS% uses the matluS% and matbsS procedures to invert a square, single
  17449.  'precision matrix.  Let e(N) contain all zeroes except for the jth position,
  17450.  'which is 1. Then the jth column of A^-1 is x, where Ax=e.
  17451.  '
  17452.  'Parameters: A(n x n) matrix
  17453.  '
  17454.  'Returns: A^-1
  17455.  '===================================================================
  17456.  FUNCTION MatInvS% (A() AS SINGLE)
  17457.  ON LOCAL ERROR GOTO sinverr: errcode% = 0
  17458.  lo = LBOUND(A, 1)
  17459.  up = UBOUND(A, 1)
  17460.  DIM Ain(lo TO up, lo TO up) AS SINGLE
  17461.  DIM e(lo TO up) AS SINGLE, x(lo TO up) AS SINGLE
  17462.  REDIM rpvt(lo TO up) AS INTEGER, cpvt(lo TO up) AS INTEGER
  17463.  errcode% = matluS%(A())                     'Get LU matrix
  17464.  IF NOT continue THEN ERROR errcode%
  17465.  FOR col% = lo TO up                         'find A^-1 one column at a time
  17466.     e(col%) = 1!
  17467.     bserrcode% = matbsS%(A(), e(), x())
  17468.     IF bserrcode% THEN ERROR bserrcode%
  17469.     FOR row% = lo TO up
  17470.        Ain(row%, col%) = x(row%)
  17471.        e(row%) = 0!
  17472.     NEXT row%
  17473.  NEXT col%
  17474.  FOR col% = lo TO up                         'put A^-1 in A
  17475.     FOR row% = lo TO up
  17476.        A(row%, col%) = Ain(row%, col%)
  17477.     NEXT row%
  17478.  NEXT col%
  17479.  IF errcode% THEN ERROR errcode%
  17480.  sinvexit:
  17481.  ERASE e, x, Ain, rpvt, cpvt
  17482.  MatInvS% = errcode%
  17483.  EXIT FUNCTION
  17484.  sinverr:
  17485.     errcode% = (ERR + 5) MOD 200 - 5
  17486.     RESUME sinvexit
  17487.  END FUNCTION
  17488.  
  17489.  '========================matluD%====================================
  17490.  'matluD% does Gaussian elimination with total pivoting to put a square, doubl
  17491.  'precision matrix in LU form. The multipliers used in the row operations to
  17492.  'create zeroes below the main diagonal are saved in the zero spaces.
  17493.  '
  17494.  'Parameters: A(n x n) matrix, rpvt(n) and cpvt(n) permutation vectors
  17495.  '            used to index the row and column pivots
  17496.  '
  17497.  'Returns: A in LU form with corresponding pivot vectors; the total number of
  17498.  '         pivots in count, which is used to find the sign of the determinant.
  17499.  '===================================================================
  17500.  FUNCTION matluD% (A() AS DOUBLE)
  17501.  ON LOCAL ERROR GOTO dluerr: errcode% = 0
  17502.  'Checks if A is square, returns error code if not
  17503.  IF NOT (lo = LBOUND(A, 2) AND up = UBOUND(A, 2)) THEN ERROR 198
  17504.  DIM rownorm(lo TO up) AS DOUBLE
  17505.  count = 0                            'initialize count, continue
  17506.  continue = -1
  17507.  FOR row% = lo TO up                  'initialize rpvt and cpvt
  17508.     rpvt(row%) = row%
  17509.     cpvt(row%) = row%
  17510.     rownorm(row%) = 0#                'find the row norms of A()
  17511.     FOR col% = lo TO up
  17512.        rownorm(row%) = rownorm(row%) + ABS(A(row%, col%))
  17513.     NEXT col%
  17514.     IF rownorm(row%) = 0# THEN        'if any rownorm is zero, the matrix
  17515.        continue = 0                   'is singular, set error, exit and
  17516.        ERROR 199                      'do not continue
  17517.     END IF
  17518.  NEXT row%
  17519.  FOR pvt% = lo TO (up - 1)
  17520.  'Find best available pivot
  17521.     max# = 0#                         'checks all values in rows and columns n
  17522.     FOR row% = pvt% TO up             'already used for pivoting and saves the
  17523.        r% = rpvt(row%)                'largest absolute number and its positio
  17524.        FOR col% = pvt% TO up
  17525.           c% = cpvt(col%)
  17526.           temp# = ABS(A(r%, c%)) / rownorm(r%)
  17527.           IF temp# > max# THEN
  17528.              max# = temp#
  17529.              bestrow% = row%          'save the position of new max#
  17530.              bestcol% = col%
  17531.           END IF
  17532.        NEXT col%
  17533.     NEXT row%
  17534.     IF max# = 0# THEN                 'if no nonzero number is found, A is
  17535.        continue = 0                   'singular, send back error, do not conti
  17536.        ERROR 199
  17537.     ELSEIF pvt% > 1 THEN              'check if drop in pivots is too much
  17538.        IF max# < (deps# * oldmax#) THEN errcode% = 199
  17539.     END IF
  17540.     oldmax# = max#
  17541.     IF rpvt(pvt%) <> rpvt(bestrow%) THEN
  17542.        count = count + 1                    'if a row or column pivot is
  17543.        SWAP rpvt(pvt%), rpvt(bestrow%)      'necessary, count it and permute
  17544.     END IF                                  'rpvt or cpvt. Note: the rows and
  17545.     IF cpvt(pvt%) <> cpvt(bestcol%) THEN    'columns are not actually switched
  17546.        count = count + 1                    'only the order in which they are
  17547.        SWAP cpvt(pvt%), cpvt(bestcol%)      'used.
  17548.     END IF
  17549.  'Eliminate all values below the pivot
  17550.     rp% = rpvt(pvt%)
  17551.     cp% = cpvt(pvt%)
  17552.     FOR row% = (pvt% + 1) TO up
  17553.        r% = rpvt(row%)
  17554.        A(r%, cp%) = -A(r%, cp%) / A(rp%, cp%)  'save multipliers
  17555.        FOR col% = (pvt% + 1) TO up
  17556.           c% = cpvt(col%)                      'complete row operations
  17557.           A(r%, c%) = A(r%, c%) + A(r%, cp%) * A(rp%, c%)
  17558.        NEXT col%
  17559.     NEXT row%
  17560.  NEXT pvt%
  17561.  IF A(rpvt(up), cpvt(up)) = 0# THEN
  17562.     continue = 0                      'if last pivot is zero or pivot drop is
  17563.     ERROR 199                         'too large, A is singular, send back err
  17564.  ELSEIF (ABS(A(rpvt(up), cpvt(up))) / rownorm(rpvt(up))) < (deps# * oldmax#) T
  17565.     errcode% = 199                    'if pivot is not identically zero then
  17566.  END IF                               'continue remains TRUE
  17567.  IF errcode% THEN ERROR errcode%
  17568.  dluexit:
  17569.  matluD% = errcode%
  17570.  EXIT FUNCTION
  17571.  dluerr:
  17572.     IF errcode% < 199 THEN continue = 0
  17573.     errcode% = ERR
  17574.     RESUME dluexit
  17575.  END FUNCTION
  17576.  
  17577.  '========================matluS%====================================
  17578.  'matluS% does Gaussian elimination with total pivoting to put a square, singl
  17579.  'precision matrix in LU form. The multipliers used in the row operations to
  17580.  'create zeroes below the main diagonal are saved in the zero spaces.
  17581.  '
  17582.  'Parameters: A(n x n) matrix, rpvt(n) and cpvt(n) permutation vectors
  17583.  '            used to index the row and column pivots
  17584.  '
  17585.  'Returns: A in LU form with corresponding pivot vectors; the total number of
  17586.  '         pivots in count, which is used to find the sign of the determinant.
  17587.  '===================================================================
  17588.  FUNCTION matluS% (A() AS SINGLE)
  17589.  ON LOCAL ERROR GOTO sluerr: errcode% = 0
  17590.  'Checks if A is square, returns error code if not
  17591.  IF NOT (lo = LBOUND(A, 2) AND up = UBOUND(A, 2)) THEN ERROR 198
  17592.  DIM rownorm(lo TO up) AS SINGLE
  17593.  count = 0                            'initialize count, continue
  17594.  continue = -1
  17595.  FOR row% = lo TO up                  'initialize rpvt and cpvt
  17596.     rpvt(row%) = row%
  17597.     cpvt(row%) = row%
  17598.     rownorm(row%) = 0!                'find the row norms of A()
  17599.     FOR col% = lo TO up
  17600.        rownorm(row%) = rownorm(row%) + ABS(A(row%, col%))
  17601.     NEXT col%
  17602.     IF rownorm(row%) = 0! THEN        'if any rownorm is zero, the matrix
  17603.        continue = 0                   'is singular, set error, exit and do
  17604.        ERROR 199                      'not continue
  17605.     END IF
  17606.  NEXT row%
  17607.  FOR pvt% = lo TO (up - 1)
  17608.  'Find best available pivot
  17609.     max! = 0!                         'checks all values in rows and columns n
  17610.     FOR row% = pvt% TO up             'already used for pivoting and finds the
  17611.        r% = rpvt(row%)                'number largest in absolute value relati
  17612.        FOR col% = pvt% TO up          'to its row norm
  17613.           c% = cpvt(col%)
  17614.           temp! = ABS(A(r%, c%)) / rownorm(r%)
  17615.           IF temp! > max! THEN
  17616.              max! = temp!
  17617.              bestrow% = row%          'save the position of new max!
  17618.              bestcol% = col%
  17619.           END IF
  17620.        NEXT col%
  17621.     NEXT row%
  17622.     IF max! = 0! THEN                 'if no nonzero number is found, A is
  17623.        continue = 0                   'singular, send back error, do not conti
  17624.        ERROR 199
  17625.     ELSEIF pvt% > 1 THEN              'check if drop in pivots is too much
  17626.        IF max! < (seps! * oldmax!) THEN errcode% = 199
  17627.     END IF
  17628.     oldmax! = max!
  17629.     IF rpvt(pvt%) <> rpvt(bestrow%) THEN
  17630.        count = count + 1                    'if a row or column pivot is
  17631.        SWAP rpvt(pvt%), rpvt(bestrow%)      'necessary, count it and permute
  17632.     END IF                                  'rpvt or cpvt. Note: the rows and
  17633.     IF cpvt(pvt%) <> cpvt(bestcol%) THEN    'columns are not actually switched
  17634.        count = count + 1                    'only the order in which they are
  17635.        SWAP cpvt(pvt%), cpvt(bestcol%)      'used.
  17636.     END IF
  17637.  'Eliminate all values below the pivot
  17638.     rp% = rpvt(pvt%)
  17639.     cp% = cpvt(pvt%)
  17640.     FOR row% = (pvt% + 1) TO up
  17641.        r% = rpvt(row%)
  17642.        A(r%, cp%) = -A(r%, cp%) / A(rp%, cp%)  'save multipliers
  17643.        FOR col% = (pvt% + 1) TO up
  17644.           c% = cpvt(col%)                      'complete row operations
  17645.           A(r%, c%) = A(r%, c%) + A(r%, cp%) * A(rp%, c%)
  17646.        NEXT col%
  17647.     NEXT row%
  17648.  NEXT pvt%
  17649.  IF A(rpvt(up), cpvt(up)) = 0! THEN
  17650.     continue = 0                      'if last pivot is zero or pivot drop is
  17651.     ERROR 199                         'too large, A is singular, send back err
  17652.  ELSEIF (ABS(A(rpvt(up), cpvt(up))) / rownorm(rpvt(up))) < (seps! * oldmax!) T
  17653.     errcode% = 199                    'if pivot is not identically zero then
  17654.  END IF                               'continue remains TRUE
  17655.  IF errcode% THEN ERROR errcode%
  17656.  sluexit:
  17657.  matluS% = errcode%
  17658.  EXIT FUNCTION
  17659.  sluerr:
  17660.     errcode% = ERR
  17661.     IF errcode% < 199 THEN continue = 0
  17662.     RESUME sluexit
  17663.  END FUNCTION
  17664.  
  17665.  '=======================MatMultC%===================================
  17666.  'MatMultC% multiplies two currency type matrices and places the
  17667.  'product in a result matrix
  17668.  '
  17669.  'Parameters: matrices Alpha,Beta,Gamma
  17670.  '
  17671.  'Returns: Gamma() = Alpha() * Beta()
  17672.  '===================================================================
  17673.  FUNCTION MatMultC% (Alpha() AS CURRENCY, Beta() AS CURRENCY, Gamma() AS CURRE
  17674.  ON LOCAL ERROR GOTO cmulterr: MatMultC% = 0
  17675.  IF (LBOUND(Alpha, 2) <> LBOUND(Beta, 1)) OR (UBOUND(Alpha, 2) <> UBOUND(Beta,
  17676.     ERROR 197                   'check inside dimensions
  17677.  ELSEIF (LBOUND(Alpha, 1) <> LBOUND(Gamma, 1)) OR (UBOUND(Alpha, 1) <> UBOUND(
  17678.     ERROR 195                   'check dimensions of result matrix
  17679.  END IF
  17680.  'loop through, Gamma(row,col)=inner product of Alpha(row,*) and Beta(*,col)
  17681.  FOR row% = LBOUND(Gamma, 1) TO UBOUND(Gamma, 1)
  17682.     FOR col% = LBOUND(Gamma, 2) TO UBOUND(Gamma, 2)
  17683.        Gamma(row%, col%) = 0@
  17684.        FOR inside% = LBOUND(Alpha, 2) TO UBOUND(Alpha, 2)
  17685.           Gamma(row%, col%) = Gamma(row%, col%) + Alpha(row%, inside%) * Beta(
  17686.        NEXT inside%
  17687.     NEXT col%
  17688.  NEXT row%
  17689.  cmultexit:
  17690.  EXIT FUNCTION
  17691.  cmulterr:
  17692.     MatMultC% = (ERR + 5) MOD 200 - 5
  17693.     RESUME cmultexit
  17694.  END FUNCTION
  17695.  
  17696.  '=======================MatMultD%===================================
  17697.  'MatMultD% multiplies two double precision matrices and places the
  17698.  'product in a result matrix
  17699.  '
  17700.  'Parameters: matrices Alpha,Beta,Gamma
  17701.  '
  17702.  'Returns: Gamma() = Alpha() * Beta()
  17703.  '===================================================================
  17704.  FUNCTION MatMultD% (Alpha() AS DOUBLE, Beta() AS DOUBLE, Gamma() AS DOUBLE)
  17705.  ON LOCAL ERROR GOTO dmulterr: MatMultD% = 0
  17706.  IF (LBOUND(Alpha, 2) <> LBOUND(Beta, 1)) OR (UBOUND(Alpha, 2) <> UBOUND(Beta,
  17707.     ERROR 197                   'check inside dimensions
  17708.  ELSEIF (LBOUND(Alpha, 1) <> LBOUND(Gamma, 1)) OR (UBOUND(Alpha, 1) <> UBOUND(
  17709.     ERROR 195                   'check dimensions of result matrix
  17710.  END IF
  17711.  'loop through, Gamma(row,col)=inner product of Alpha(row,*) and Beta(*,col)
  17712.  FOR row% = LBOUND(Gamma, 1) TO UBOUND(Gamma, 1)
  17713.     FOR col% = LBOUND(Gamma, 2) TO UBOUND(Gamma, 2)
  17714.        Gamma(row%, col%) = 0#
  17715.        FOR inside% = LBOUND(Alpha, 2) TO UBOUND(Alpha, 2)
  17716.           Gamma(row%, col%) = Gamma(row%, col%) + Alpha(row%, inside%) * Beta(
  17717.        NEXT inside%
  17718.     NEXT col%
  17719.  NEXT row%
  17720.  dmultexit:
  17721.  EXIT FUNCTION
  17722.  dmulterr:
  17723.     MatMultD% = (ERR + 5) MOD 200 - 5
  17724.     RESUME dmultexit
  17725.  END FUNCTION
  17726.  
  17727.  '=======================MatMultI%===================================
  17728.  'MatMultI% multiplies two integer matrices and places the product in
  17729.  'a result matrix
  17730.  '
  17731.  'Parameters: matrices Alpha,Beta,Gamma
  17732.  '
  17733.  'Returns: Gamma() = Alpha() * Beta()
  17734.  '===================================================================
  17735.  FUNCTION MatMultI% (Alpha() AS INTEGER, Beta() AS INTEGER, Gamma() AS INTEGER
  17736.  ON LOCAL ERROR GOTO imulterr: MatMultI% = 0
  17737.  IF (LBOUND(Alpha, 2) <> LBOUND(Beta, 1)) OR (UBOUND(Alpha, 2) <> UBOUND(Beta,
  17738.     ERROR 197                   'check inside dimensions
  17739.  ELSEIF (LBOUND(Alpha, 1) <> LBOUND(Gamma, 1)) OR (UBOUND(Alpha, 1) <> UBOUND(
  17740.     ERROR 195                   'check dimensions of result matrix
  17741.  END IF
  17742.  'loop through, Gamma(row,col)=inner product of Alpha(row,*) and Beta(*,col)
  17743.  FOR row% = LBOUND(Gamma, 1) TO UBOUND(Gamma, 1)
  17744.     FOR col% = LBOUND(Gamma, 2) TO UBOUND(Gamma, 2)
  17745.        Gamma(row%, col%) = 0
  17746.        FOR inside% = LBOUND(Alpha, 2) TO UBOUND(Alpha, 2)
  17747.           Gamma(row%, col%) = Gamma(row%, col%) + Alpha(row%, inside%) * Beta(
  17748.        NEXT inside%
  17749.     NEXT col%
  17750.  NEXT row%
  17751.  imultexit:
  17752.  EXIT FUNCTION
  17753.  imulterr:
  17754.     MatMultI% = (ERR + 5) MOD 200 - 5
  17755.     RESUME imultexit
  17756.  END FUNCTION
  17757.  
  17758.  '=======================MatMultL%===================================
  17759.  'MatMultL% multiplies two long integer matrices and places the product
  17760.  'in a result matrix
  17761.  '
  17762.  'Parameters: matrices Alpha,Beta,Gamma
  17763.  '
  17764.  'Returns: Gamma() = Alpha() * Beta()
  17765.  '===================================================================
  17766.  FUNCTION MatMultL% (Alpha() AS LONG, Beta() AS LONG, Gamma() AS LONG)
  17767.  ON LOCAL ERROR GOTO lmulterr: MatMultL% = 0
  17768.  IF (LBOUND(Alpha, 2) <> LBOUND(Beta, 1)) OR (UBOUND(Alpha, 2) <> UBOUND(Beta,
  17769.     ERROR 197                   'check inside dimensions
  17770.  ELSEIF (LBOUND(Alpha, 1) <> LBOUND(Gamma, 1)) OR (UBOUND(Alpha, 1) <> UBOUND(
  17771.     ERROR 195                   'check dimensions of result matrix
  17772.  END IF
  17773.  'loop through, Gamma(row,col)=inner product of Alpha(row,*) and Beta(*,col)
  17774.  FOR row% = LBOUND(Gamma, 1) TO UBOUND(Gamma, 1)
  17775.     FOR col% = LBOUND(Gamma, 2) TO UBOUND(Gamma, 2)
  17776.        Gamma(row%, col%) = 0&
  17777.        FOR inside% = LBOUND(Alpha, 2) TO UBOUND(Alpha, 2)
  17778.           Gamma(row%, col%) = Gamma(row%, col%) + Alpha(row%, inside%) * Beta(
  17779.        NEXT inside%
  17780.     NEXT col%
  17781.  NEXT row%
  17782.  lmultexit:
  17783.  EXIT FUNCTION
  17784.  lmulterr:
  17785.     MatMultL% = (ERR + 5) MOD 200 - 5
  17786.     RESUME lmultexit
  17787.  END FUNCTION
  17788.  
  17789.  '=======================MatMultS%===================================
  17790.  'MatMultS% multiplies two single precision matrices and places the
  17791.  'product in a result matrix
  17792.  '
  17793.  'Parameters: matrices Alpha,Beta,Gamma
  17794.  '
  17795.  'Returns: Gamma() = Alpha() * Beta()
  17796.  '===================================================================
  17797.  FUNCTION MatMultS% (Alpha() AS SINGLE, Beta() AS SINGLE, Gamma() AS SINGLE)
  17798.  ON LOCAL ERROR GOTO smulterr: MatMultS% = 0
  17799.  IF (LBOUND(Alpha, 2) <> LBOUND(Beta, 1)) OR (UBOUND(Alpha, 2) <> UBOUND(Beta,
  17800.     ERROR 197                   'check inside dimensions
  17801.  ELSEIF (LBOUND(Alpha, 1) <> LBOUND(Gamma, 1)) OR (UBOUND(Alpha, 1) <> UBOUND(
  17802.     ERROR 195                   'check dimensions of result matrix
  17803.  END IF
  17804.  'loop through, Gamma(row,col)=inner product of Alpha(row,*) and Beta(*,col)
  17805.  FOR row% = LBOUND(Gamma, 1) TO UBOUND(Gamma, 1)
  17806.     FOR col% = LBOUND(Gamma, 2) TO UBOUND(Gamma, 2)
  17807.        Gamma(row%, col%) = 0!
  17808.        FOR inside% = LBOUND(Alpha, 2) TO UBOUND(Alpha, 2)
  17809.           Gamma(row%, col%) = Gamma(row%, col%) + Alpha(row%, inside%) * Beta(
  17810.        NEXT inside%
  17811.     NEXT col%
  17812.  NEXT row%
  17813.  smultexit:
  17814.  EXIT FUNCTION
  17815.  smulterr:
  17816.     MatMultS% = (ERR + 5) MOD 200 - 5
  17817.     RESUME smultexit
  17818.  END FUNCTION
  17819.  
  17820.  '========================MatSEqnC%==================================
  17821.  'MatSEqnC% solves a system of n linear equations, Ax=b, and puts the
  17822.  'answer in b. A is first put in LU form by matluC%, then matbsC is called
  17823.  'to solve the system.  matrices A,b are currency type.
  17824.  '
  17825.  'Parameters: A(n x n) contains coefficient matrix, b(N) contains the right si
  17826.  '
  17827.  'Returns: A in LU form, solution in b
  17828.  '===================================================================
  17829.  FUNCTION MatSEqnC% (A() AS CURRENCY, b() AS CURRENCY)
  17830.  ON LOCAL ERROR GOTO cseqnerr: errcode% = 0
  17831.  lo = LBOUND(A, 1)
  17832.  up = UBOUND(A, 1)
  17833.  REDIM rpvt(lo TO up) AS INTEGER, cpvt(lo TO up) AS INTEGER
  17834.  'duplicate A(), b() in temporary double precision matrices Tmp(), btmp()
  17835.  DIM Tmp(lo TO up, LBOUND(A, 2) TO UBOUND(A, 2)) AS DOUBLE
  17836.  DIM x(lo TO up) AS DOUBLE, btmp(lo TO up) AS DOUBLE
  17837.  FOR row% = lo TO up
  17838.     FOR col% = LBOUND(A, 2) TO UBOUND(A, 2)
  17839.        Tmp(row%, col%) = CDBL(A(row%, col%))
  17840.     NEXT col%
  17841.  NEXT row%
  17842.  errcode% = matluD%(Tmp())                   'Get LU matrix
  17843.  IF NOT continue THEN ERROR errcode%
  17844.  'check dimensions of b, make double precision copy if ok.
  17845.  IF (lo <> LBOUND(b)) OR (up <> UBOUND(b)) THEN ERROR 197
  17846.  FOR row% = lo TO up
  17847.     btmp(row%) = CDBL(b(row%))
  17848.  NEXT row%
  17849.  bserrcode% = matbsD%(Tmp(), btmp(), x())    'Backsolve system
  17850.  IF bserrcode% THEN ERROR bserrcode%
  17851.  FOR row% = lo TO up
  17852.     b(row%) = CCUR(x(row%))                  'Put solution in b for return
  17853.  NEXT row%
  17854.  IF errcode% THEN ERROR errcode%
  17855.  cseqnexit:
  17856.  ERASE Tmp, btmp, x, rpvt, cpvt
  17857.  MatSEqnC% = errcode%
  17858.  EXIT FUNCTION
  17859.  cseqnerr:
  17860.     errcode% = (ERR + 5) MOD 200 - 5
  17861.     RESUME cseqnexit
  17862.  END FUNCTION
  17863.  
  17864.  '========================MatSEqnD%==================================
  17865.  'MatSEqnD% solves a system of n linear equations, Ax=b, and puts the
  17866.  'answer in b. A is first put in LU form by matluD%, then matbsD is called
  17867.  'to solve the system.  matrices A,b are double precision.
  17868.  '
  17869.  'Parameters: A(n x n) contains coefficient matrix, b(N) contains the right si
  17870.  '
  17871.  'Returns: A in LU form, solution in b
  17872.  '===================================================================
  17873.  FUNCTION MatSEqnD% (A() AS DOUBLE, b() AS DOUBLE)
  17874.  ON LOCAL ERROR GOTO dseqnerr: errcode% = 0
  17875.  lo = LBOUND(A, 1)
  17876.  up = UBOUND(A, 1)
  17877.  DIM x(lo TO up) AS DOUBLE
  17878.  REDIM rpvt(lo TO up) AS INTEGER, cpvt(lo TO up) AS INTEGER
  17879.  errcode% = matluD%(A())                      'Get LU matrix
  17880.  IF NOT continue THEN ERROR errcode%
  17881.  'check dimensions of b
  17882.  IF (lo <> LBOUND(b)) OR (up <> UBOUND(b)) THEN ERROR 197
  17883.  bserrcode% = matbsD%(A(), b(), x())          'Backsolve system
  17884.  IF bserrcode% THEN ERROR bserrcode%
  17885.  FOR row% = lo TO up
  17886.     b(row%) = x(row%)                         'Put solution in b for return
  17887.  NEXT row%
  17888.  IF errcode% THEN ERROR errcode%
  17889.  dseqnexit:
  17890.  ERASE x, rpvt, cpvt
  17891.  MatSEqnD% = errcode%
  17892.  EXIT FUNCTION
  17893.  dseqnerr:
  17894.     errcode% = (ERR + 5) MOD 200 - 5
  17895.     RESUME dseqnexit
  17896.  END FUNCTION
  17897.  
  17898.  '========================MatSEqnS%==================================
  17899.  'MatSEqnS% solves a system of n linear equations, Ax=b, and puts the
  17900.  'answer in b. A is first put in LU form by matluS%, then matbsS is called
  17901.  'to solve the system.  matrices A,b are single precision.
  17902.  '
  17903.  'Parameters: A(n x n) contains coefficient matrix, b(N) contains the right si
  17904.  '
  17905.  'Returns: A in LU form, solution in b
  17906.  '===================================================================
  17907.  FUNCTION MatSEqnS% (A() AS SINGLE, b() AS SINGLE)
  17908.  ON LOCAL ERROR GOTO sseqnerr: errcode% = 0
  17909.  lo = LBOUND(A, 1)
  17910.  up = UBOUND(A, 1)
  17911.  DIM x(lo TO up) AS SINGLE
  17912.  REDIM rpvt(lo TO up) AS INTEGER, cpvt(lo TO up) AS INTEGER
  17913.  errcode% = matluS%(A())                      'Get LU matrix
  17914.  IF NOT continue THEN ERROR errcode%
  17915.  'check dimensions of b
  17916.  IF (lo <> LBOUND(b)) OR (up <> UBOUND(b)) THEN ERROR 197
  17917.  bserrcode% = matbsS%(A(), b(), x())          'Backsolve system
  17918.  IF bserrcode% THEN ERROR bserrcode%
  17919.  FOR row% = lo TO up
  17920.     b(row%) = x(row%)                         'Put solution in b for return
  17921.  NEXT row%
  17922.  IF errcode% THEN ERROR errcode%
  17923.  sseqnexit:
  17924.  ERASE x, rpvt, cpvt
  17925.  MatSEqnS% = errcode%
  17926.  EXIT FUNCTION
  17927.  sseqnerr:
  17928.     errcode% = (ERR + 5) MOD 200 - 5
  17929.     RESUME sseqnexit
  17930.  END FUNCTION
  17931.  
  17932.  '=======================MatSubC%====================================
  17933.  'MatSubC% takes the difference of two currency type matrices and
  17934.  'places the result in the first.
  17935.  '
  17936.  'Params: matrices Alpha,Beta
  17937.  '
  17938.  'Returns: Alpha=Alpha-Beta
  17939.  '===================================================================
  17940.  FUNCTION MatSubC% (Alpha() AS CURRENCY, Beta() AS CURRENCY)
  17941.  ON LOCAL ERROR GOTO csuberr: MatSubC% = 0
  17942.  'check if Alpha, Beta have same dimensions if not, exit and send back error
  17943.  IF (LBOUND(Alpha, 1) <> LBOUND(Beta, 1)) OR (UBOUND(Alpha, 1) <> UBOUND(Beta,
  17944.  'loop through and subtract elements
  17945.  FOR row% = LBOUND(Alpha, 1) TO UBOUND(Alpha, 1)
  17946.     FOR col% = LBOUND(Alpha, 2) TO UBOUND(Alpha, 2)
  17947.        Alpha(row%, col%) = Alpha(row%, col%) - Beta(row%, col%)
  17948.     NEXT col%
  17949.  NEXT row%
  17950.  csubexit:
  17951.  EXIT FUNCTION
  17952.  csuberr:
  17953.     MatSubC% = (ERR + 5) MOD 200 - 5
  17954.     RESUME csubexit:
  17955.  END FUNCTION
  17956.  
  17957.  '=======================MatSubD%====================================
  17958.  'MatSubD% takes the difference of two double precision matrices and
  17959.  'places the result in the first.
  17960.  '
  17961.  'Parameters: matrices Alpha,Beta
  17962.  '
  17963.  'Returns: Alpha() = Alpha() - Beta()
  17964.  '===================================================================
  17965.  FUNCTION MatSubD% (Alpha() AS DOUBLE, Beta() AS DOUBLE)
  17966.  ON LOCAL ERROR GOTO dsuberr: MatSubD% = 0
  17967.  'check if Alpha, Beta have same dimensions if not, exit and send back error
  17968.  IF (LBOUND(Alpha, 1) <> LBOUND(Beta, 1)) OR (UBOUND(Alpha, 1) <> UBOUND(Beta,
  17969.  'loop through and subtract elements
  17970.  FOR row% = LBOUND(Alpha, 1) TO UBOUND(Alpha, 1)
  17971.     FOR col% = LBOUND(Alpha, 2) TO UBOUND(Alpha, 2)
  17972.        Alpha(row%, col%) = Alpha(row%, col%) - Beta(row%, col%)
  17973.     NEXT col%
  17974.  NEXT row%
  17975.  dsubexit:
  17976.  EXIT FUNCTION
  17977.  dsuberr:
  17978.     MatSubD% = (ERR + 5) MOD 200 - 5
  17979.     RESUME dsubexit:
  17980.  END FUNCTION
  17981.  
  17982.  '=======================MatSubI%====================================
  17983.  'MatSubI% takes the difference of two integer matrices and places the
  17984.  'result in the first.
  17985.  '
  17986.  'Parameters: matrices Alpha,Beta
  17987.  '
  17988.  'Returns: Alpha() = Alpha() - Beta()
  17989.  '===================================================================
  17990.  FUNCTION MatSubI% (Alpha() AS INTEGER, Beta() AS INTEGER)
  17991.  ON LOCAL ERROR GOTO isuberr: MatSubI% = 0
  17992.  'check if Alpha, Beta have same dimensions if not, exit and send back error
  17993.  IF (LBOUND(Alpha, 1) <> LBOUND(Beta, 1)) OR (UBOUND(Alpha, 1) <> UBOUND(Beta,
  17994.  'loop through and subtract elements
  17995.  FOR row% = LBOUND(Alpha, 1) TO UBOUND(Alpha, 1)
  17996.     FOR col% = LBOUND(Alpha, 2) TO UBOUND(Alpha, 2)
  17997.        Alpha(row%, col%) = Alpha(row%, col%) - Beta(row%, col%)
  17998.     NEXT col%
  17999.  NEXT row%
  18000.  isubexit:
  18001.  EXIT FUNCTION
  18002.  isuberr:
  18003.     MatSubI% = (ERR + 5) MOD 200 - 5
  18004.     RESUME isubexit:
  18005.  END FUNCTION
  18006.  
  18007.  '=======================MatSubL%====================================
  18008.  'MatSubL% takes the difference of two long integer matrices and places
  18009.  'the result in the first.
  18010.  '
  18011.  'Parameters: matrices Alpha,Beta
  18012.  '
  18013.  'Returns: Alpha() = Alpha() - Beta()
  18014.  '===================================================================
  18015.  FUNCTION MatSubL% (Alpha() AS LONG, Beta() AS LONG)
  18016.  ON LOCAL ERROR GOTO lsuberr: MatSubL% = 0
  18017.  'check if Alpha, Beta have same dimensions if not, exit and send back error
  18018.  IF (LBOUND(Alpha, 1) <> LBOUND(Beta, 1)) OR (UBOUND(Alpha, 1) <> UBOUND(Beta,
  18019.  'loop through and subtract elements
  18020.  FOR row% = LBOUND(Alpha, 1) TO UBOUND(Alpha, 1)
  18021.     FOR col% = LBOUND(Alpha, 2) TO UBOUND(Alpha, 2)
  18022.        Alpha(row%, col%) = Alpha(row%, col%) - Beta(row%, col%)
  18023.     NEXT col%
  18024.  NEXT row%
  18025.  lsubexit:
  18026.  EXIT FUNCTION
  18027.  lsuberr:
  18028.     MatSubL% = (ERR + 5) MOD 200 - 5
  18029.     RESUME lsubexit:
  18030.  END FUNCTION
  18031.  
  18032.  '=======================MatSubS%====================================
  18033.  'MatSubS% takes the difference of two single precision matrices and
  18034.  'places the result in the first.
  18035.  '
  18036.  'Parameters: matrices Alpha,Beta
  18037.  '
  18038.  'Returns: Alpha() = Alpha() - Beta()
  18039.  '===================================================================
  18040.  FUNCTION MatSubS% (Alpha() AS SINGLE, Beta() AS SINGLE)
  18041.  ON LOCAL ERROR GOTO ssuberr: MatSubS% = 0
  18042.  'check if Alpha, Beta have same dimensions if not, exit and send back error
  18043.  IF (LBOUND(Alpha, 1) <> LBOUND(Beta, 1)) OR (UBOUND(Alpha, 1) <> UBOUND(Beta,
  18044.  'loop through and subtract elements
  18045.  FOR row% = LBOUND(Alpha, 1) TO UBOUND(Alpha, 1)
  18046.     FOR col% = LBOUND(Alpha, 2) TO UBOUND(Alpha, 2)
  18047.        Alpha(row%, col%) = Alpha(row%, col%) - Beta(row%, col%)
  18048.     NEXT col%
  18049.  NEXT row%
  18050.  ssubexit:
  18051.  EXIT FUNCTION
  18052.  ssuberr:
  18053.     MatSubS% = (ERR + 5) MOD 200 - 5
  18054.     RESUME ssubexit:
  18055.  END FUNCTION
  18056.  
  18057.  
  18058.  
  18059.  MENU.BAS
  18060.  CD-ROM Disc Path:   \SAMPCODE\BASIC\MENU.BAS
  18061.  
  18062.  '============================================================================
  18063.  '
  18064.  '     MENU.BAS - Pull-down Menu Routines for the User Interface Toolbox in
  18065.  '           Microsoft BASIC 7.0, Professional Development System
  18066.  '              Copyright (C) 1987-1989, Microsoft Corporation
  18067.  '
  18068.  '  NOTE:    This sample source code toolbox is intended to demonstrate some
  18069.  '           of the extended capabilities of Microsoft BASIC 7.0 Professional
  18070.  '           Development system that can help to leverage the professional
  18071.  '           developer's time more effectively.  While you are free to use,
  18072.  '           modify, or distribute the routines in this module in any way you
  18073.  '           find useful, it should be noted that these are examples only and
  18074.  '           should not be relied upon as a fully-tested "add-on" library.
  18075.  '
  18076.  '  PURPOSE: These are the routines which provide support for the pull-down
  18077.  '           menus in the user interface toolbox.
  18078.  '
  18079.  '  For information on creating a library and QuickLib from the routines
  18080.  '  contained in this file, read the comment header of GENERAL.BAS.
  18081.  '
  18082.  '============================================================================
  18083.  
  18084.  DEFINT A-Z
  18085.  
  18086.  '$INCLUDE: 'general.bi'
  18087.  '$INCLUDE: 'mouse.bi'
  18088.  '$INCLUDE: 'menu.bi'
  18089.  
  18090.  COMMON SHARED /uitools/ GloMenu    AS MenuMiscType
  18091.  COMMON SHARED /uitools/ GloTitle() AS MenuTitleType
  18092.  COMMON SHARED /uitools/ GloItem()  AS MenuItemType
  18093.  
  18094.  FUNCTION MenuCheck (action%) STATIC
  18095.  
  18096.      SELECT CASE action
  18097.  
  18098.      '=======================================================================
  18099.      ' This simulates "polling" for a menu event.  If a menu event occured,
  18100.      ' GloMenu.currMenu and .currItem are set.  When MenuCheck(0) is
  18101.      ' called, these values are transfered to .lastMenu and .lastItem.
  18102.      ' MenuCheck(0) then returns the menu number, or 0 (FALSE) if none
  18103.      ' selected as of last call
  18104.      '=======================================================================
  18105.  
  18106.          CASE 0
  18107.              GloMenu.lastMenu = GloMenu.currMenu
  18108.              GloMenu.lastItem = GloMenu.currItem
  18109.              GloMenu.currMenu = 0
  18110.              GloMenu.currItem = 0
  18111.              MenuCheck = GloMenu.lastMenu
  18112.  
  18113.          '===================================================================
  18114.          ' Returns the menu item last selected.  Functions only after a call
  18115.          ' to MenuCheck(0)
  18116.          '===================================================================
  18117.  
  18118.          CASE 1
  18119.              MenuCheck = GloMenu.lastItem
  18120.  
  18121.          '===================================================================
  18122.          ' Checks GloMenu.currMenu and .currItem.  If both are not 0, this
  18123.          ' returns TRUE meaning a menu has been selected since MenuCheck(0)
  18124.          ' was last called.  This does not change any values, it simply
  18125.          ' reports on the current state.
  18126.          '===================================================================
  18127.  
  18128.          CASE 2
  18129.              IF GloMenu.currMenu = 0 OR GloMenu.currItem = 0 THEN
  18130.                  MenuCheck = FALSE
  18131.              ELSE
  18132.                  MenuCheck = TRUE
  18133.              END IF
  18134.          CASE ELSE
  18135.              MenuCheck = 0
  18136.      END SELECT
  18137.  
  18138.  END FUNCTION
  18139.  
  18140.  SUB MenuColor (fore, back, highlight, disabled, cursorFore, cursorBack, curso
  18141.  
  18142.      GloMenu.fore = fore
  18143.      GloMenu.back = back
  18144.      GloMenu.highlight = highlight
  18145.      GloMenu.disabled = disabled
  18146.      GloMenu.cursorFore = cursorFore
  18147.      GloMenu.cursorBack = cursorBack
  18148.      GloMenu.cursorHi = cursorHi
  18149.  
  18150.  END SUB
  18151.  
  18152.  SUB MenuDo STATIC
  18153.  
  18154.      '=======================================================================
  18155.      ' If menu event trapping turned off, return immediately
  18156.      '=======================================================================
  18157.  
  18158.      IF NOT GloMenu.MenuOn THEN
  18159.          EXIT SUB
  18160.      END IF
  18161.  
  18162.      '=======================================================================
  18163.      ' Initialize MenuDo's variables, and then enter the main loop
  18164.      '=======================================================================
  18165.  
  18166.      GOSUB MenuDoInit
  18167.  
  18168.      WHILE NOT MenuDoDone
  18169.  
  18170.          '===================================================================
  18171.          ' If in MouseMode then
  18172.          '   if button is pressed, check where mouse is and react acccordingly
  18173.          '   if button not pressed, switch to keyboard mode.
  18174.          '===================================================================
  18175.          IF mouseMode THEN
  18176.              MousePoll mouseRow, mouseCol, lButton, rButton
  18177.              IF lButton THEN
  18178.                  IF mouseRow = 1 THEN
  18179.                      GOSUB MenuDoGetMouseMenu
  18180.                  ELSE
  18181.                      GOSUB MenuDoGetMouseItem
  18182.                  END IF
  18183.              ELSE
  18184.                  mouseMode = FALSE
  18185.                  GOSUB MenuDoMouseRelease
  18186.                  IF NOT pulldown THEN
  18187.                      GOSUB MenuDoShowTitleAccessKeys
  18188.                  END IF
  18189.              END IF
  18190.          ELSE
  18191.  
  18192.              '===============================================================
  18193.              ' If in keyboard mode, show the cursor, wait for key, hide cursor
  18194.              ' Perform the desired action based on what key was pressed.
  18195.              '===============================================================
  18196.  
  18197.              GOSUB MenuDoShowCursor
  18198.              GOSUB MenuDoGetKey
  18199.              GOSUB MenuDoHideCursor
  18200.  
  18201.              SELECT CASE kbd$
  18202.                  CASE "enter":       GOSUB MenuDoEnter
  18203.                  CASE "up":          GOSUB MenuDoUp
  18204.                  CASE "down":        GOSUB menuDoDown
  18205.                  CASE "left":        GOSUB MenuDoLeft
  18206.                  CASE "right":       GOSUB MenuDoRight
  18207.                  CASE "escape":      GOSUB MenuDoEscape
  18208.                  CASE "altReleased": GOSUB MenuDoAltReleased
  18209.                  CASE "mouse":       GOSUB MenuDoMousePress
  18210.                  CASE ELSE:          GOSUB MenuDoAccessKey
  18211.              END SELECT
  18212.          END IF
  18213.      WEND
  18214.      GOSUB MenuDoHideTitleAccessKeys
  18215.      EXIT SUB
  18216.  
  18217.  '===========================================================================
  18218.  ' Initialize variables for proper MenuDo execution.
  18219.  '===========================================================================
  18220.  
  18221.  MenuDoInit:
  18222.      REDIM buffer$(MAXMENU), copyFlag(MAXMENU)             'Stores screen back
  18223.  
  18224.      FOR a = 1 TO MAXMENU
  18225.          buffer$(a) = ""                         '1 buffer per menu
  18226.          copyFlag(a) = FALSE                     'FALSE means not copied yet
  18227.      NEXT a
  18228.  
  18229.      pulldown = FALSE                            'FALSE means no menu is shown
  18230.      MenuDoDone = FALSE                          'FALSE means keep going in lo
  18231.  
  18232.      altWasReleased = FALSE                      'Set to TRUE if ALT is presse
  18233.                                                  'and then released
  18234.  
  18235.      altWasPressedAgain = FALSE                  'Set to TRUE is ALT is presse
  18236.                                                  'and then released, and then
  18237.                                                  'pressed again.
  18238.  
  18239.      '=======================================================================
  18240.      ' If mouse installed and button is pressed, then set MouseMode to TRUE
  18241.      ' Else, set MouseMode to FALSE
  18242.      '=======================================================================
  18243.  
  18244.      MousePoll mouseRow, mouseCol, lButton, rButton
  18245.      IF lButton THEN
  18246.          mouseMode = TRUE
  18247.          currMenu = 0
  18248.          currItem = 0
  18249.      ELSE
  18250.          mouseMode = FALSE
  18251.          currMenu = 1
  18252.          currItem = 0
  18253.          GOSUB MenuDoShowTitleAccessKeys
  18254.      END IF
  18255.  
  18256.  RETURN
  18257.  
  18258.  '===========================================================================
  18259.  ' This shows the cursor at the location CurrMenu,CurrItem.
  18260.  '===========================================================================
  18261.  
  18262.  MenuDoShowCursor:
  18263.  
  18264.      MouseHide
  18265.      IF currMenu <> 0 AND RTRIM$(GloItem(currMenu, currItem).text) <> "-" THEN
  18266.          IF currItem = 0 THEN
  18267.              COLOR GloMenu.cursorFore, GloMenu.cursorBack
  18268.              LOCATE 1, GloTitle(currMenu).lColTitle
  18269.              PRINT " "; RTRIM$(GloTitle(currMenu).text); " ";
  18270.              IF NOT mouseMode THEN
  18271.                 COLOR GloMenu.cursorHi, GloMenu.cursorBack
  18272.                 LOCATE 1, GloTitle(currMenu).lColTitle + GloTitle(currMenu).ac
  18273.                 PRINT MID$(GloTitle(currMenu).text, GloTitle(currMenu).accessK
  18274.              END IF
  18275.          ELSE
  18276.              IF GloItem(currMenu, currItem).state = 2 THEN
  18277.                  chk$ = CHR$(175)
  18278.              ELSE
  18279.                  chk$ = " "
  18280.              END IF
  18281.  
  18282.              COLOR GloMenu.cursorFore, GloMenu.cursorBack
  18283.              LOCATE GloItem(currMenu, currItem).row, GloTitle(currMenu).lColIt
  18284.              PRINT chk$; LEFT$(GloItem(currMenu, currItem).text, GloTitle(curr
  18285.  
  18286.              IF GloItem(currMenu, currItem).state > 0 THEN
  18287.                  COLOR GloMenu.cursorHi, GloMenu.cursorBack
  18288.                  LOCATE GloItem(currMenu, currItem).row, col + GloItem(currMen
  18289.                  PRINT MID$(GloItem(currMenu, currItem).text, GloItem(currMenu
  18290.              END IF
  18291.  
  18292.          END IF
  18293.      END IF
  18294.      MouseShow
  18295.  
  18296.  RETURN
  18297.  
  18298.  '===========================================================================
  18299.  ' This hides the cursor at the location CurrMenu,CurrItem.
  18300.  '===========================================================================
  18301.  
  18302.  MenuDoHideCursor:
  18303.  
  18304.      MouseHide
  18305.      IF currMenu <> 0 AND RTRIM$(GloItem(currMenu, currItem).text) <> "-" THEN
  18306.          IF currItem = 0 THEN
  18307.              SELECT CASE GloTitle(currMenu).state
  18308.                  CASE 0: COLOR GloMenu.disabled, GloMenu.back
  18309.                  CASE 1, 2: COLOR GloMenu.fore, GloMenu.back
  18310.                  CASE ELSE
  18311.              END SELECT
  18312.              LOCATE 1, GloTitle(currMenu).lColTitle
  18313.              PRINT " "; RTRIM$(GloTitle(currMenu).text); " ";
  18314.  
  18315.              IF GloTitle(currMenu).state > 0 THEN
  18316.                  COLOR GloMenu.highlight, GloMenu.back
  18317.                  LOCATE 1, GloTitle(currMenu).lColTitle + GloTitle(currMenu).a
  18318.                  PRINT MID$(GloTitle(currMenu).text, GloTitle(currMenu).access
  18319.              END IF
  18320.          ELSE
  18321.              IF GloItem(currMenu, currItem).state = 2 THEN
  18322.                  chk$ = CHR$(175)
  18323.              ELSE
  18324.                  chk$ = " "
  18325.              END IF
  18326.              SELECT CASE GloItem(currMenu, currItem).state
  18327.                  CASE 0: COLOR GloMenu.disabled, GloMenu.back
  18328.                  CASE 1, 2: COLOR GloMenu.fore, GloMenu.back
  18329.                  CASE ELSE
  18330.              END SELECT
  18331.              LOCATE GloItem(currMenu, currItem).row, GloTitle(currMenu).lColIt
  18332.              PRINT chk$; LEFT$(GloItem(currMenu, currItem).text, GloTitle(curr
  18333.  
  18334.              IF GloItem(currMenu, currItem).state > 0 THEN
  18335.                  COLOR GloMenu.highlight, GloMenu.back
  18336.                  LOCATE GloItem(currMenu, currItem).row, col + GloItem(currMen
  18337.                  PRINT MID$(GloItem(currMenu, currItem).text, GloItem(currMenu
  18338.              END IF
  18339.  
  18340.          END IF
  18341.      END IF
  18342.      MouseShow
  18343.  RETURN
  18344.  
  18345.  '===========================================================================
  18346.  ' Handles state where mouse is at row #1.
  18347.  '===========================================================================
  18348.  
  18349.  MenuDoGetMouseMenu:
  18350.  
  18351.      '=======================================================================
  18352.      ' Computes the menu number based on mouse column location.  Uses info
  18353.      ' calculated in MenuShow()
  18354.      '=======================================================================
  18355.  
  18356.      newMenu = CVI(MID$(GloMenu.menuIndex, mouseCol * 2 - 1, 2))
  18357.  
  18358.      IF GloTitle(newMenu).state <> 1 THEN
  18359.          newMenu = 0
  18360.      END IF
  18361.  
  18362.      '=======================================================================
  18363.      ' If new menu<>current menu, hide current menu, show new menu, assign new
  18364.      ' menu to current menu
  18365.      '=======================================================================
  18366.  
  18367.      IF newMenu <> currMenu THEN
  18368.          GOSUB MenuDoHidePullDown
  18369.          currMenu = newMenu
  18370.          currItem = 0
  18371.          GOSUB menuDoShowPullDown
  18372.      END IF
  18373.  
  18374.  RETURN
  18375.  
  18376.  '===========================================================================
  18377.  ' Handles state where mouse is not in row #1.  If a menu is down, it picks
  18378.  ' the proper menu item based on which row the mouse is located
  18379.  '===========================================================================
  18380.  
  18381.  MenuDoGetMouseItem:
  18382.  
  18383.      '=======================================================================
  18384.      ' If pulldown, and mouse column is within the menu area, then compute new
  18385.      ' item  based on computations done in MenuShow.  If not in box, then new
  18386.      ' item = 0
  18387.      '=======================================================================
  18388.  
  18389.      IF pulldown THEN
  18390.          IF mouseCol >= GloTitle(currMenu).lColItem AND mouseCol <= GloTitle(c
  18391.              newItem = GloItem(currMenu, mouseRow - 2).index
  18392.          ELSE
  18393.              newItem = 0
  18394.          END IF
  18395.  
  18396.          ' ===================================================================
  18397.          ' If current item <> new item, hide old cursor, show new cursor,
  18398.          ' assign new item to current item.
  18399.          ' ===================================================================
  18400.  
  18401.          IF currItem <> newItem THEN
  18402.              IF currItem <> 0 THEN
  18403.                  GOSUB MenuDoHideCursor
  18404.              END IF
  18405.              currItem = newItem
  18406.              GOSUB MenuDoShowCursor
  18407.          END IF
  18408.      END IF
  18409.  RETURN
  18410.  
  18411.  ' ===========================================================================
  18412.  ' Handles state when MenuDo is in mouse mode, and mouse button is released.
  18413.  ' ===========================================================================
  18414.  
  18415.  MenuDoMouseRelease:
  18416.      menuMode = FALSE
  18417.  
  18418.      ' =======================================================================
  18419.      ' If no menu selected, then exit MenuDo returning 0s for menu and item
  18420.      ' =======================================================================
  18421.  
  18422.      IF currMenu = 0 THEN
  18423.          GloMenu.currMenu = 0
  18424.          GloMenu.currItem = 0
  18425.          MenuDoDone = TRUE
  18426.      ELSE
  18427.  
  18428.          ' ===================================================================
  18429.          ' If menu is down, but no item is selected then
  18430.          '    if mouse is on the top row, simply gosub the MenuDoDown routine
  18431.          '    else hide menu then exit MenuDo returning 0's for menu and item
  18432.          ' ===================================================================
  18433.  
  18434.          IF currItem = 0 THEN
  18435.              IF mouseRow = 1 THEN
  18436.                  GOSUB menuDoDown
  18437.              ELSE
  18438.                  GOSUB MenuDoHidePullDown
  18439.                  GloMenu.currMenu = 0
  18440.                  GloMenu.currItem = 0
  18441.                  MenuDoDone = TRUE
  18442.              END IF
  18443.          ELSE
  18444.  
  18445.              ' ===============================================================
  18446.              ' If current (menu,item)'s state is disabled, then just beep
  18447.              ' ===============================================================
  18448.  
  18449.              IF GloItem(currMenu, currItem).state = 0 THEN
  18450.                  BEEP
  18451.  
  18452.              ' ===============================================================
  18453.              ' If current (menu,item)'s state is a line
  18454.              ' then exit MenuDo returning 0s for menu and item
  18455.              ' ===============================================================
  18456.  
  18457.              ELSEIF RTRIM$(GloItem(currMenu, currItem).text) = "-" THEN
  18458.                  GOSUB MenuDoHidePullDown
  18459.                  GloMenu.currMenu = 0
  18460.                  GloMenu.currItem = 0
  18461.                  MenuDoDone = TRUE
  18462.              ELSE
  18463.  
  18464.                  ' ===========================================================
  18465.                  ' Otherwise, selection must be valid, exit MenuDo, returning
  18466.                  ' proper menu,item pair in the proper global variables
  18467.                  ' ===========================================================
  18468.                  GOSUB MenuDoHidePullDown
  18469.                  GloMenu.currMenu = currMenu
  18470.                  GloMenu.currItem = currItem
  18471.                  MenuDoDone = TRUE
  18472.              END IF
  18473.          END IF
  18474.      END IF
  18475.  RETURN
  18476.  
  18477.  ' ==========================================================================
  18478.  ' This routine shows the menu bar's access keys
  18479.  ' ==========================================================================
  18480.  
  18481.  MenuDoShowTitleAccessKeys:
  18482.      MouseHide
  18483.      COLOR GloMenu.highlight, GloMenu.back
  18484.      FOR menu = 1 TO MAXMENU
  18485.          IF GloTitle(menu).state = 1 THEN
  18486.              LOCATE 1, GloTitle(menu).lColTitle + GloTitle(menu).accessKey
  18487.              PRINT MID$(GloTitle(menu).text, GloTitle(menu).accessKey, 1);
  18488.          END IF
  18489.      NEXT menu
  18490.      MouseShow
  18491.  RETURN
  18492.  
  18493.  
  18494.  ' ===========================================================================
  18495.  ' This routine hides the menu bar's access keys
  18496.  ' ===========================================================================
  18497.  
  18498.  MenuDoHideTitleAccessKeys:
  18499.      MouseHide
  18500.      COLOR GloMenu.fore, GloMenu.back
  18501.      FOR menu = 1 TO MAXMENU
  18502.          IF GloTitle(menu).state = 1 THEN
  18503.              LOCATE 1, GloTitle(menu).lColTitle + GloTitle(menu).accessKey
  18504.              PRINT MID$(GloTitle(menu).text, GloTitle(menu).accessKey, 1);
  18505.          END IF
  18506.      NEXT menu
  18507.      MouseShow
  18508.  RETURN
  18509.  
  18510.  ' ===========================================================================
  18511.  ' Waits for key press, then returns the key press.  It also returns several
  18512.  ' tokens such as "menu", or "altReleased" in special cases.  Read on...
  18513.  ' ===========================================================================
  18514.  
  18515.  MenuDoGetKey:
  18516.      DO
  18517.          kbd$ = INKEY$
  18518.  
  18519.          ' ===================================================================
  18520.          ' If ALT key pressed, then if it was a access key (Alt+A..) reduce
  18521.          '  the Alt+A to A.
  18522.          '  Also set the altPressed flags to reflect the current state of the
  18523.          '  ALT key.
  18524.          ' ===================================================================
  18525.  
  18526.          IF GetShiftState(3) THEN
  18527.              IF kbd$ = "" THEN
  18528.                  IF altWasReleased THEN
  18529.                      altWasPressedAgain = TRUE
  18530.                  END IF
  18531.              ELSE
  18532.                  altWasPressedAgain = FALSE
  18533.                  kbd$ = AltToASCII(kbd$)
  18534.              END IF
  18535.              altWasReleased = FALSE
  18536.          ELSE
  18537.  
  18538.              ' ===============================================================
  18539.              ' If ALT key is released (initially), then pressed, then released
  18540.              ' again with no other action in between, then return the
  18541.              ' token "altReleased"
  18542.              ' ===============================================================
  18543.  
  18544.              IF altWasPressedAgain THEN
  18545.                  kbd$ = "altReleased"
  18546.                  altWasPressedAgain = FALSE
  18547.              ELSE
  18548.  
  18549.                  ' ===========================================================
  18550.                  ' Based on the key that was pressed, return the proper token
  18551.                  ' ===========================================================
  18552.  
  18553.                  altWasReleased = TRUE
  18554.  
  18555.                  SELECT CASE kbd$
  18556.                      CASE CHR$(27) + "": kbd$ = "escape"
  18557.                      CASE CHR$(32) + "": kbd$ = ""
  18558.                      CASE CHR$(13) + "": kbd$ = "enter"
  18559.                      CASE CHR$(0) + "H": kbd$ = "up"
  18560.                      CASE CHR$(0) + "P": kbd$ = "down"
  18561.                      CASE CHR$(0) + "K": kbd$ = "left"
  18562.                      CASE CHR$(0) + "M": kbd$ = "right"
  18563.                      CASE ELSE
  18564.                          IF LEN(kbd$) = 1 THEN
  18565.                              kbd$ = UCASE$(kbd$)
  18566.                          END IF
  18567.                  END SELECT
  18568.              END IF
  18569.          END IF
  18570.  
  18571.          ' ===================================================================
  18572.          ' If mouse button is pressed, it overrides all key actions, and
  18573.          ' the token "mouse" is returned
  18574.          ' ===================================================================
  18575.  
  18576.          MousePoll mouseRow, mouseCol, lButton, rButton
  18577.          IF lButton THEN
  18578.              kbd$ = "mouse"
  18579.          END IF
  18580.  
  18581.      LOOP UNTIL kbd$ <> ""
  18582.  
  18583.  RETURN
  18584.  
  18585.  
  18586.  ' ===========================================================================
  18587.  ' Handles the state where the up arrow is pressed.  It searches for the
  18588.  ' first non empty, non "-" (dashed) item.
  18589.  ' ===========================================================================
  18590.  
  18591.  MenuDoUp:
  18592.      IF currItem <> 0 THEN
  18593.          DO
  18594.              currItem = (currItem + MAXITEM - 2) MOD MAXITEM + 1
  18595.          LOOP UNTIL GloItem(currMenu, currItem).state >= 0 AND RTRIM$(GloItem(
  18596.      END IF
  18597.  RETURN
  18598.  
  18599.  
  18600.  ' ===========================================================================
  18601.  ' Handles 2 different states:
  18602.  '
  18603.  '  State 1: Menu is open, and the down arrow is pressed.
  18604.  '
  18605.  '  State 2: Any time a new menu is opened, and the top item
  18606.  '      is to be the current item.  Specifically:
  18607.  '          - When no menu is opened, and the down arrow is pressed
  18608.  '          - When the mouse is released over the menu title
  18609.  '          - When a menu is opened, and the user hits right/left arrow
  18610.  '          - When enter is pressed while cursor is on title bar
  18611.  '          - When a access key is used on the title bar.
  18612.  ' ===========================================================================
  18613.  
  18614.  menuDoDown:
  18615.      DO
  18616.          IF currItem = 0 THEN
  18617.              GOSUB MenuDoHideTitleAccessKeys
  18618.              GOSUB menuDoShowPullDown
  18619.              currItem = (currItem) MOD MAXITEM + 1
  18620.          ELSEIF currItem > 0 THEN
  18621.              currItem = (currItem) MOD MAXITEM + 1
  18622.          END IF
  18623.  
  18624.      LOOP UNTIL GloItem(currMenu, currItem).state >= 0 AND RTRIM$(GloItem(curr
  18625.  RETURN
  18626.  
  18627.  
  18628.  ' ===========================================================================
  18629.  ' Handles state when the left arrow is pressed.  If a menu is down, it
  18630.  ' hides it.  It then finds the first valid menu to the left.  If the menu
  18631.  ' was initially down, then the new menu is pulled down as well
  18632.  ' ===========================================================================
  18633.  
  18634.  MenuDoLeft:
  18635.      IF pulldown THEN
  18636.          GOSUB MenuDoHidePullDown
  18637.          pulldown = TRUE
  18638.      END IF
  18639.  
  18640.      DO
  18641.          currMenu = (currMenu + MAXMENU - 2) MOD MAXMENU + 1
  18642.      LOOP UNTIL GloTitle(currMenu).state = 1
  18643.  
  18644.      IF pulldown THEN
  18645.          currItem = 0
  18646.          GOSUB menuDoDown
  18647.      END IF
  18648.  RETURN
  18649.  
  18650.  
  18651.  ' ===========================================================================
  18652.  ' Handles state when the right arrow is pressed.  If a menu is down, it
  18653.  ' hides it.  It then finds the first valid menu to the right.  If the menu
  18654.  ' was initially down, then the new menu is pulled down as well
  18655.  ' ===========================================================================
  18656.  
  18657.  MenuDoRight:
  18658.      IF pulldown THEN
  18659.          GOSUB MenuDoHidePullDown
  18660.          pulldown = TRUE
  18661.      END IF
  18662.  
  18663.      DO
  18664.          currMenu = (currMenu) MOD MAXMENU + 1
  18665.      LOOP UNTIL GloTitle(currMenu).state = 1
  18666.  
  18667.      IF pulldown THEN
  18668.          currItem = 0
  18669.          GOSUB menuDoDown
  18670.      END IF
  18671.  RETURN
  18672.  
  18673.  
  18674.  ' ===========================================================================
  18675.  ' Handles state when the ESC key is pressed.  First hides the menu, and
  18676.  ' then exits menuDo, returning 0's in the proper global variables
  18677.  ' ===========================================================================
  18678.  
  18679.  MenuDoEscape:
  18680.      GOSUB MenuDoHidePullDown
  18681.      GloMenu.currMenu = 0
  18682.      GloMenu.currItem = 0
  18683.      MenuDoDone = TRUE
  18684.  RETURN
  18685.  
  18686.  ' ===========================================================================
  18687.  ' Handles state when Enter is pressed.  If on a valid item, return the
  18688.  ' proper (menu,item) pair and exit.  Else beep.  If on a valid menu
  18689.  ' this will open the menu by calling MenuDoDown
  18690.  ' ===========================================================================
  18691.  
  18692.  MenuDoEnter:
  18693.      IF currItem = 0 THEN
  18694.          IF GloTitle(currMenu).state = 0 THEN
  18695.              BEEP
  18696.          ELSE
  18697.              GOSUB menuDoDown
  18698.          END IF
  18699.      ELSE
  18700.          IF GloItem(currMenu, currItem).state <= 0 THEN
  18701.              BEEP
  18702.          ELSE
  18703.              GOSUB MenuDoHidePullDown
  18704.              GloMenu.currMenu = currMenu
  18705.              GloMenu.currItem = currItem
  18706.              MenuDoDone = TRUE
  18707.          END IF
  18708.      END IF
  18709.  RETURN
  18710.  
  18711.  
  18712.  ' ===========================================================================
  18713.  ' If ALT pressed and released with nothing else happening in between, it
  18714.  ' will exit if no menu is open, or close the menu if one is open.
  18715.  ' ===========================================================================
  18716.  
  18717.  MenuDoAltReleased:
  18718.      IF pulldown THEN
  18719.          GOSUB MenuDoHidePullDown
  18720.          currItem = 0
  18721.          GOSUB MenuDoShowTitleAccessKeys
  18722.      ELSE
  18723.          GloMenu.currMenu = 0
  18724.          GloMenu.currItem = 0
  18725.          MenuDoDone = TRUE
  18726.      END IF
  18727.  RETURN
  18728.  
  18729.  
  18730.  ' ===========================================================================
  18731.  ' If mouse is pressed while in keyboard mode, this routine assigns
  18732.  ' TRUE to MouseMode, resets the item, and hides the access keys
  18733.  ' ===========================================================================
  18734.  
  18735.  MenuDoMousePress:
  18736.      mouseMode = TRUE
  18737.      currItem = 0
  18738.      IF NOT pulldown THEN
  18739.          GOSUB MenuDoHideTitleAccessKeys
  18740.      END IF
  18741.  RETURN
  18742.  
  18743.  
  18744.  ' ===========================================================================
  18745.  ' If a access key is pressed
  18746.  ' ===========================================================================
  18747.  
  18748.  MenuDoAccessKey:
  18749.  
  18750.      ' =======================================================================
  18751.      ' If an access key is pressed
  18752.      '   If no menu selected, search titles for matching access key, and open
  18753.      '      than menu.
  18754.      ' =======================================================================
  18755.  
  18756.      IF currItem = 0 THEN
  18757.          newMenu = (currMenu + MAXMENU - 2) MOD MAXMENU + 1
  18758.          loopEnd = (currMenu + MAXMENU - 2) MOD MAXMENU + 1
  18759.          DO
  18760.              newMenu = (newMenu) MOD MAXMENU + 1
  18761.          LOOP UNTIL (UCASE$(MID$(GloTitle(newMenu).text, GloTitle(newMenu).acc
  18762.  
  18763.          IF kbd$ = UCASE$(MID$(GloTitle(newMenu).text, GloTitle(newMenu).acces
  18764.              currMenu = newMenu
  18765.              GOSUB menuDoDown
  18766.          END IF
  18767.      ELSE
  18768.  
  18769.          ' ===================================================================
  18770.          ' If menu is selected, search items for matching access key, and
  18771.          ' select that (menu,item) and exit MenuDo if item is enabled
  18772.          ' ===================================================================
  18773.  
  18774.          newItem = (currItem + MAXITEM - 2) MOD MAXITEM + 1
  18775.          loopEnd = (currItem + MAXITEM - 2) MOD MAXITEM + 1
  18776.          DO
  18777.              newItem = (newItem) MOD MAXITEM + 1
  18778.          LOOP UNTIL (UCASE$(MID$(GloItem(currMenu, newItem).text, GloItem(curr
  18779.  
  18780.  
  18781.          IF kbd$ = UCASE$(MID$(GloItem(currMenu, newItem).text, GloItem(currMe
  18782.              currItem = newItem
  18783.  
  18784.              IF GloItem(currMenu, currItem).state <= 0 THEN
  18785.                  BEEP
  18786.              ELSE
  18787.                  GOSUB MenuDoHidePullDown
  18788.                  GloMenu.currMenu = currMenu
  18789.                  GloMenu.currItem = currItem
  18790.                  MenuDoDone = TRUE
  18791.              END IF
  18792.          END IF
  18793.      END IF
  18794.  RETURN
  18795.  
  18796.  ' ===========================================================================
  18797.  ' Draws the menu -- only if menu is enabled.
  18798.  ' ===========================================================================
  18799.  
  18800.  menuDoShowPullDown:
  18801.      IF currMenu <> 0 AND GloTitle(currMenu).state = 1 THEN
  18802.  
  18803.          ' ===================================================================
  18804.          ' Copies the background if this is the first time this particular
  18805.          ' menu is being drawn
  18806.          ' ===================================================================
  18807.  
  18808.          MouseHide
  18809.          IF NOT copyFlag(currMenu) THEN
  18810.              IF GloTitle(currMenu).rColItem - GloTitle(currMenu).lColItem < LE
  18811.                  GloTitle(currMenu).rColItem = GloTitle(currMenu).lColItem + L
  18812.              END IF
  18813.  
  18814.              GetBackground 1, GloTitle(currMenu).lColItem, GloTitle(currMenu).
  18815.              copyFlag(currMenu) = TRUE
  18816.          END IF
  18817.  
  18818.          ' ===================================================================
  18819.          ' Draw the menu, this is pretty straight forward
  18820.          ' ===================================================================
  18821.          pulldown = TRUE
  18822.          length = GloTitle(currMenu).itemLength
  18823.          IF length = 0 THEN length = 6
  18824.          lowestRow = 3
  18825.          col = GloTitle(currMenu).lColItem
  18826.  
  18827.          COLOR GloMenu.cursorFore, GloMenu.cursorBack
  18828.          LOCATE 1, GloTitle(currMenu).lColTitle
  18829.          PRINT " "; RTRIM$(GloTitle(currMenu).text); " ";
  18830.  
  18831.          COLOR GloMenu.fore, GloMenu.back
  18832.          LOCATE 2, col
  18833.          PRINT "┌"; STRING$(length + 2, "─"); "┐"
  18834.  
  18835.          FOR item = 1 TO MAXITEM
  18836.              IF GloItem(currMenu, item).state >= 0 THEN
  18837.                  IF GloItem(currMenu, item).state = 2 THEN
  18838.                      chk$ = CHR$(175)
  18839.                  ELSE
  18840.                      chk$ = " "
  18841.                  END IF
  18842.  
  18843.                  LOCATE GloItem(currMenu, item).row, col
  18844.                  COLOR GloMenu.fore, GloMenu.back
  18845.  
  18846.                  IF RTRIM$(GloItem(currMenu, item).text) = "-" THEN
  18847.                      PRINT "├"; STRING$(length + 2, "─"); "┤"
  18848.                  ELSE
  18849.                      PRINT "│"; chk$;
  18850.                      IF GloItem(currMenu, item).state > 0 THEN
  18851.                          COLOR GloMenu.fore, GloMenu.back
  18852.                      ELSE
  18853.                          COLOR GloMenu.disabled, GloMenu.back
  18854.                      END IF
  18855.                      PRINT LEFT$(GloItem(currMenu, item).text + SPACE$(20), le
  18856.                      COLOR GloMenu.fore, GloMenu.back
  18857.                      PRINT " │";
  18858.  
  18859.                      IF GloItem(currMenu, item).state > 0 THEN
  18860.                          COLOR GloMenu.highlight, GloMenu.back
  18861.                          LOCATE GloItem(currMenu, item).row, col + GloItem(cur
  18862.                          PRINT MID$(GloItem(currMenu, item).text, GloItem(curr
  18863.                      END IF
  18864.                  END IF
  18865.                  lowestRow = GloItem(currMenu, item).row + 1
  18866.              END IF
  18867.          NEXT item
  18868.  
  18869.          COLOR GloMenu.fore, GloMenu.back
  18870.          LOCATE lowestRow, col
  18871.          PRINT "└"; STRING$(length + 2, "─"); "┘";
  18872.  
  18873.          rCol = col + length + 5
  18874.  
  18875.          AttrBox 3, rCol - 1, lowestRow, rCol, 8
  18876.          AttrBox lowestRow + 1, col + 2, lowestRow + 1, rCol, 8
  18877.      END IF
  18878.  
  18879.      MouseShow
  18880.  
  18881.  RETURN
  18882.  
  18883.  ' ===========================================================================
  18884.  ' Replace the background over the menu
  18885.  ' ===========================================================================
  18886.  
  18887.  MenuDoHidePullDown:
  18888.      IF pulldown THEN
  18889.          MouseHide
  18890.  
  18891.          PutBackground 1, GloTitle(currMenu).lColItem, buffer$(currMenu)
  18892.  
  18893.          MouseShow
  18894.          pulldown = FALSE
  18895.      END IF
  18896.  RETURN
  18897.  
  18898.  END SUB
  18899.  
  18900.  SUB MenuEvent
  18901.  
  18902.      ' =======================================================================
  18903.      ' If ALT key is pressed, let MenuDo take over.  NOTE:  This will
  18904.      ' not call MenuDo if the ALT key has not been released at least
  18905.      ' once since the last time MenuDo was called.  This prevents the menu
  18906.      ' from flashing if the user simply holds down the ALT key.
  18907.      ' =======================================================================
  18908.  
  18909.      IF GetShiftState(3) THEN
  18910.          IF GloMenu.altKeyReset THEN
  18911.              MenuDo
  18912.              GloMenu.altKeyReset = FALSE
  18913.          END IF
  18914.      ELSE
  18915.          GloMenu.altKeyReset = TRUE
  18916.      END IF
  18917.  
  18918.      ' =======================================================================
  18919.      ' Call MenuDo if the mouse button is down, and the cursor is on the top r
  18920.      ' =======================================================================
  18921.  
  18922.      MousePoll mouseRow, mouseCol, lButton, rButton
  18923.      IF mouseRow = 1 AND lButton THEN
  18924.          MenuDo
  18925.      END IF
  18926.  
  18927.  END SUB
  18928.  
  18929.  SUB MenuInit
  18930.  
  18931.      ' =======================================================================
  18932.      '  Initialize global menu arrays
  18933.      ' =======================================================================
  18934.  
  18935.      FOR menu = 1 TO MAXMENU
  18936.          GloTitle(menu).text = ""
  18937.          GloTitle(menu).state = -1            'state of -1 means "empty"
  18938.          GloTitle(menu).rColItem = 0           'These get set in MenuShow
  18939.          GloTitle(menu).lColItem = 0           ' |
  18940.          GloTitle(menu).rColTitle = 0          ' |
  18941.          GloTitle(menu).lColTitle = 0          ' |
  18942.          GloTitle(menu).itemLength = 0         ' |
  18943.          GloTitle(menu).accessKey = 1            'Initial AccessKey of 1
  18944.  
  18945.          FOR item = 1 TO MAXITEM
  18946.              GloItem(menu, item).text = ""
  18947.              GloItem(menu, item).state = -1      'state of -1 means "empty"
  18948.              GloItem(menu, item).index = 0       'These get set in MenuShow
  18949.              GloItem(menu, item).row = 0         '  |
  18950.              GloItem(menu, item).accessKey = 1   'Initial AccessKey of 1
  18951.          NEXT item
  18952.      NEXT menu
  18953.  
  18954.      ' =======================================================================
  18955.      ' Initialize mouse
  18956.      ' =======================================================================
  18957.  
  18958.      MouseInit
  18959.  
  18960.      ' =======================================================================
  18961.      ' Set initial state of ALT key to "reset"
  18962.      ' Clear out shortcut key index
  18963.      ' Set initial state of menu to ON
  18964.      ' =======================================================================
  18965.  
  18966.      GloMenu.altKeyReset = TRUE
  18967.      GloMenu.shortcutKeyIndex = STRING$(100, 0)
  18968.      GloMenu.MenuOn = TRUE
  18969.  
  18970.      GloMenu.fore = 0
  18971.      GloMenu.back = 7
  18972.      GloMenu.highlight = 15
  18973.      GloMenu.disabled = 8
  18974.      GloMenu.cursorFore = 7
  18975.      GloMenu.cursorBack = 0
  18976.      GloMenu.cursorHi = 15
  18977.  
  18978.  END SUB
  18979.  
  18980.  FUNCTION MenuInkey$ STATIC
  18981.  
  18982.      ' =======================================================================
  18983.      ' Scan keyboard, return KBD$ by default -- unless it is over written belo
  18984.      ' =======================================================================
  18985.  
  18986.      kbd$ = INKEY$
  18987.      MenuInkey$ = kbd$
  18988.  
  18989.      ' =======================================================================
  18990.      ' Check if KBD$ matches a shortcut key.  If it does, return "menu" instea
  18991.      ' of the key that was pressed
  18992.      ' =======================================================================
  18993.  
  18994.      ShortCutKeyEvent kbd$
  18995.      IF MenuCheck(2) THEN
  18996.          MenuInkey$ = "menu"
  18997.      ELSE
  18998.  
  18999.          ' ===================================================================
  19000.          ' Call menu event, which looks at mouse, and state of ALT key
  19001.          ' If a menu item is selected, return "menu" instead of KBD$
  19002.          ' ===================================================================
  19003.  
  19004.          MenuEvent
  19005.          IF MenuCheck(2) THEN
  19006.              MenuInkey$ = "menu"
  19007.          END IF
  19008.      END IF
  19009.  
  19010.  END FUNCTION
  19011.  
  19012.  SUB MenuItemToggle (menu, item)
  19013.  
  19014.      IF item >= 0 AND menu >= 1 AND item <= MAXITEM AND menu <= MAXMENU THEN
  19015.  
  19016.          IF item = 0 OR GloItem(menu, item).state < 1 OR GloItem(menu, item).s
  19017.              SOUND 2000, 40
  19018.          ELSE
  19019.              GloItem(menu, item).state = 3 - GloItem(menu, item).state
  19020.          END IF
  19021.  
  19022.      END IF
  19023.  END SUB
  19024.  
  19025.  DEFSNG A-Z
  19026.  SUB MenuOff
  19027.  
  19028.      ' =======================================================================
  19029.      ' Simply assigns FALSE to the proper global variable
  19030.      ' =======================================================================
  19031.  
  19032.      GloMenu.MenuOn = FALSE
  19033.  
  19034.  END SUB
  19035.  
  19036.  DEFINT A-Z
  19037.  SUB MenuOn
  19038.  
  19039.      ' =======================================================================
  19040.      ' Simply assigns TRUE to the proper global variable
  19041.      ' =======================================================================
  19042.  
  19043.      GloMenu.MenuOn = TRUE
  19044.  
  19045.  END SUB
  19046.  
  19047.  SUB MenuPreProcess STATIC
  19048.  
  19049.      currCol = 2     'Represents the col where first menu title is located
  19050.  
  19051.      ' =======================================================================
  19052.      ' Menu index is a fast way of decoding which menu the mouse cursor
  19053.      ' is pointing to based on the col of the cursor.  See MENU.BI for details
  19054.      ' =======================================================================
  19055.  
  19056.      GloMenu.menuIndex = STRING$(160, 0)
  19057.  
  19058.      ' =======================================================================
  19059.      ' Process each menu, one at a time
  19060.      ' =======================================================================
  19061.  
  19062.      FOR menu = 1 TO MAXMENU
  19063.  
  19064.          ' ===================================================================
  19065.          ' If state is empty, or text is "" then clear out data for that menu
  19066.          ' ===================================================================
  19067.  
  19068.          IF GloTitle(menu).state < 0 OR LEN(RTRIM$(GloTitle(menu).text)) = 0 T
  19069.              GloTitle(menu).rColItem = 0
  19070.              GloTitle(menu).lColItem = 0
  19071.              GloTitle(menu).rColTitle = 0
  19072.              GloTitle(menu).lColTitle = 0
  19073.              GloTitle(menu).itemLength = 0
  19074.              GloTitle(menu).state = -1
  19075.         ELSE
  19076.              ' ===============================================================
  19077.              ' else, assign data about the column location to the global stora
  19078.              ' ===============================================================
  19079.  
  19080.              GloTitle(menu).lColTitle = currCol
  19081.              GloTitle(menu).rColTitle = currCol + LEN(RTRIM$(GloTitle(menu).te
  19082.              GloTitle(menu).lColItem = currCol - 1
  19083.  
  19084.              IF GloTitle(menu).rColTitle > MAXCOL THEN
  19085.                  BEEP: CLS : PRINT "Menu bar longer than screen!  Cannot funct
  19086.                  END
  19087.              END IF
  19088.  
  19089.              ' ===============================================================
  19090.              ' Update the index about where the menu is located, increment
  19091.              ' currCol
  19092.              ' ===============================================================
  19093.  
  19094.              FOR index = currCol TO currCol + LEN(RTRIM$(GloTitle(menu).text))
  19095.                  MID$(GloMenu.menuIndex, index * 2 - 1, 2) = MKI$(menu)
  19096.              NEXT index
  19097.  
  19098.              currCol = currCol + LEN(RTRIM$(GloTitle(menu).text)) + 2
  19099.  
  19100.              ' ===============================================================
  19101.              ' Process the items in the menu, computing the
  19102.              ' longest item, and preparing the row index
  19103.              ' ===============================================================
  19104.  
  19105.              GloTitle(menu).itemLength = 0
  19106.              currRow = 3
  19107.              iFlag = FALSE
  19108.  
  19109.              FOR item = 1 TO MAXITEM
  19110.                  GloItem(menu, currRow - 2).index = 0
  19111.                  IF GloItem(menu, item).state >= 0 THEN
  19112.                      GloItem(menu, currRow - 2).index = item
  19113.                      GloItem(menu, item).row = currRow
  19114.                      currRow = currRow + 1
  19115.                      IF LEN(RTRIM$(GloItem(menu, item).text)) > GloTitle(menu)
  19116.                          GloTitle(menu).itemLength = LEN(RTRIM$(GloItem(menu,
  19117.                      END IF
  19118.                      iFlag = TRUE
  19119.                  END IF
  19120.              NEXT item
  19121.  
  19122.              ' ===============================================================
  19123.              ' If all items were empty, disable the menu itself
  19124.              ' else, assign the longest length to the proper variable
  19125.              ' ===============================================================
  19126.  
  19127.              IF NOT iFlag THEN
  19128.                  GloTitle(menu).state = 0
  19129.              ELSE
  19130.                  GloTitle(menu).rColItem = GloTitle(menu).lColItem + GloTitle(
  19131.                  IF GloTitle(menu).rColItem > MAXCOL - 2 THEN
  19132.                     diff = GloTitle(menu).rColItem - (MAXCOL - 2)
  19133.                     GloTitle(menu).rColItem = GloTitle(menu).rColItem - diff
  19134.                     GloTitle(menu).lColItem = GloTitle(menu).lColItem - diff
  19135.                  END IF
  19136.              END IF
  19137.  
  19138.          END IF
  19139.  
  19140.          GloTitle(menu).lowestRow = currRow + 1
  19141.      NEXT menu
  19142.  
  19143.  END SUB
  19144.  
  19145.  SUB MenuSet (menu, item, state, text$, accessKey) STATIC
  19146.  
  19147.      IF accessKey > LEN(text$) THEN accessKey = LEN(text$)
  19148.  
  19149.      IF item >= 0 AND menu >= 1 AND item <= MAXITEM AND menu <= MAXMENU THEN
  19150.  
  19151.          ' ===================================================================
  19152.          ' Assign parameters to proper global menu variables
  19153.          ' ===================================================================
  19154.  
  19155.          IF item = 0 THEN
  19156.              IF state < -1 OR state > 1 THEN
  19157.                  SOUND 3000, 40
  19158.              ELSE
  19159.                  GloTitle(menu).text = text$
  19160.                  GloTitle(menu).state = state
  19161.                  GloTitle(menu).accessKey = accessKey
  19162.              END IF
  19163.          ELSE
  19164.              IF state < -1 OR state > 2 THEN
  19165.                  SOUND 4000, 40
  19166.              ELSE
  19167.                  GloItem(menu, item).text = text$
  19168.                  GloItem(menu, item).state = state
  19169.                  GloItem(menu, item).accessKey = accessKey
  19170.              END IF
  19171.          END IF
  19172.      END IF
  19173.  
  19174.  END SUB
  19175.  
  19176.  SUB MenuSetState (menu, item, state) STATIC
  19177.  
  19178.      ' =======================================================================
  19179.      ' Assign parameters to proper global menu variables
  19180.      ' =======================================================================
  19181.  
  19182.      IF item = 0 THEN
  19183.          IF state < 0 OR state > 1 OR GloTitle(menu).state < 0 THEN
  19184.              SOUND 5000, 40
  19185.          ELSE
  19186.              GloTitle(menu).state = state
  19187.          END IF
  19188.      ELSE
  19189.          IF state < 0 OR state > 2 OR GloItem(menu, item).state < 0 THEN
  19190.              SOUND 6000, 40
  19191.          ELSE
  19192.              GloItem(menu, item).state = state
  19193.          END IF
  19194.      END IF
  19195.  
  19196.  END SUB
  19197.  
  19198.  DEFSNG A-Z
  19199.  SUB MenuShow
  19200.  
  19201.      ' =======================================================================
  19202.      ' This section actually prints the menu on the screen
  19203.      ' =======================================================================
  19204.  
  19205.      COLOR GloMenu.fore, GloMenu.back
  19206.      LOCATE 1, 1
  19207.      PRINT SPACE$(MAXCOL);
  19208.  
  19209.      FOR menu = 1 TO MAXMENU
  19210.          SELECT CASE GloTitle(menu).state
  19211.              CASE 0:
  19212.                  COLOR GloMenu.disabled, GloMenu.back
  19213.                  LOCATE 1, GloTitle(menu).lColTitle + 1
  19214.                  PRINT RTRIM$(GloTitle(menu).text$);
  19215.              CASE 1:
  19216.                  COLOR GloMenu.fore, GloMenu.back
  19217.                  LOCATE 1, GloTitle(menu).lColTitle + 1
  19218.                  PRINT RTRIM$(GloTitle(menu).text$);
  19219.              CASE ELSE
  19220.          END SELECT
  19221.  
  19222.      NEXT menu
  19223.  
  19224.  END SUB
  19225.  
  19226.  DEFINT A-Z
  19227.  SUB ShortCutKeyDelete (menu, item) STATIC
  19228.  
  19229.      '=======================================================================
  19230.      ' Search through shortcut key index until the menu,item pair is found
  19231.      ' or the end of the list is reached.
  19232.      '=======================================================================
  19233.  
  19234.      ptr = -1
  19235.      DO
  19236.          ptr = ptr + 1
  19237.          temp = CVI(MID$(GloMenu.shortcutKeyIndex, ptr * 4 + 1, 2))
  19238.          testMenu = INT(temp / 256)
  19239.          testItem = INT(temp MOD 256)
  19240.      LOOP UNTIL (menu = testMenu AND item = testItem) OR testMenu = 0 AND test
  19241.  
  19242.      '=======================================================================
  19243.      ' If a match is found, delete the shortcut key by squeezing out the four
  19244.      ' bytes that represents the shortcut key, and adding four chr$(0) at the
  19245.      ' end.
  19246.      '=======================================================================
  19247.  
  19248.      IF menu = testMenu AND item = testItem THEN
  19249.          GloMenu.shortcutKeyIndex = LEFT$(GloMenu.shortcutKeyIndex, ptr * 4) +
  19250.      END IF
  19251.  
  19252.  END SUB
  19253.  
  19254.  SUB ShortCutKeyEvent (theKey$)
  19255.  
  19256.      '=======================================================================
  19257.      ' If menu event trapping turned off, return immediately
  19258.      '=======================================================================
  19259.  
  19260.      IF NOT GloMenu.MenuOn THEN
  19261.          EXIT SUB
  19262.      END IF
  19263.  
  19264.      '=======================================================================
  19265.      ' Make sure the length of theKey$ is two bytes by adding a chr$(0) if
  19266.      ' necessary.  If the length is > 2, make it null.
  19267.      '=======================================================================
  19268.  
  19269.      SELECT CASE LEN(theKey$)
  19270.          CASE 1
  19271.              theKey$ = theKey$ + CHR$(0)
  19272.          CASE 2
  19273.          CASE ELSE
  19274.              theKey$ = ""
  19275.      END SELECT
  19276.  
  19277.      '=======================================================================
  19278.      ' Search the shortcut key list for a match -- only if theKey$ is valid.
  19279.      '=======================================================================
  19280.  
  19281.      IF theKey$ <> "" THEN
  19282.  
  19283.          ptr = -1
  19284.          DO
  19285.              ptr = ptr + 1
  19286.              testKey$ = MID$(GloMenu.shortcutKeyIndex, ptr * 4 + 3, 2)
  19287.  
  19288.          LOOP UNTIL theKey$ = testKey$ OR testKey$ = STRING$(2, 0) OR ptr = 25
  19289.  
  19290.          '===================================================================
  19291.          ' If match is found, make sure menu choice is valid (state > 0)
  19292.          ' If so, assign the proper global variables.
  19293.          '===================================================================
  19294.  
  19295.      IF theKey$ = testKey$ THEN
  19296.              temp = CVI(MID$(GloMenu.shortcutKeyIndex, ptr * 4 + 1, 2))
  19297.              tempMenu = INT(temp / 256)
  19298.              tempItem = INT(temp MOD 256)
  19299.  
  19300.              IF GloItem(tempMenu, tempItem).state > 0 THEN
  19301.                  GloMenu.currMenu = tempMenu
  19302.                  GloMenu.currItem = tempItem
  19303.              END IF
  19304.          END IF
  19305.      END IF
  19306.  
  19307.  END SUB
  19308.  
  19309.  SUB ShortCutKeySet (menu, item, shortcutKey$)
  19310.  
  19311.      '=======================================================================
  19312.      ' Make sure the length of theKey$ is two bytes by adding a chr$(0) if
  19313.      ' necessary.  If the length is >2, make it null.
  19314.      '=======================================================================
  19315.  
  19316.      SELECT CASE LEN(shortcutKey$)
  19317.          CASE 1
  19318.              shortcutKey$ = shortcutKey$ + CHR$(0)
  19319.          CASE 2
  19320.          CASE ELSE
  19321.              shortcutKey$ = ""
  19322.      END SELECT
  19323.  
  19324.      '=======================================================================
  19325.      ' First delete the shortcut key, just in case it already exists, and then
  19326.      ' and the shortcut key to the front of the shortcut key index string.
  19327.      '=======================================================================
  19328.  
  19329.      ShortCutKeyDelete menu, item
  19330.      IF shortcutKey$ <> "" THEN
  19331.          newKey$ = MKI$(menu * 256 + item) + shortcutKey$
  19332.          GloMenu.shortcutKeyIndex = newKey$ + LEFT$(GloMenu.shortcutKeyIndex,
  19333.      END IF
  19334.  
  19335.  END SUB
  19336.  
  19337.  
  19338.  
  19339.  MOUSE.BAS
  19340.  CD-ROM Disc Path:   \SAMPCODE\BASIC\MOUSE.BAS
  19341.  
  19342.  '============================================================================
  19343.  '
  19344.  '    MOUSE.BAS - Mouse Support Routines for the User Interface Toolbox in
  19345.  '           Microsoft BASIC 7.0, Professional Development System
  19346.  '              Copyright (C) 1987-1989, Microsoft Corporation
  19347.  '
  19348.  ' NOTE:     This sample source code toolbox is intended to demonstrate some
  19349.  '           of the extended capabilities of Microsoft BASIC 7.0 Professional
  19350.  '           Development system that can help to leverage the professional
  19351.  '           developer's time more effectively.  While you are free to use,
  19352.  '           modify, or distribute the routines in this module in any way you
  19353.  '           find useful, it should be noted that these are examples only and
  19354.  '           should not be relied upon as a fully-tested "add-on" library.
  19355.  '
  19356.  '  PURPOSE: These routines are required for mouse support in the user
  19357.  '           interface toolbox, but they may be used independently as well.
  19358.  '
  19359.  '  For information on creating a library and QuickLib from the routines
  19360.  '  contained in this file, read the comment header of GENERAL.BAS.
  19361.  '
  19362.  '============================================================================
  19363.  
  19364.  DEFINT A-Z
  19365.  
  19366.  '$INCLUDE: 'general.bi'
  19367.  '$INCLUDE: 'mouse.bi'
  19368.  '$INCLUDE: 'menu.bi'
  19369.  
  19370.  COMMON SHARED /uitools/ GloMenu      AS MenuMiscType
  19371.  COMMON SHARED /uitools/ GloTitle()   AS MenuTitleType
  19372.  COMMON SHARED /uitools/ GloItem()    AS MenuItemType
  19373.  
  19374.  SUB MouseBorder (row1, col1, row2, col2) STATIC
  19375.  
  19376.      ' =======================================================================
  19377.      ' Sets max and min bounds on mouse movement both vertically, and
  19378.      ' horizontally
  19379.      ' =======================================================================
  19380.  
  19381.      MouseDriver 7, 0, (col1 - 1) * 8, (col2 - 1) * 8
  19382.      MouseDriver 8, 0, (row1 - 1) * 8, (row2 - 1) * 8
  19383.  
  19384.  END SUB
  19385.  
  19386.  SUB MouseDriver (m0, m1, m2, m3) STATIC
  19387.  
  19388.      DIM regs AS RegType
  19389.  
  19390.      IF MouseChecked = FALSE THEN
  19391.          DEF SEG = 0
  19392.  
  19393.          MouseSegment& = 256& * PEEK(207) + PEEK(206)
  19394.          MouseOffset& = 256& * PEEK(205) + PEEK(204)
  19395.  
  19396.          DEF SEG = MouseSegment&
  19397.  
  19398.          IF (MouseSegment& = 0 AND MouseOffset& = 0) OR PEEK(MouseOffset&) = 2
  19399.              MousePresent = FALSE
  19400.              MouseChecked = TRUE
  19401.              DEF SEG
  19402.          END IF
  19403.      END IF
  19404.  
  19405.      IF MousePresent = FALSE AND MouseChecked = TRUE THEN
  19406.          EXIT SUB
  19407.      END IF
  19408.  
  19409.      ' =======================================================================
  19410.      ' Calls interrupt 51 to invoke mouse functions in the MS Mouse Driver.
  19411.      ' =======================================================================
  19412.  
  19413.      regs.ax = m0
  19414.      regs.bx = m1
  19415.      regs.cx = m2
  19416.      regs.dx = m3
  19417.  
  19418.      Interrupt 51, regs, regs
  19419.  
  19420.      m0 = regs.ax
  19421.      m1 = regs.bx
  19422.      m2 = regs.cx
  19423.      m3 = regs.dx
  19424.  
  19425.      IF MouseChecked THEN EXIT SUB
  19426.  
  19427.      ' =======================================================================
  19428.      ' Check for successful mouse initialization
  19429.      ' =======================================================================
  19430.  
  19431.      IF m0 AND NOT MouseChecked THEN
  19432.          MousePresent = TRUE
  19433.      END IF
  19434.  
  19435.      MouseChecked = TRUE
  19436.  
  19437.  END SUB
  19438.  
  19439.  SUB MouseHide
  19440.  
  19441.      ' =======================================================================
  19442.      ' Decrements internal cursor flag
  19443.      ' =======================================================================
  19444.  
  19445.     MouseDriver 2, 0, 0, 0
  19446.  
  19447.  END SUB
  19448.  
  19449.  SUB MouseInit
  19450.  
  19451.      ' =======================================================================
  19452.      ' Mouse driver's initialization routine
  19453.      ' =======================================================================
  19454.  
  19455.      MouseDriver 0, 0, 0, 0
  19456.  
  19457.  END SUB
  19458.  
  19459.  SUB MousePoll (row, col, lButton, rButton) STATIC
  19460.  
  19461.      ' =======================================================================
  19462.      ' Polls mouse driver, then sets parms correctly
  19463.      ' =======================================================================
  19464.  
  19465.      MouseDriver 3, button, col, row
  19466.      row = row / 8 + 1
  19467.      col = col / 8 + 1
  19468.  
  19469.      IF button AND 1 THEN
  19470.          lButton = TRUE
  19471.      ELSE
  19472.          lButton = FALSE
  19473.      END IF
  19474.  
  19475.      IF button AND 2 THEN
  19476.          rButton = TRUE
  19477.      ELSE
  19478.          rButton = FALSE
  19479.      END IF
  19480.  
  19481.  END SUB
  19482.  
  19483.  SUB MouseShow
  19484.  
  19485.      ' =======================================================================
  19486.      ' Increments mouse's internal cursor flag
  19487.      ' =======================================================================
  19488.  
  19489.      MouseDriver 1, 0, 0, 0
  19490.  
  19491.  END SUB
  19492.  
  19493.  
  19494.  
  19495.  MUSIC.BAS
  19496.  CD-ROM Disc Path:   \SAMPCODE\BASIC\MUSIC.BAS
  19497.  
  19498.  ' Turn on trapping of background music events:
  19499.  PLAY ON
  19500.  
  19501.  ' Branch to the Refresh subroutine when there are fewer than
  19502.  ' two notes in the background music buffer:
  19503.  ON PLAY(2) GOSUB Refresh
  19504.  
  19505.  PRINT "Press any key to start, q to end."
  19506.  Pause$ = INPUT$(1)
  19507.  
  19508.  ' Select the background music option for PLAY:
  19509.  PLAY "MB"
  19510.  
  19511.  ' Start playing the music, so notes will be put in the
  19512.  ' background music buffer:
  19513.  GOSUB Refresh
  19514.  
  19515.  I = 0
  19516.  
  19517.  DO
  19518.  
  19519.          ' Print the numbers from 0 to 10,000 over and over until
  19520.          ' the user presses the "q" key. While this is happening,
  19521.          ' the music will repeat in the background:
  19522.          PRINT I
  19523.          I = (I + 1) MOD 10001
  19524.  LOOP UNTIL INKEY$ = "q"
  19525.  
  19526.  END
  19527.  
  19528.  Refresh:
  19529.  
  19530.          ' Plays the opening motive of
  19531.          ' Beethoven's Fifth Symphony:
  19532.          Listen$ = "t180 o2 p2 p8 L8 GGG L2 E-"
  19533.          Fate$   = "p24 p8 L8 FFF L2 D"
  19534.          PLAY Listen$ + Fate$
  19535.          RETURN
  19536.  
  19537.  
  19538.  
  19539.  MXADSTA.ASM
  19540.  CD-ROM Disc Path:   \SAMPCODE\BASIC\MXADSTA.ASM
  19541.  
  19542.  ;***************************** ADDSTRING ********************************
  19543.  ; This procedure accepts two far strings, concatenates them, and
  19544.  ; returns the result in the form of a far string.
  19545.  
  19546.          .model  medium,basic        ;Define memory model to match BASIC.
  19547.          .stack
  19548.          .data?
  19549.          maxst = 50                  ;Maximum bytes reserved for strings
  19550.  inbuffer1       db  maxst dup(0)    ;Room for first fixed-length string
  19551.  inbuffer2       db  maxst dup(0)    ;and second one
  19552.  outbuffer       db  2*maxst dup(0)  ;Work area for string processing
  19553.          .data
  19554.  sh              dd  0               ;Output string descriptor
  19555.          .code
  19556.  addstring   proc    uses si di ds, s1:far ptr, s1len, s2:far ptr, s2len
  19557.  
  19558.  ;First get BASIC to convert BASIC strings into standard form.
  19559.          les     ax,s1               ;Push far pointer to
  19560.          push    es                  ;input string descriptor.
  19561.          push    ax
  19562.          xor     ax,ax               ;Push a zero to indicate
  19563.          push    ax                  ;it is variable length.
  19564.          push    ds                  ;Push far pointer to
  19565.          lea     ax, inbuffer1       ;destination string.
  19566.          push    ax
  19567.          mov     ax,maxst            ;Push length of destination
  19568.          push    ax                  ;fixed-length string.
  19569.          extrn   stringassign:proc
  19570.          call    stringassign        ;Call BASIC to assign variable-length
  19571.                                      ;string to fixed-length string.
  19572.          les     ax,s2               ;Push far pointer to second
  19573.          push    es                  ;input string descriptor.
  19574.          push    ax
  19575.          xor     ax,ax               ;Push a zero to indicate
  19576.          push    ax                  ;it is variable length.
  19577.          push    ds                  ;Push far pointer to
  19578.          lea     ax,inbuffer2        ;second destination string.
  19579.          push    ax
  19580.          mov     ax,maxst            ;Push length of destination
  19581.          push    ax                  ;fixed-length string.
  19582.          extrn   stringassign:proc
  19583.          call    stringassign        ;Call BASIC to assign variable-length
  19584.                                      ;string to fixed-length string.
  19585.  ;Concatenate strings.
  19586.          lea     si,inbuffer1        ;Copy first string to buffer.
  19587.          lea     di,outbuffer
  19588.          mov     ax,ds
  19589.          mov     es,ax
  19590.          mov     cx,s1len
  19591.          rep     movsb
  19592.          lea     si,inbuffer2        ;Concatenate second string to
  19593.          mov     cx,s2len            ;end of first.
  19594.          rep     movsb
  19595.  
  19596.  ;Get BASIC to convert result back into a BASIC string.
  19597.          push    ds                  ;Push far pointer to fixed-length
  19598.          lea     ax,outbuffer        ;result string.
  19599.          push    ax
  19600.          mov     ax,s1len            ;Compute total length of
  19601.          mov     bx,s2len            ;fixed-length result string.
  19602.          add     ax,bx
  19603.          push    ax                  ;Push length.
  19604.          push    ds                  ;Push far pointer to sh (BASIC
  19605.          lea     ax,sh               ;will use this in StringAssign).
  19606.          push    ax
  19607.          xor     ax,ax               ;Push a zero for length
  19608.          push    ax                  ;indicating variable-length.
  19609.          call    stringassign        ;Call BASIC to assign the
  19610.                                      ;result to sh.
  19611.          lea     ax,sh               ;Return output string pointer
  19612.                                      ;in ax and go back to BASIC.
  19613.          ret
  19614.  
  19615.  addstring       endp
  19616.                  end
  19617.  
  19618.  
  19619.  MXADSTB.BAS
  19620.  CD-ROM Disc Path:   \SAMPCODE\BASIC\MXADSTB.BAS
  19621.  
  19622.  DEFINT A-Z
  19623.  
  19624.  'Start program in BASIC for proper initialization.
  19625.  ' Define external and internal procedures.
  19626.  DECLARE SUB shakespeare ()
  19627.  DECLARE SUB StringAssign (BYVAL srcsegment, BYVAL srcoffset, BYVAL srclen, BY
  19628.  DECLARE SUB addstring (instrg1off, instrg1len, instrg2off, instrg2len, outstr
  19629.  DECLARE SUB StringRelease (s$)
  19630.  
  19631.  'Go to main routine in second language
  19632.  CALL shakespeare
  19633.  
  19634.  'The non-BASIC program calls this SUB to add the two strings together
  19635.  SUB addstring (instrg1off, instrg1len, instrg2off, instrg2len, outstrgoff, ou
  19636.  
  19637.  'Create variable-length strings and transfer non-BASIC fixed strings to them.
  19638.  'Use VARSEG() to compute the segement of the strings returned from the other
  19639.  'language--this is the DGROUP segment, and all string descriptors are found
  19640.  'in this segment (even though the far string itself is elsewhere).
  19641.  
  19642.  CALL StringAssign(VARSEG(a$), instrg1off, instrg1len, VARSEG(a$), VARPTR(a$),
  19643.  CALL StringAssign(VARSEG(b$), instrg2off, instrg2len, VARSEG(b$), VARPTR(b$),
  19644.  
  19645.  ' Process the strings--in this case, add them.
  19646.  c$ = a$ + b$
  19647.  
  19648.  ' Calculate the new output length.
  19649.  outstrglen = LEN(c$)
  19650.  
  19651.  ' Transfer string output to a non-BASIC fixed-length string.
  19652.  CALL StringAssign(VARSEG(c$), VARPTR(c$), 0, VARSEG(c$), outstrgoff, outstrgl
  19653.  
  19654.  END SUB
  19655.  
  19656.  
  19657.  
  19658.  MXADSTC.C
  19659.  CD-ROM Disc Path:   \SAMPCODE\BASIC\MXADSTC.C
  19660.  
  19661.  #include <string.h>
  19662.  
  19663.  /* Function Prototypes force either correct data typing or compiler warnings.
  19664.   * Note all functions exported to BASIC and all BASIC callback (extern)
  19665.   * functions are declared with the far pascal calling convention.
  19666.   * WARNING: This must be compiled with the Medium memory model (/AM)
  19667.   */
  19668.  
  19669.  char * pascal addstring( char far *s1, int s1len,
  19670.                char far *s2, int s2len );
  19671.  extern void far pascal StringAssign( char far *source, int slen,
  19672.                         char far *dest, int dlen );
  19673.  
  19674.  /* Declare global char array to contain new BASIC string descriptor.
  19675.   */
  19676.  char BASICDesc[4];
  19677.  
  19678.  char * pascal addstring( char far *s1, int s1len,
  19679.                char far *s2, int s2len )
  19680.  {
  19681.      char TS1[50];
  19682.      char TS2[50];
  19683.      char TSBig[100];
  19684.  
  19685.      /* Use the BASIC callback StringAssign to retrieve information
  19686.       * from the descriptors, s1 and s2, and place them in the temporary
  19687.       * arrays TS1 and TS2.
  19688.       */
  19689.      StringAssign( s1, 0, TS1, 49 );        /* Get S1 as array of char */
  19690.      StringAssign( s2, 0, TS2, 49 );        /* Get S2 as array of char */
  19691.  
  19692.      /* Copy the data from TS1 into TSBig, then append the data from
  19693.       * TS2.
  19694.       */
  19695.      memcpy( TSBig, TS1, s1len );
  19696.      memcpy( &TSBig[s1len], TS2, s2len );
  19697.  
  19698.      StringAssign( TSBig, s1len + s2len, BASICDesc, 0 );
  19699.  
  19700.      return BASICDesc;
  19701.  }
  19702.  
  19703.  /*
  19704.   * If, for example, we wanted to return not just one variable length string,
  19705.   * but rather the variable length string and the reverse of that:
  19706.   *
  19707.   * call addstring( "foo ", 4, "bar", 3, a$, r$ )
  19708.   *
  19709.   * you get "foo bar" in a$ and "rab oof" in r$.
  19710.   *
  19711.   * Say you give me s1, and s2 (and their respective lengths) on input; for
  19712.   * output, I want s3 and s4.
  19713.   *
  19714.   * Change the StringAssign for TSBig to assign to s3 instead of BASICDesc.
  19715.   *
  19716.   * Add the following lines of code:
  19717.   *
  19718.   *     TSBig[s1len + s2len] = '\0';
  19719.   *     strrev( TSBig );
  19720.   *     StringAssign( TSBig, s1len + s2len, s4, 0 );
  19721.   *
  19722.   * Delete the return statement.
  19723.   *
  19724.   * Change the prototype and function header to say:
  19725.   *
  19726.   * void far pascal addstring
  19727.   *
  19728.   * instead of
  19729.   *
  19730.   * char far * pascal addstring
  19731.   */
  19732.  
  19733.  
  19734.  MXADSTF.FOR
  19735.  CD-ROM Disc Path:   \SAMPCODE\BASIC\MXADSTF.FOR
  19736.  
  19737.  C ******************** ADDSTRING  *********************
  19738.  C This program is in file MXADSTF.FOR
  19739.  C Declare interface to Stringassign subprogram. The pointer fields are
  19740.  C declared INTEGER*4, so that different types of far pointers can be
  19741.  C passed without conflict. The INTEGER*4 fields are essentially generic
  19742.  C pointers. [VALUE] must be specified, or FORTRAN will pass pointers to
  19743.  C pointers. INTEGER*2 also passed by [VALUE], to be consistent with
  19744.  C declaration of Stringassign.
  19745.  C
  19746.         INTERFACE TO SUBROUTINE STRASG [ALIAS:'STRINGASSIGN'] (S,SL,D,DL)
  19747.         INTEGER*4 S [VALUE]
  19748.         INTEGER*2 SL [VALUE]
  19749.         INTEGER*4 D [VALUE]
  19750.         INTEGER*2 DL [VALUE]
  19751.         END
  19752.  C
  19753.  C Declare heading of Addstring function in the same way as above: the
  19754.  C pointer fields are INTEGER*4
  19755.  C
  19756.         INTEGER*2 FUNCTION ADDSTR [ALIAS:'ADDSTRING'] (S1,S1LEN,S2,S2LEN)
  19757.         INTEGER*4 S1 [VALUE]
  19758.         INTEGER*2 S1LEN [VALUE]
  19759.         INTEGER*4 S2 [VALUE]
  19760.         INTEGER*2 S2LEN [VALUE]
  19761.  C
  19762.  C Local parameters TS1, TS2, and BIGSTR are temporary strings. STRDES is
  19763.  C a four-byte object into which Stringassign will put BASIC string
  19764.  C descriptor.
  19765.  C
  19766.         CHARACTER*50 TS1, TS2
  19767.         CHARACTER*100 BIGSTR
  19768.         INTEGER*4 STRDES
  19769.  
  19770.          TS1 = " "
  19771.          TS2 = " "
  19772.          STRDES = 0
  19773.  
  19774.  C
  19775.  C Use the LOCFAR function to take the far address of data. LOCFAR returns
  19776.  C a value of type INTEGER*4.
  19777.  C
  19778.         CALL STRASG (S1, 0, LOCFAR(TS1), S1LEN)
  19779.         CALL STRASG (S2, 0, LOCFAR(TS2), S2LEN)
  19780.         BIGSTR = TS1(1:S1LEN) // TS2(1:S2LEN)
  19781.         CALL STRASG (LOCFAR(BIGSTR), S1LEN+S2LEN, LOCFAR(STRDES), 0)
  19782.         ADDSTR = LOC(STRDES)
  19783.         RETURN
  19784.         END
  19785.  
  19786.  
  19787.  MXSHKA.ASM
  19788.  CD-ROM Disc Path:   \SAMPCODE\BASIC\MXSHKA.ASM
  19789.  
  19790.  ;*************************** SHAKESPEARE ******************************
  19791.  ; This program creates two strings and passes them to a BASIC procedure
  19792.  ; called addstring (in file MXADSTB.BAS).  This procedure concatenates
  19793.  ; the strings and passes the result to MASM which prints it.
  19794.  
  19795.          .model  medium,basic        ;Use same memory model as BASIC.
  19796.          .stack
  19797.          .data                       ;Create the data.
  19798.  phrase1         db      "To be or not to be;"
  19799.  phrase1len      dw      $-phrase1
  19800.  phrase1off      dw      phrase1
  19801.  phrase2         db      " that is the question."
  19802.  phrase2len      dw      $-phrase2
  19803.  phrase2off      dw      phrase2
  19804.  sentence        db      100 dup(0)  ;Make room for return data
  19805.  sentencelen     dw      0           ;and a length indicator.
  19806.  sentenceoff     dw      sentence
  19807.  
  19808.          .code
  19809.  shakespeare proc    uses si
  19810.  
  19811.  ;First call BASIC to concatenate strings.
  19812.          lea     ax,phrase1off       ;Push far address of
  19813.          push    ax                  ;fixed-length string #1,
  19814.          lea     ax,phrase1len       ;and its length.
  19815.          push    ax
  19816.          lea     ax,phrase2off       ;Do the same for the
  19817.          push    ax                  ;address of string #2,
  19818.          lea     ax,phrase2len       ;and its length.
  19819.          push    ax
  19820.          lea     ax,sentenceoff      ;Push far address of
  19821.          push    ax                  ;the return string,
  19822.          lea     ax,sentencelen      ;and its length.
  19823.          push    ax
  19824.          extrn   addstring:proc      ;Call BASIC function to
  19825.          call    addstring           ;concatenate the strings and
  19826.                                      ;put the result in the
  19827.                                      ;fixed-length return string.
  19828.  
  19829.  ;Call DOS to print string. The DOS string output routine (09H)
  19830.  ;requires that strings end with a "$" character.
  19831.          mov     bx,sentencelen      ;Go to end of the result string
  19832.          lea     si,sentence         ;and add a "$" (24h) character.
  19833.          mov     byte ptr [bx + si],24h
  19834.  
  19835.          lea     dx,sentence         ;Set up registers
  19836.          mov     ah,9                ;and call DOS to
  19837.          int     21h                 ;print result string.
  19838.          ret
  19839.  
  19840.  shakespeare endp
  19841.  
  19842.          end
  19843.  
  19844.  
  19845.  MXSHKB.BAS
  19846.  CD-ROM Disc Path:   \SAMPCODE\BASIC\MXSHKB.BAS
  19847.  
  19848.  DEFINT A-Z
  19849.  'Define non-basic procedures
  19850.  DECLARE FUNCTION addstring$(SEG s1$, BYVAL s1length, SEG s2$, BYVAL s2length)
  19851.  
  19852.  
  19853.  'Create the data
  19854.  a$ = "To be or not to be;"
  19855.  b$ = " that is the question."
  19856.  
  19857.  'Use non-BASIC function to add two BASIC far strings
  19858.  c$ = addstring(a$, LEN(a$), b$, LEN(b$))
  19859.  
  19860.  'print the result on the screen
  19861.  
  19862.  PRINT c$
  19863.  
  19864.  
  19865.  MXSHKC.C
  19866.  CD-ROM Disc Path:   \SAMPCODE\BASIC\MXSHKC.C
  19867.  
  19868.  #include <stdio.h>
  19869.  #include <string.h>
  19870.  
  19871.  /* Function Prototypes force either correct data typing or compiler warnings.
  19872.   * Note all functions exported to BASIC and all BASIC callback (extern)
  19873.   * functions are declared with the far pascal calling convention.
  19874.   * IMPORTANT: This must be compiled with the Medium memory model (/AM)
  19875.   */
  19876.  void far pascal shakespeare( void );
  19877.  extern void far pascal addstring( char  ** s1, int * s1len,
  19878.                                      char ** s2, int * s2len,
  19879.                                      char ** s3, int * s3len );
  19880.  
  19881.  void far pascal shakespeare( void )
  19882.  {
  19883.      char * s1 = "To be or not to be;";
  19884.      int  s1len;
  19885.      char * s2 = " that is the question.";
  19886.      int  s2len;
  19887.      char s3[100];
  19888.      int  s3len;
  19889.      char * s3add = s3;
  19890.  
  19891.      s1len = strlen( s1 );
  19892.      s2len = strlen( s2 );
  19893.      addstring( &s1, &s1len, &s2, &s2len, &s3add, &s3len );
  19894.  
  19895.      s3[s3len] = '\0';
  19896.      printf("\n%s", s3 );
  19897.  }
  19898.  
  19899.  
  19900.  MXSHKF.FOR
  19901.  CD-ROM Disc Path:   \SAMPCODE\BASIC\MXSHKF.FOR
  19902.  
  19903.  C *********************** SHAKESPEARE ****************
  19904.  C This program is in file MXSHKF.FOR
  19905.  C Declare interface to BASIC routine ADDSTRING.
  19906.  C All parameters must be passed NEAR, for compatibility with BASIC's
  19907.  C conventions.
  19908.  C
  19909.  
  19910.  
  19911.         INTERFACE TO SUBROUTINE ADDSTR[ALIAS:'ADDSTRING']
  19912.       * (S1,L1,S2,L2,S3,L3)
  19913.         INTEGER*2 S1 [NEAR]
  19914.         INTEGER*2 L1 [NEAR]
  19915.         INTEGER*2 S2 [NEAR]
  19916.         INTEGER*2 L2 [NEAR]
  19917.         INTEGER*2 S3 [NEAR]
  19918.         INTEGER*2 L3 [NEAR]
  19919.         END
  19920.  C
  19921.  C Declare subroutine SHAKESPEARE, which declares two strings, calls BASIC
  19922.  C subroutine ADDSTRING, and prints the result.
  19923.  C
  19924.         SUBROUTINE SHAKES [ALIAS:'SHAKESPEARE']
  19925.         CHARACTER*50 STR1, STR2
  19926.         CHARACTER*100 STR3
  19927.         INTEGER*2 STRLEN1, STRLEN2, STRLEN3
  19928.         INTEGER*2 TMP1, TMP2, TMP3
  19929.  C
  19930.  C The subroutine uses FORTRAN LEN_TRIM function, which returns the length
  19931.  C of string, excluding trailing blanks. (All FORTRAN strings are initialized
  19932.  C to blanks.)
  19933.  C
  19934.         STR1 = 'To be or not to be;'
  19935.         STRLEN1 = LEN_TRIM(STR1)
  19936.         STR2 = ' that is the question.'
  19937.         STRLEN2 = LEN_TRIM(STR2)
  19938.         TMP1 = LOC(STR1)
  19939.         TMP2 = LOC(STR2)
  19940.         TMP3 = LOC(STR3)
  19941.         CALL ADDSTR (TMP1, STRLEN1, TMP2, STRLEN2, TMP3, STRLEN3)
  19942.         WRITE (*,*) STR3
  19943.         END
  19944.  
  19945.  
  19946.  
  19947.  PALETTE.BAS
  19948.  CD-ROM Disc Path:   \SAMPCODE\BASIC\PALETTE.BAS
  19949.  
  19950.  DECLARE SUB InitPalette ()
  19951.  DECLARE        SUB ChangePalette ()
  19952.  DECLARE        SUB DrawEllipses ()
  19953.  
  19954.  DEFINT A-Z
  19955.  DIM SHARED PaletteArray(15)
  19956.  
  19957.  SCREEN 8                 ' 640 x 200 resolution; 16 colors
  19958.  
  19959.  InitPalette                 ' Initialize PaletteArray.
  19960.  DrawEllipses                 ' Draw and paint concentric ellipses.
  19961.  
  19962.  DO                         ' Shift the palette until a key
  19963.     ChangePalette         ' is pressed.
  19964.  LOOP WHILE INKEY$ = ""
  19965.  
  19966.  END
  19967.  
  19968.  
  19969.  ' ====================== InitPalette ======================
  19970.  '    This procedure initializes the integer array used to
  19971.  '    change the palette.
  19972.  ' =========================================================
  19973.  
  19974.  SUB InitPalette        STATIC
  19975.     FOR I = 0 TO        15
  19976.        PaletteArray(I) =        I
  19977.     NEXT        I
  19978.  END SUB
  19979.  ' ===================== DrawEllipses ======================
  19980.  '    This procedure draws 15 concentric ellipses and
  19981.  '    paints the interior of each with a different color.
  19982.  ' =========================================================
  19983.  
  19984.  SUB DrawEllipses STATIC
  19985.     CONST ASPECT        = 1 / 3
  19986.     FOR ColorVal        = 15 TO        1 STEP -1
  19987.        Radius = 20 * ColorVal
  19988.        CIRCLE (320, 100), Radius, ColorVal, , , ASPECT
  19989.        PAINT (320, 100),        ColorVal
  19990.     NEXT
  19991.  END SUB
  19992.  
  19993.  
  19994.  ' ===================== ChangePalette =====================
  19995.  '  This procedure rotates the palette by one each time it
  19996.  '  is called. For example, after the first call to
  19997.  '  ChangePalette, PaletteArray(1) = 2, PaletteArray(2) = 3,
  19998.  '  . . . , PaletteArray(14) = 15, and PaletteArray(15) = 1
  19999.  ' =========================================================
  20000.  
  20001.  SUB ChangePalette STATIC
  20002.     FOR I = 1 TO        15
  20003.        PaletteArray(I) =        (PaletteArray(I) MOD 15) + 1
  20004.     NEXT        I
  20005.     PALETTE USING PaletteArray(0) ' Shift the color displayed
  20006.                                   ' by each of the attributes.
  20007.  END SUB
  20008.  
  20009.  
  20010.  
  20011.  PASSWRD.BAS
  20012.  CD-ROM Disc Path:   \SAMPCODE\BASIC\PASSWRD.BAS
  20013.  
  20014.  DECLARE FUNCTION CertifiedOperator% ()
  20015.  CONST FALSE = 0, True = NOT FALSE
  20016.  
  20017.  IF CertifiedOperator = FALSE THEN
  20018.          PRINT "Connection Refused."
  20019.          END
  20020.  END IF
  20021.  
  20022.  PRINT "Connected to Network."
  20023.  'Main program continues here.
  20024.  '  .
  20025.  '  .
  20026.  '  .
  20027.  END
  20028.  
  20029.  FUNCTION CertifiedOperator%
  20030.  ON LOCAL ERROR GOTO Handler
  20031.  'Count the number of times the operator tries to sign on.
  20032.  Attempts% = 0
  20033.  
  20034.  TryAgain:
  20035.  'Assume the operator has valid credentials.
  20036.  CertifiedOperator = True
  20037.  'Keep track of bad entries.
  20038.  Attempts% = Attempts% + 1
  20039.  IF Attempts% > 3 THEN ERROR 255
  20040.  'Check out the operator's credentials.
  20041.  INPUT "Enter Account Number"; Account$
  20042.  IF LEFT$(Account$, 4) <> "1234" THEN ERROR 200
  20043.  INPUT "Enter Password"; Password$
  20044.  IF Password$ <> "Swordfish" THEN ERROR 201
  20045.  EXIT FUNCTION
  20046.  
  20047.  Handler:
  20048.  SELECT CASE ERR
  20049.      'Start over if account number doesn't have "1234" in it.
  20050.          CASE 200
  20051.                  PRINT "Illegal account number. Please re-enter."
  20052.                  RESUME TryAgain
  20053.      'Start over if the password is wrong.
  20054.          CASE 201
  20055.                  PRINT "Wrong password. Please re-enter both items."
  20056.                  RESUME TryAgain
  20057.      'Return false if operator makes too many mistakes.
  20058.          CASE 255
  20059.                  CertifiedOperator% = FALSE
  20060.                  EXIT FUNCTION
  20061.  END SELECT
  20062.  
  20063.  END FUNCTION
  20064.  
  20065.  
  20066.  PGBAR.BAS
  20067.  CD-ROM Disc Path:   \SAMPCODE\BASIC\PGBAR.BAS
  20068.  
  20069.  ' PGBAR.BAS:  Create sample bar chart
  20070.  
  20071.  DEFINT A-Z
  20072.  ' $INCLUDE: 'CHRTB.BI'
  20073.  CONST FALSE = 0, TRUE = NOT FALSE, MONTHS = 12
  20074.  CONST HIGHESTMODE = 13, TEXTONLY = 0
  20075.  
  20076.  DIM Env AS ChartEnvironment                 ' See CHRTB.BI for declaration of
  20077.  DIM MonthCategories(1 TO MONTHS) AS STRING  ' Array for categories (used for
  20078.  
  20079.  DIM OJvalues(1 TO MONTHS) AS SINGLE         ' Array for 1st data series
  20080.  
  20081.  DECLARE FUNCTION BestMode ()
  20082.  
  20083.  ' Initialize the data arrays
  20084.  FOR index = 1 TO MONTHS: READ OJvalues(index): NEXT index
  20085.  FOR index = 1 TO MONTHS: READ MonthCategories$(index): NEXT index
  20086.  
  20087.  ' Pass the value returned by the BestMode function to the Presentation
  20088.  ' Graphics routine ChartScreen to set the graphics mode for charting
  20089.  
  20090.  ChartScreen (BestMode)      ' Even if SCREEN is already set to an acceptable
  20091.  
  20092.  IF ChartErr = cBadScreen THEN   ' Check to make sure ChartScreen succeeded
  20093.          PRINT "Sorry --- There is a screen-mode problem in the Charting libra
  20094.          END
  20095.  END IF
  20096.  ' Initialize a default pie chart
  20097.  
  20098.  DefaultChart Env, cBar, cPlain      ' the constant cBar (for Bar Chart) and
  20099.  
  20100.  
  20101.  ' Add Titles and some chart options. These assignments modify some default
  20102.  ' values set in the variable Env (of type ChartEnvironment) by DefaultChart
  20103.  
  20104.  Env.MainTitle.Title = "Good Neighbor Grocery" ' Specifies text of chart title
  20105.  Env.MainTitle.TitleColor = 15                 ' Specifies color of title text
  20106.  Env.MainTitle.Justify = cRight                ' How to align of title text
  20107.  Env.SubTitle.Title = "Orange Juice Sales"     ' Text of chart subtitle
  20108.  Env.SubTitle.TitleColor = 15                  ' Color of subtitle text
  20109.  Env.SubTitle.Justify = cRight                 ' How to align of subtitle text
  20110.  Env.ChartWindow.Border = cNo                  ' Specifies chart has no border
  20111.  
  20112.  ' The next 2 assignments label the x-axis and y-axis
  20113.  Env.XAxis.AxisTitle.Title = "Quantity (cases)"
  20114.  Env.YAxis.AxisTitle.Title = "Months"
  20115.  
  20116.  ' Call the bar-charting routine --- Arguments for call to Chart are:
  20117.  ' Env                 - Environment variable
  20118.  ' MonthCategories()   - Array containing Category labels
  20119.  ' OJvalues()          - Array containing Data values to chart
  20120.  ' MONTHS              - Tells number of data values to chart
  20121.  
  20122.          Chart Env, MonthCategories(), OJvalues(), MONTHS
  20123.          SLEEP
  20124.          '  If the rest of your program isn't graphic, reset original mode her
  20125.  END
  20126.  
  20127.  ' Simulate data generation for chart values and category labels
  20128.  DATA 33,27,42,64,106,157,182,217,128,62,43,36
  20129.  DATA "Jan","Feb","Mar","Apr","May","Jun","Jly","Aug","Sep","Oct","Nov","Dec",
  20130.  
  20131.  '============= Function to determine and set highest resolution ========
  20132.  ' The BestMode function uses a local error trap to check available modes,
  20133.  ' then assigns the integer representing the best mode for charting to its
  20134.  ' name so it is returned to the caller. The function terminate execution if
  20135.  ' the hardware doesn't support a mode appropriate for Presentation Graphics
  20136.  '========================================================================
  20137.  FUNCTION BestMode
  20138.  
  20139.  ' Set a trap for an expected local error --- handled within the function
  20140.  ON LOCAL ERROR GOTO ScreenError
  20141.  
  20142.  FOR TestValue = HIGHESTMODE TO 0 STEP -1
  20143.          DisplayError = FALSE
  20144.          SCREEN TestValue
  20145.          IF DisplayError = FALSE THEN
  20146.                  SELECT CASE TestValue
  20147.                          CASE 12, 13
  20148.                                  BestMode = 12
  20149.                          CASE 9, 10, 11
  20150.                                  BestMode = 9
  20151.                          CASE 8, 4, 3
  20152.                                  BestMode = TestValue
  20153.                          CASE 2, 7
  20154.                                  BestMode = 2
  20155.                          CASE 1
  20156.                                  BestMode = 1
  20157.                          CASE ELSE
  20158.                                  PRINT "Sorry, you need graphics to display ch
  20159.                                  END
  20160.                  END SELECT
  20161.                  EXIT FUNCTION
  20162.          END IF
  20163.  NEXT TestValue
  20164.  ' Note there is no need to turn off the local error handler. It is turned off
  20165.  ' automatically when control passes out of the function
  20166.  
  20167.  EXIT FUNCTION
  20168.  '==================== | Local error handler code |=======================
  20169.  ' The ScreenError label identifies a local error handler relied in the
  20170.  ' BestMode function. Invalid SCREEN values generate Error # 5 (Illegal
  20171.  ' function call) --- so if that is not the error reset ERROR to the ERR
  20172.  ' value that was generated so the error can be passed to other, possibly
  20173.  ' more appropriate errors.
  20174.  ScreenError:
  20175.          IF ERR = 5 THEN
  20176.                  DisplayError = TRUE
  20177.                  RESUME NEXT
  20178.          ELSE
  20179.                  ERROR ERR
  20180.          END IF
  20181.  END FUNCTION
  20182.  
  20183.  
  20184.  
  20185.  PGLINEMS.BAS
  20186.  CD-ROM Disc Path:   \SAMPCODE\BASIC\PGLINEMS.BAS
  20187.  
  20188.  ' PGLINEMS.BAS - Program to generate a simple multi-data series line chart
  20189.  
  20190.  DEFINT A-Z
  20191.  '$INCLUDE: 'CHRTB.BI'                 ' Declarations and Definitions
  20192.  DIM Env AS ChartEnvironment           ' Variable to hold environment structur
  20193.  DIM AxisLabels(1 TO 4) AS STRING      ' Array of categories
  20194.  DIM LegendLabels(1 TO 2) AS STRING    ' Array of series labels
  20195.  DIM Values(1 TO 4, 1 TO 3) AS SINGLE  ' 2-dimentsion array of values to plot
  20196.  
  20197.  DIM Col%(0 TO cPalLen)          ' Define arrays to hold values retrieved with
  20198.  DIM Lines%(0 TO cPalLen)        ' call to GetPaletteDef. By modifying these
  20199.  DIM Fill$(0 TO cPalLen)         ' values, then calling ResetPaletteDef, you
  20200.  DIM Char%(0 TO cPalLen)         ' can change colors, plot characters, borders
  20201.  DIM Bord%(0 TO cPalLen)         ' and even the line styles and fill patterns
  20202.  
  20203.  ' Read the data to display into the arrays
  20204.  
  20205.  FOR index = 1 TO 2: READ LegendLabels(index): NEXT index
  20206.  FOR index = 1 TO 4: READ AxisLabels(index): NEXT index
  20207.  
  20208.  FOR columnindex = 1 TO 2                ' The array has 2 columns, each of
  20209.    FOR rowindex = 1 TO 4                 ' which has 4 rows. Each column rep-
  20210.      READ Values(rowindex, columnindex)  ' resents 1 full data series. First,
  20211.    NEXT rowindex                         ' fill column 1, then fill column 2
  20212.  NEXT columnindex                        ' with values from the last DATA
  20213.                                          ' statement (below).
  20214.  CLS
  20215.  
  20216.  ChartScreen 2                           ' Set a common graphics mode
  20217.  
  20218.  ' Retrieve current palette settings, then assign some new values
  20219.  
  20220.  GetPaletteDef Col%(), Lines%(), Fill$(), Char%(), Bord%()
  20221.  
  20222.   Col%(2) = (15)          '  Assign white as color for second-series plot line
  20223.   Char%(1) = (4)          '  Assign  "" as plot character for 1st plot line
  20224.   Char%(2) = (18)         '  Assign  "" as plot character for 2nd plot line
  20225.  
  20226.  ' Reset the palettes with modified arrays
  20227.  
  20228.  SetPaletteDef Col%(), Lines%(), Fill$(), Char%(), Bord%()   ' Enter the chang
  20229.  
  20230.  DefaultChart Env, cLine, cLines         ' Set up multi-series line chart
  20231.  
  20232.  ' Display the chart
  20233.  
  20234.  ChartMS Env, AxisLabels(), Values(), 4, 1, 2, LegendLabels()
  20235.  
  20236.  SLEEP                                   ' Keep it onscreen until user presses
  20237.                                          ' a key
  20238.  END
  20239.  
  20240.  ' Simulated data to be shown on chart
  20241.  DATA "Qtr 1","Qtr 2"
  20242.  DATA "Admn","Markg","Prodn","Devel"
  20243.  DATA 38,30,40,32,18,40,20,12
  20244.  
  20245.  
  20246.  
  20247.  PGPIE.BAS
  20248.  CD-ROM Disc Path:   \SAMPCODE\BASIC\PGPIE.BAS
  20249.  
  20250.  ' PGPIE.BAS:  Create sample pie chart
  20251.  
  20252.  DEFINT A-Z
  20253.  ' $INCLUDE: 'fontb.BI'
  20254.  ' $INCLUDE: 'CHRTB.BI'
  20255.  CONST FALSE = 0, TRUE = NOT FALSE, MONTHS = 12
  20256.  CONST HIGHESTMODE = 13, TEXTONLY = 0
  20257.  
  20258.  DIM Env AS ChartEnvironment                 ' See CHRTB.BI for declaration of
  20259.  DIM MonthCategories(1 TO MONTHS) AS STRING  ' Array for categories
  20260.  DIM OJvalues(1 TO MONTHS) AS SINGLE         ' Array for 1st data series
  20261.  DIM Exploded(1 TO MONTHS) AS INTEGER        ' "Explode" flags array (specifie
  20262.  
  20263.  DECLARE FUNCTION BestMode ()
  20264.  
  20265.  ' Initialize the data arrays
  20266.  FOR index = 1 TO MONTHS: READ OJvalues(index): NEXT index
  20267.  FOR index = 1 TO MONTHS: READ MonthCategories$(index): NEXT index
  20268.  
  20269.  ' Set the elements of the array that determines separation of the pie slices
  20270.  FOR Flags = 1 TO MONTHS                       ' If value of OJvalues(Flags)
  20271.          Exploded(Flags) = (OJvalues(Flags) >= 100)  ' >= 100 the correspondin
  20272.  NEXT Flags                                    ' is set true, separating slice
  20273.  
  20274.  ' Pass the value returned by the BestMode function to the Presentation
  20275.  ' Graphics routine ChartScreen to set the graphics mode for charting
  20276.  
  20277.  ChartScreen (BestMode)      ' Even if SCREEN is already set to an acceptable
  20278.  
  20279.  
  20280.  IF ChartErr = cBadScreen THEN   ' Check to make sure ChartScreen succeeded
  20281.          PRINT "Sorry --- There is a screen-mode problem in the Charting libra
  20282.          END
  20283.  END IF
  20284.  
  20285.  ' Initialize a default pie chart
  20286.  
  20287.  DefaultChart Env, cPie, cPercent    ' the constant cPie (for Pie Chart) and
  20288.  
  20289.  
  20290.  ' Add Titles and some chart options. These assignments modify some default
  20291.  ' values set in the variable Env (of type ChartEnvironment) by DefaultChart
  20292.  
  20293.  
  20294.  Env.MainTitle.Title = "Good Neighbor Grocery" ' Specifies text of chart title
  20295.  Env.MainTitle.TitleColor = 15                 ' Specifies color of title text
  20296.  Env.MainTitle.Justify = cCenter               ' How to align of title text
  20297.  Env.SubTitle.Title = "Orange Juice Sales"     ' Text of chart subtitle
  20298.  Env.SubTitle.TitleColor = 11                  ' Color of subtitle text
  20299.  Env.SubTitle.Justify = cCenter                ' How to align of subtitle text
  20300.  Env.ChartWindow.Border = cYes                 ' Specifies chart has no border
  20301.  
  20302.  ' Call the pie-charting routine --- Arguments for call to ChartPie are:
  20303.  ' Env                 - Environment variable
  20304.  ' MonthCategories()   - Array containing Category labels
  20305.  ' OJvalues()          - Array containing Data values to chart
  20306.  ' Exploded()          - Integer array tells which pieces of the pie should
  20307.  '                         be separated (non-zero=exploded, 0=not exploded)
  20308.  ' MONTHS              - Tells number of data values to chart
  20309.  
  20310.          ChartPie Env, MonthCategories(), OJvalues(), Exploded(), MONTHS
  20311.          SLEEP
  20312.          '  If the rest of your program isn't graphic, reset original mode her
  20313.  END
  20314.  
  20315.  ' Simulate data generation for chart values and category labels
  20316.  DATA 33,27,42,64,106,157,182,217,128,62,43,36
  20317.  DATA "Jan","Feb","Mar","Apr","May","Jun","Jly","Aug","Sep","Oct","Nov","Dec"
  20318.  
  20319.  '============= Function to determine and set highest resolution ========
  20320.  ' The BestMode function uses a local error trap to check available modes,
  20321.  ' then assigns the integer representing the best mode for charting to its
  20322.  ' name so it is returned to the caller. The function terminate execution if
  20323.  ' the hardware doesn't support a mode appropriate for Presentation Graphics
  20324.  '========================================================================
  20325.  FUNCTION BestMode
  20326.  
  20327.  ' Set a trap for an expected local error --- handled within the function
  20328.  ON LOCAL ERROR GOTO ScreenError
  20329.  
  20330.  FOR TestValue = HIGHESTMODE TO 0 STEP -1
  20331.          DisplayError = FALSE
  20332.          SCREEN TestValue
  20333.          IF DisplayError = FALSE THEN
  20334.                  SELECT CASE TestValue
  20335.                          CASE 12, 13
  20336.                                  BestMode = 12
  20337.                          CASE 9, 10, 11
  20338.                                  BestMode = 9
  20339.                          CASE 8, 4, 3
  20340.                                  BestMode = TestValue
  20341.                          CASE 2, 7
  20342.                                  BestMode = 2
  20343.                          CASE 1
  20344.                                  BestMode = 1
  20345.                          CASE ELSE
  20346.                                  PRINT "Sorry, you need graphics to display ch
  20347.                                  END
  20348.                  END SELECT
  20349.                  EXIT FUNCTION
  20350.          END IF
  20351.  NEXT TestValue
  20352.  ' Note there is no need to turn off the local error handler. It is turned off
  20353.  ' automatically when control passes out of the function
  20354.  
  20355.  EXIT FUNCTION
  20356.  '==================== | Local error handler code |=======================
  20357.  ' The ScreenError label identifies a local error handler relied in the
  20358.  ' BestMode function. Invalid SCREEN values generate Error # 5 (Illegal
  20359.  ' function call) --- so if that is not the error reset ERROR to the ERR
  20360.  ' value that was generated so the error can be passed to other, possibly
  20361.  ' more appropriate errors.
  20362.  ScreenError:
  20363.          IF ERR = 5 THEN
  20364.                  DisplayError = TRUE
  20365.                  RESUME NEXT
  20366.          ELSE
  20367.                  ERROR ERR
  20368.          END IF
  20369.  END FUNCTION
  20370.  
  20371.  
  20372.  
  20373.  PGSCAT.BAS
  20374.  CD-ROM Disc Path:   \SAMPCODE\BASIC\PGSCAT.BAS
  20375.  
  20376.  ' PGSCAT.BAS:  Create sample scatter diagram
  20377.  
  20378.  DEFINT A-Z
  20379.  ' $INCLUDE: 'CHRTB.BI'
  20380.  CONST FALSE = 0, TRUE = NOT FALSE, MONTHS = 12
  20381.  CONST HIGHESTMODE = 13, TEXTONLY = 0
  20382.  
  20383.  DIM Env AS ChartEnvironment                 ' See CHRTB.BI for declaration of
  20384.  
  20385.  DIM OJvalues(1 TO MONTHS) AS SINGLE         ' Array for 1st data series
  20386.  DIM HCvalues(1 TO MONTHS) AS SINGLE         ' Array for 2nd data series
  20387.  DECLARE FUNCTION BestMode ()
  20388.  
  20389.  ' Initialize the data arrays
  20390.  FOR index = 1 TO MONTHS: READ OJvalues(index): NEXT index
  20391.  FOR index = 1 TO MONTHS: READ HCvalues(index): NEXT index
  20392.  
  20393.  ' Pass the value returned by the BestMode function to the Presentation
  20394.  ' Graphics routine ChartScreen to set the graphics mode for charting
  20395.  
  20396.  ChartScreen (BestMode)      ' Even if SCREEN is already set to an acceptable
  20397.  
  20398.  IF ChartErr = cBadScreen THEN   ' Check to make sure ChartScreen succeeded
  20399.          PRINT "Sorry --- There is a screen-mode problem in the Charting libra
  20400.          END
  20401.  END IF
  20402.  
  20403.  ' Initialize a default pie chart
  20404.  
  20405.  DefaultChart Env, cScatter, cNoLines  ' constant cScatter (for Scatter Chart)
  20406.  
  20407.  
  20408.  ' Add Titles and some chart options. These assignments modify some default
  20409.  ' values set in the variable Env (of type ChartEnvironment) by DefaultChart
  20410.  
  20411.  Env.MainTitle.Title = "Good Neighbor Grocery" ' Specifies text of chart title
  20412.  Env.MainTitle.TitleColor = 11                 ' Specifies color of title text
  20413.  Env.MainTitle.Justify = cRight                ' How to align of title text
  20414.  Env.SubTitle.Title = "OJ vs. Hot Chocolate"   ' Text of chart subtitle
  20415.  Env.SubTitle.TitleColor = 15                  ' Color of subtitle text
  20416.  Env.SubTitle.Justify = cRight                 ' How to align of subtitle text
  20417.  Env.ChartWindow.Border = cNo                  ' Specifies chart has no border
  20418.  
  20419.  ' The next two assignments label the x and y axes of the chart
  20420.  Env.XAxis.AxisTitle.Title = "Orange Juice Sales"
  20421.  Env.YAxis.AxisTitle.Title = "Hot Chocolate Sales"
  20422.  
  20423.  ' Call the pie-charting routine --- Arguments for call to ChartPie are:
  20424.  ' Env                 - Environment variable
  20425.  ' OJvalues            - Array containing orange-juice sales values to chart
  20426.  ' HCvalues            - Array containing hot-chocolate sales values to chart
  20427.  ' MONTHS              - Tells number of data values to chart
  20428.  
  20429.          ChartScatter Env, OJvalues(), HCvalues(), MONTHS
  20430.          SLEEP
  20431.          '  If the rest of your program isn't graphic, reset original mode her
  20432.  END
  20433.  
  20434.  ' Simulate data generation for chart values and category labels
  20435.  DATA 33,27,42,64,106,157,182,217,128,62,43,36
  20436.  DATA 37,37,30,19,10,5,2,1,7,15,28,39
  20437.  
  20438.  '============= Function to determine and set highest resolution ========
  20439.  ' The BestMode function uses a local error trap to check available modes,
  20440.  ' then assigns the integer representing the best mode for charting to its
  20441.  ' name so it is returned to the caller. The function terminate execution if
  20442.  ' the hardware doesn't support a mode appropriate for Presentation Graphics
  20443.  '========================================================================
  20444.  FUNCTION BestMode
  20445.  
  20446.  ' Set a trap for an expected local error --- handled within the function
  20447.  ON LOCAL ERROR GOTO ScreenError
  20448.  
  20449.  FOR TestValue = HIGHESTMODE TO 0 STEP -1
  20450.          DisplayError = FALSE
  20451.          SCREEN TestValue
  20452.          IF DisplayError = FALSE THEN
  20453.                  SELECT CASE TestValue
  20454.                          CASE 12, 13
  20455.                                  BestMode = 12
  20456.                          CASE 9, 10, 11
  20457.                                  BestMode = 9
  20458.                          CASE 8, 4, 3
  20459.                                  BestMode = TestValue
  20460.                          CASE 2, 7
  20461.                                  BestMode = 2
  20462.                          CASE 1
  20463.                                  BestMode = 1
  20464.                          CASE ELSE
  20465.                                  PRINT "Sorry, you need graphics to display ch
  20466.                                  END
  20467.                  END SELECT
  20468.                  EXIT FUNCTION
  20469.          END IF
  20470.  NEXT TestValue
  20471.  ' Note there is no need to turn off the local error handler. It is turned off
  20472.  ' automatically when control passes out of the function
  20473.  
  20474.  EXIT FUNCTION
  20475.  '==================== | Local error handler code |=======================
  20476.  ' The ScreenError label identifies a local error handler relied in the
  20477.  ' BestMode function. Invalid SCREEN values generate Error # 5 (Illegal
  20478.  ' function call) --- so if that is not the error reset ERROR to the ERR
  20479.  ' value that was generated so the error can be passed to other, possibly
  20480.  ' more appropriate errors.
  20481.  ScreenError:
  20482.          IF ERR = 5 THEN
  20483.                  DisplayError = TRUE
  20484.                  RESUME NEXT
  20485.          ELSE
  20486.                  ERROR ERR
  20487.          END IF
  20488.  END FUNCTION
  20489.  
  20490.  
  20491.  
  20492.  PLOTTER.BAS
  20493.  CD-ROM Disc Path:   \SAMPCODE\BASIC\PLOTTER.BAS
  20494.  
  20495.  ' Values for keys on the numeric keypad and the spacebar:
  20496.  CONST UP = 72, DOWN = 80, LFT = 75, RGHT = 77
  20497.  CONST UPLFT = 71, UPRGHT = 73, DOWNLFT = 79, DOWNRGHT = 81
  20498.  CONST SPACEBAR = " "
  20499.  
  20500.  ' Null$ is the first character of the two-character INKEY$
  20501.  ' value returned for direction keys such as UP and DOWN:
  20502.  Null$ = CHR$(0)
  20503.  ' Plot$ = "" means draw lines; Plot$ = "B" means
  20504.  ' move graphics cursor, but don't draw lines:
  20505.  Plot$ = ""
  20506.  
  20507.  PRINT "Use the cursor movement keys to draw lines."
  20508.  PRINT "Press spacebar to toggle line drawing on and off."
  20509.  PRINT "Press <ENTER> to begin. Press q to end the program."
  20510.  DO : LOOP WHILE INKEY$ = ""
  20511.  
  20512.  SCREEN 1
  20513.  
  20514.  DO
  20515.     SELECT CASE KeyVal$
  20516.        CASE Null$ + CHR$(UP)
  20517.           DRAW Plot$ + "C1 U2"
  20518.        CASE Null$ + CHR$(DOWN)
  20519.           DRAW Plot$ + "C1 D2"
  20520.        CASE Null$ + CHR$(LFT)
  20521.           DRAW Plot$ + "C2 L2"
  20522.        CASE Null$ + CHR$(RGHT)
  20523.           DRAW Plot$ + "C2 R2"
  20524.        CASE Null$ + CHR$(UPLFT)
  20525.           DRAW Plot$ + "C3 H2"
  20526.        CASE Null$ + CHR$(UPRGHT)
  20527.           DRAW Plot$ + "C3 E2"
  20528.        CASE Null$ + CHR$(DOWNLFT)
  20529.           DRAW Plot$ + "C3 G2"
  20530.        CASE Null$ + CHR$(DOWNRGHT)
  20531.           DRAW Plot$ + "C3 F2"
  20532.        CASE SPACEBAR
  20533.           IF Plot$ = "" THEN Plot$ = "B " ELSE Plot$ = ""
  20534.        CASE ELSE
  20535.           ' The user pressed some key other than one of the
  20536.           ' direction keys, the spacebar, or "q," so
  20537.           ' don't do anything.
  20538.     END SELECT
  20539.  
  20540.     KeyVal$ = INKEY$
  20541.  
  20542.  LOOP UNTIL KeyVal$ = "q"
  20543.  
  20544.  SCREEN 0, 0                ' Restore the screen to 80-column
  20545.  WIDTH 80                ' text mode and end.
  20546.  END
  20547.  
  20548.  
  20549.  
  20550.  QLBDUMP.BAS
  20551.  CD-ROM Disc Path:   \SAMPCODE\BASIC\QLBDUMP.BAS
  20552.  
  20553.  'This program prints the names of Quick library procedures.
  20554.  
  20555.  DECLARE SUB DumpSym (SymStart AS INTEGER, QHdrPos AS LONG)
  20556.  
  20557.  TYPE ExeHdr                   'Part of DOS .EXE header.
  20558.       other1    AS STRING * 8  'Other header information.
  20559.       CParHdr   AS INTEGER     'Size of header in paragraphs.
  20560.       other2    AS STRING * 10 'Other header information.
  20561.       IP         AS INTEGER    'Initial IP value.
  20562.       CS         AS INTEGER    'Initial (relative) CS value.
  20563.  END TYPE
  20564.  TYPE QBHdr                    'QLB header.
  20565.       QBHead    AS STRING * 6  'QBX specific heading.
  20566.       Magic     AS INTEGER     'Magic word: identifies file as a Quick library
  20567.       SymStart  AS INTEGER     'Offset from header to first code symbol.
  20568.       DatStart  AS INTEGER     'Offset from header to first data symbol.
  20569.  END TYPE
  20570.  
  20571.  TYPE QbSym                    'QuickLib symbol entry.
  20572.       Flags     AS INTEGER     'Symbol flags.
  20573.       NameStart AS INTEGER     'Offset into name table.
  20574.       other     AS STRING * 4  'Other header information.
  20575.  END TYPE
  20576.  
  20577.  DIM EHdr AS ExeHdr, Qhdr AS QBHdr, QHdrPos AS LONG
  20578.  
  20579.  INPUT "Enter Quick library file name: ", FileName$
  20580.  FileName$ = UCASE$(FileName$)
  20581.  IF INSTR(FileName$, ".QLB") = 0 THEN FileName$ = FileName$ + ".QLB"
  20582.  INPUT "Enter output file name or press ENTER for screen: ", OutFile$
  20583.  OutFile$ = UCASE$(OutFile$)
  20584.  IF OutFile$ = "" THEN OutFile$ = "CON"
  20585.  
  20586.  IF DIR$(FileName$) = "" THEN PRINT "File "; FileName$; " not found.": END
  20587.  
  20588.  OPEN FileName$ FOR BINARY AS #1
  20589.  OPEN OutFile$ FOR OUTPUT AS #2
  20590.  
  20591.  GET #1, , EHdr                     'Read the EXE format header.
  20592.  TEMP1& = EHdr.CParHdr + EHdr.CS    'Use a LONG temp to prevent overflow.
  20593.  QHdrPos = TEMP1& * 16 + EHdr.IP + 1
  20594.  
  20595.  GET #1, QHdrPos, Qhdr              'Read the QuickLib format header.
  20596.  IF Qhdr.Magic <> &H6C75 THEN PRINT "Not a valid QBX Quick-Library": END
  20597.  
  20598.  PRINT #2, "Code Symbols:": PRINT #2,
  20599.  DumpSym Qhdr.SymStart, QHdrPos     'Dump code symbols.
  20600.  PRINT #2,
  20601.  PRINT #2, "Data Symbols:": PRINT #2, ""
  20602.  DumpSym Qhdr.DatStart, QHdrPos     'Dump data symbols.
  20603.  PRINT #2,
  20604.  
  20605.  END
  20606.  
  20607.  SUB DumpSym (SymStart AS INTEGER, QHdrPos AS LONG)
  20608.          DIM QlbSym AS QbSym
  20609.          DIM NextSym AS LONG, CurrentSym AS LONG
  20610.  
  20611.      'Calculate the location of the first symbol entry, then read that entry.
  20612.          NextSym = QHdrPos + SymStart
  20613.          GET #1, NextSym, QlbSym
  20614.  DO
  20615.          NextSym = SEEK(1)         'Save the location of the next symbol.
  20616.                  CurrentSym = QHdrPos + QlbSym.NameStart
  20617.          SEEK #1, CurrentSym       'Use SEEK to move to the name
  20618.                                    'for the current symbol entry.
  20619.          Prospect$ = INPUT$(40, 1) 'Read the longest legal string,
  20620.                                    'plus one additional byte for
  20621.                                    'the final null character (CHR$(0)).
  20622.  
  20623.          'Extract the null-terminated name.
  20624.                  SName$ = LEFT$(Prospect$, INSTR(Prospect$, CHR$(0)))
  20625.  
  20626.          'Print only those names that do not begin with "__", "$", or "b$"
  20627.          'as these names are usually considered reserved.
  20628.                  T$ = LEFT$(SName$, 2)
  20629.                  IF T$ <> "__" AND LEFT$(SName$, 1) <> "$" AND UCASE$(T$) <> "
  20630.                          PRINT #2, "  " + SName$
  20631.                  END IF
  20632.  
  20633.          GET #1, NextSym, QlbSym    'Read a symbol entry.
  20634.      LOOP WHILE QlbSym.Flags        'Flags=0 (false) means end of table.
  20635.  
  20636.  END SUB
  20637.  
  20638.  
  20639.  REMLINE.BAS
  20640.  CD-ROM Disc Path:   \SAMPCODE\BASIC\REMLINE.BAS
  20641.  
  20642.  DEFINT A-Z
  20643.  '
  20644.  '   Microsoft RemLine - Line Number Removal Utility
  20645.  '   Copyright (C) Microsoft Corporation   - 1985, 1986, 1987
  20646.  '
  20647.  '   REMLINE.BAS is a program to remove line numbers from Microsoft BASIC
  20648.  '   Programs. It removes only those line numbers that are not the object
  20649.  '   of one of the following statements: GOSUB, RETURN, GOTO, THEN, ELSE,
  20650.  '   RESUME, RESTORE, or RUN.
  20651.  '
  20652.  '   REMLINE is run by typing
  20653.  '
  20654.  '      REMLINE [<input> [, <output>]]
  20655.  '
  20656.  '   where <input> is the name of the file to be processed and <output>
  20657.  '   is the name of the file or device to receive the reformatted output.
  20658.  '   If no extension is given, .BAS is assumed (except for output devices).
  20659.  '   If file names are not given, REMLINE prompts for file names. If both
  20660.  '   file names are the same, REMLINE saves the original file with the
  20661.  '   extension .BAK.
  20662.  '
  20663.  '   REMLINE makes several assumptions about the program:
  20664.  '
  20665.  '     1. It must be correct syntactically, and must run in BASICA or
  20666.  '        GWBASIC interpreter.
  20667.  '     2. There is a 400 line limit. To process larger files, change
  20668.  '        MaxLines constant.
  20669.  '     3. The first number encountered on a line is considered a line
  20670.  '        number; thus some continuation lines (in a compiler specific
  20671.  '        constructiion) may not be handled correctly.
  20672.  '     4. REMLINE can handle simple statements that test the ERL function
  20673.  '        using  relational operators such as =, <, and >. For example,
  20674.  '        the following statement is handled correctly:
  20675.  '
  20676.  '             IF ERL = 100 THEN END
  20677.  '
  20678.  '        Line 100 is not removed from the source code. However, more
  20679.  '        complex expressions that contain the +, -, AND, OR, XOR, EQV,
  20680.  '        MOD, or IMP operators may not be handled correctly. For example,
  20681.  '        in the following statement REMLINE does not recognize line 105
  20682.  '        as a referenced line number and removes it from the source code:
  20683.  '
  20684.  '             IF ERL + 5 = 105 THEN END
  20685.  '
  20686.  '   If you do not like the way REMLINE formats its output, you can modify
  20687.  '   the output lines in SUB GenOutFile. An example is shown in comments.
  20688.  
  20689.  ' Function and Subprogram declarations
  20690.  
  20691.  DECLARE FUNCTION GetToken$ (Search$, Delim$)
  20692.  DECLARE FUNCTION StrSpn% (InString$, Separator$)
  20693.  DECLARE FUNCTION StrBrk% (InString$, Separator$)
  20694.  DECLARE FUNCTION IsDigit% (Char$)
  20695.  DECLARE SUB GetFileNames ()
  20696.  DECLARE SUB BuildTable ()
  20697.  DECLARE SUB GenOutFile ()
  20698.  DECLARE SUB InitKeyTable ()
  20699.  
  20700.  ' Global and constant data
  20701.  
  20702.  CONST TRUE = -1
  20703.  CONST false = 0
  20704.  CONST MaxLines = 400
  20705.  
  20706.  DIM SHARED LineTable!(MaxLines)
  20707.  DIM SHARED LineCount
  20708.  DIM SHARED Seps$, InputFile$, OutputFile$, TmpFile$
  20709.  
  20710.  ' Keyword search data
  20711.  
  20712.  CONST KeyWordCount = 9
  20713.  DIM SHARED KeyWordTable$(KeyWordCount)
  20714.  
  20715.  KeyData:
  20716.     DATA THEN, ELSE, GOSUB, GOTO, RESUME, RETURN, RESTORE, RUN, ERL, ""
  20717.  
  20718.  ' Start of module-level program code
  20719.  
  20720.     Seps$ = " ,:=<>()" + CHR$(9)
  20721.     InitKeyTable
  20722.     GetFileNames
  20723.     ON ERROR GOTO FileErr1
  20724.     OPEN InputFile$ FOR INPUT AS 1
  20725.     ON ERROR GOTO 0
  20726.     COLOR 7: PRINT "Working"; : COLOR 23: PRINT " . . .": COLOR 7: PRINT
  20727.     BuildTable
  20728.     CLOSE #1
  20729.     OPEN InputFile$ FOR INPUT AS 1
  20730.     ON ERROR GOTO FileErr2
  20731.     OPEN OutputFile$ FOR OUTPUT AS 2
  20732.     ON ERROR GOTO 0
  20733.     GenOutFile
  20734.     CLOSE #1, #2
  20735.     IF OutputFile$ <> "CON" THEN CLS
  20736.  
  20737.  END
  20738.  
  20739.  FileErr1:
  20740.     CLS
  20741.     PRINT "      Invalid file name": PRINT
  20742.     INPUT "      New input file name (ENTER to terminate): ", InputFile$
  20743.     IF InputFile$ = "" THEN END
  20744.  FileErr2:
  20745.     INPUT "      Output file name (ENTER to print to screen) :", OutputFile$
  20746.     PRINT
  20747.     IF (OutputFile$ = "") THEN OutputFile$ = "CON"
  20748.     IF TmpFile$ = "" THEN
  20749.        RESUME
  20750.     ELSE
  20751.        TmpFile$ = ""
  20752.        RESUME NEXT
  20753.     END IF
  20754.  
  20755.  '
  20756.  ' BuildTable:
  20757.  '   Examines the entire text file looking for line numbers that are
  20758.  '   the object of GOTO, GOSUB, etc. As each is found, it is entered
  20759.  '   into a table of line numbers. The table is used during a second
  20760.  '   pass (see GenOutFile), when all line numbers not in the list
  20761.  '   are removed.
  20762.  ' Input:
  20763.  '   Uses globals KeyWordTable$, KeyWordCount, and Seps$
  20764.  ' Output:
  20765.  '   Modefies LineTable! and LineCount
  20766.  '
  20767.  SUB BuildTable STATIC
  20768.  
  20769.     DO WHILE NOT EOF(1)
  20770.        ' Get line and first token
  20771.        LINE INPUT #1, InLin$
  20772.        token$ = GetToken$(InLin$, Seps$)
  20773.        DO WHILE (token$ <> "")
  20774.           FOR KeyIndex = 1 TO KeyWordCount
  20775.              ' See if token is keyword
  20776.              IF (KeyWordTable$(KeyIndex) = UCASE$(token$)) THEN
  20777.                 ' Get possible line number after keyword
  20778.                 token$ = GetToken$("", Seps$)
  20779.                 ' Check each token to see if it is a line number
  20780.                 ' (the LOOP is necessary for the multiple numbers
  20781.                 ' of ON GOSUB or ON GOTO). A non-numeric token will
  20782.                 ' terminate search.
  20783.                 DO WHILE (IsDigit(LEFT$(token$, 1)))
  20784.                    LineCount = LineCount + 1
  20785.                    LineTable!(LineCount) = VAL(token$)
  20786.                    token$ = GetToken$("", Seps$)
  20787.                    IF token$ <> "" THEN KeyIndex = 0
  20788.                 LOOP
  20789.              END IF
  20790.           NEXT KeyIndex
  20791.           ' Get next token
  20792.           token$ = GetToken$("", Seps$)
  20793.        LOOP
  20794.     LOOP
  20795.  
  20796.  END SUB
  20797.  
  20798.  '
  20799.  ' GenOutFile:
  20800.  '  Generates an output file with unreferenced line numbers removed.
  20801.  ' Input:
  20802.  '  Uses globals LineTable!, LineCount, and Seps$
  20803.  ' Output:
  20804.  '  Processed file
  20805.  '
  20806.  SUB GenOutFile STATIC
  20807.  
  20808.     ' Speed up by eliminating comma and colon (can't separate first token)
  20809.     Sep$ = " " + CHR$(9)
  20810.     DO WHILE NOT EOF(1)
  20811.        LINE INPUT #1, InLin$
  20812.        IF (InLin$ <> "") THEN
  20813.           ' Get first token and process if it is a line number
  20814.           token$ = GetToken$(InLin$, Sep$)
  20815.           IF IsDigit(LEFT$(token$, 1)) THEN
  20816.              LineNumber! = VAL(token$)
  20817.              FoundNumber = false
  20818.              ' See if line number is in table of referenced line numbers
  20819.              FOR index = 1 TO LineCount
  20820.                 IF (LineNumber! = LineTable!(index)) THEN
  20821.                    FoundNumber = TRUE
  20822.                 END IF
  20823.              NEXT index
  20824.              ' Modify line strings
  20825.              IF (NOT FoundNumber) THEN
  20826.                 token$ = SPACE$(LEN(token$))
  20827.                 MID$(InLin$, StrSpn(InLin$, Sep$), LEN(token$)) = token$
  20828.              END IF
  20829.  
  20830.              ' You can replace the previous lines with your own
  20831.              ' code to reformat output. For example, try these lines:
  20832.  
  20833.              'TmpPos1 = StrSpn(InLin$, Sep$) + LEN(Token$)
  20834.              'TmpPos2 = TmpPos1 + StrSpn(MID$(InLin$, TmpPos1), Sep$)
  20835.              '
  20836.              'IF FoundNumber THEN
  20837.              '   InLin$ = LEFT$(InLin$, TmpPos1 - 1) + CHR$(9) + MID$(InLin$,
  20838.              'ELSE
  20839.              '   InLin$ = CHR$(9) + MID$(InLin$, TmpPos2)
  20840.              'END IF
  20841.  
  20842.           END IF
  20843.        END IF
  20844.        ' Print line to file or console (PRINT is faster than console device)
  20845.        IF OutputFile$ = "CON" THEN
  20846.           PRINT InLin$
  20847.        ELSE
  20848.           PRINT #2, InLin$
  20849.        END IF
  20850.     LOOP
  20851.  
  20852.  END SUB
  20853.  
  20854.  '
  20855.  ' GetFileNames:
  20856.  '  Gets a file name from COMMAND$ or by prompting the user.
  20857.  ' Input:
  20858.  '  Used Command$ or user input
  20859.  ' Output:
  20860.  '  Defines InputFiles$ and OutputFiles$
  20861.  '
  20862.  SUB GetFileNames STATIC
  20863.  
  20864.     IF (COMMAND$ = "") THEN
  20865.        CLS
  20866.        PRINT " Microsoft RemLine: Line Number Removal Utility"
  20867.        PRINT "       (.BAS assumed if no extension given)"
  20868.        PRINT
  20869.        INPUT "      Input file name (ENTER to terminate): ", InputFile$
  20870.        IF InputFile$ = "" THEN END
  20871.        INPUT "      Output file name (ENTER to print to screen): ", OutputFile
  20872.        PRINT
  20873.        IF (OutputFile$ = "") THEN OutputFile$ = "CON"
  20874.     ELSE
  20875.        InputFile$ = UCASE$(GetToken$(COMMAND$, Seps$))
  20876.        OutputFile$ = UCASE$(GetToken$("", Seps$))
  20877.        IF (OutputFile$ = "") THEN
  20878.           INPUT "      Output file name (ENTER to print to screen): ", OutputF
  20879.           PRINT
  20880.           IF (OutputFile$ = "") THEN OutputFile$ = "CON"
  20881.        END IF
  20882.     END IF
  20883.     IF INSTR(InputFile$, ".") = 0 THEN
  20884.        InputFile$ = InputFile$ + ".BAS"
  20885.     END IF
  20886.     IF INSTR(OutputFile$, ".") = 0 THEN
  20887.        SELECT CASE OutputFile$
  20888.           CASE "CON", "SCRN", "PRN", "COM1", "COM2", "LPT1", "LPT2", "LPT3"
  20889.              EXIT SUB
  20890.           CASE ELSE
  20891.              OutputFile$ = OutputFile$ + ".BAS"
  20892.        END SELECT
  20893.     END IF
  20894.     DO WHILE InputFile$ = OutputFile$
  20895.        TmpFile$ = LEFT$(InputFile$, INSTR(InputFile$, ".")) + "BAK"
  20896.        ON ERROR GOTO FileErr1
  20897.        NAME InputFile$ AS TmpFile$
  20898.        ON ERROR GOTO 0
  20899.        IF TmpFile$ <> "" THEN InputFile$ = TmpFile$
  20900.     LOOP
  20901.  
  20902.  END SUB
  20903.  
  20904.  '
  20905.  ' GetToken$:
  20906.  '  Extracts tokens from a string. A token is a word that is surrounded
  20907.  '  by separators, such as spaces or commas. Tokens are extracted and
  20908.  '  analyzed when parsing sentences or commands. To use the GetToken$
  20909.  '  function, pass the string to be parsed on the first call, then pass
  20910.  '  a null string on subsequent calls until the function returns a null
  20911.  '  to indicate that the entire string has been parsed.
  20912.  ' Input:
  20913.  '  Search$ = string to search
  20914.  '  Delim$  = String of separators
  20915.  ' Output:
  20916.  '  GetToken$ = next token
  20917.  '
  20918.  FUNCTION GetToken$ (Search$, Delim$) STATIC
  20919.  
  20920.     ' Note that SaveStr$ and BegPos must be static from call to call
  20921.     ' (other variables are only static for efficiency).
  20922.     ' If first call, make a copy of the string
  20923.     IF (Search$ <> "") THEN
  20924.        BegPos = 1
  20925.        SaveStr$ = Search$
  20926.     END IF
  20927.  
  20928.     ' Find the start of the next token
  20929.     NewPos = StrSpn(MID$(SaveStr$, BegPos, LEN(SaveStr$)), Delim$)
  20930.     IF NewPos THEN
  20931.        ' Set position to start of token
  20932.        BegPos = NewPos + BegPos - 1
  20933.     ELSE
  20934.        ' If no new token, quit and return null
  20935.        GetToken$ = ""
  20936.        EXIT FUNCTION
  20937.     END IF
  20938.  
  20939.     ' Find end of token
  20940.     NewPos = StrBrk(MID$(SaveStr$, BegPos, LEN(SaveStr$)), Delim$)
  20941.     IF NewPos THEN
  20942.        ' Set position to end of token
  20943.        NewPos = BegPos + NewPos - 1
  20944.     ELSE
  20945.        ' If no end of token, return set to end a value
  20946.        NewPos = LEN(SaveStr$) + 1
  20947.     END IF
  20948.     ' Cut token out of search string
  20949.     GetToken$ = MID$(SaveStr$, BegPos, NewPos - BegPos)
  20950.     ' Set new starting position
  20951.     BegPos = NewPos
  20952.  
  20953.  END FUNCTION
  20954.  
  20955.  '
  20956.  ' InitKeyTable:
  20957.  '  Initializes a keyword table. Keywords must be recognized so that
  20958.  '  line numbers can be distinguished from numeric constants.
  20959.  ' Input:
  20960.  '  Uses KeyData
  20961.  ' Output:
  20962.  '  Modifies global array KeyWordTable$
  20963.  '
  20964.  SUB InitKeyTable STATIC
  20965.  
  20966.     RESTORE KeyData
  20967.     FOR Count = 1 TO KeyWordCount
  20968.        READ KeyWord$
  20969.        KeyWordTable$(Count) = KeyWord$
  20970.     NEXT
  20971.  
  20972.  END SUB
  20973.  
  20974.  '
  20975.  ' IsDigit:
  20976.  '  Returns true if character passed is a decimal digit. Since any
  20977.  '  BASIC token starting with a digit is a number, the function only
  20978.  '  needs to check the first digit. Doesn't check for negative numbers,
  20979.  '  but that's not needed here.
  20980.  ' Input:
  20981.  '  Char$ - initial character of string to check
  20982.  ' Output:
  20983.  '  IsDigit - true if within 0 - 9
  20984.  '
  20985.  FUNCTION IsDigit (Char$) STATIC
  20986.  
  20987.     IF (Char$ = "") THEN
  20988.        IsDigit = false
  20989.     ELSE
  20990.        CharAsc = ASC(Char$)
  20991.        IsDigit = (CharAsc >= ASC("0")) AND (CharAsc <= ASC("9"))
  20992.     END IF
  20993.  
  20994.  END FUNCTION
  20995.  
  20996.  '
  20997.  ' StrBrk:
  20998.  '  Searches InString$ to find the first character from among those in
  20999.  '  Separator$. Returns the index of that character. This function can
  21000.  '  be used to find the end of a token.
  21001.  ' Input:
  21002.  '  InString$ = string to search
  21003.  '  Separator$ = characters to search for
  21004.  ' Output:
  21005.  '  StrBrk = index to first match in InString$ or 0 if none match
  21006.  '
  21007.  FUNCTION StrBrk (InString$, Separator$) STATIC
  21008.  
  21009.     Ln = LEN(InString$)
  21010.     BegPos = 1
  21011.     ' Look for end of token (first character that is a delimiter).
  21012.     DO WHILE INSTR(Separator$, MID$(InString$, BegPos, 1)) = 0
  21013.        IF BegPos > Ln THEN
  21014.           StrBrk = 0
  21015.           EXIT FUNCTION
  21016.        ELSE
  21017.           BegPos = BegPos + 1
  21018.        END IF
  21019.     LOOP
  21020.     StrBrk = BegPos
  21021.  
  21022.  END FUNCTION
  21023.  
  21024.  '
  21025.  ' StrSpn:
  21026.  '  Searches InString$ to find the first character that is not one of
  21027.  '  those in Separator$. Returns the index of that character. This
  21028.  '  function can be used to find the start of a token.
  21029.  ' Input:
  21030.  '  InString$ = string to search
  21031.  '  Separator$ = characters to search for
  21032.  ' Output:
  21033.  '  StrSpn = index to first nonmatch in InString$ or 0 if all match
  21034.  '
  21035.  FUNCTION StrSpn% (InString$, Separator$) STATIC
  21036.  
  21037.     Ln = LEN(InString$)
  21038.     BegPos = 1
  21039.     ' Look for start of a token (character that isn't a delimiter).
  21040.     DO WHILE INSTR(Separator$, MID$(InString$, BegPos, 1))
  21041.        IF BegPos > Ln THEN
  21042.           StrSpn = 0
  21043.           EXIT FUNCTION
  21044.        ELSE
  21045.           BegPos = BegPos + 1
  21046.        END IF
  21047.     LOOP
  21048.     StrSpn = BegPos
  21049.  
  21050.  END FUNCTION
  21051.  
  21052.  
  21053.  
  21054.  SINEWAVE.BAS
  21055.  CD-ROM Disc Path:   \SAMPCODE\BASIC\SINEWAVE.BAS
  21056.  
  21057.  SCREEN 2
  21058.  
  21059.  ' Viewport sized to proper scale for graph:
  21060.  VIEW (20, 2)-(620, 172), , 1
  21061.  CONST PI = 3.141592653589#
  21062.  
  21063.  ' Make window large enough to graph sine wave from
  21064.  ' 0 radians to pi radians:
  21065.  WINDOW (0, -1.1)-(2 * PI, 1.1)
  21066.  Style% = &HFF00         ' Use to make dashed line.
  21067.  VIEW PRINT 23 TO 24  ' Scroll printed output in rows 23, 24.
  21068.  DO
  21069.          PRINT TAB(20);
  21070.          INPUT "Number of cycles (0 to end): ", Cycles
  21071.          CLS
  21072.          LINE (2 * PI, 0)-(0, 0), , , Style%  ' Draw the x axis.
  21073.          IF Cycles > 0 THEN
  21074.  
  21075.                  '  Start at (0,0) and plot the graph:
  21076.                  FOR X = 0 TO 2 * PI STEP .01
  21077.           Y = SIN(Cycles * X) ' Calculate the y coordinate.
  21078.           LINE -(X, Y)     ' Draw a line to new point.
  21079.                  NEXT X
  21080.          END IF
  21081.  LOOP WHILE Cycles > 0
  21082.  
  21083.  
  21084.  
  21085.  STRTONUM.BAS
  21086.  CD-ROM Disc Path:   \SAMPCODE\BASIC\STRTONUM.BAS
  21087.  
  21088.  DECLARE FUNCTION Filter$ (Txt$, FilterString$)
  21089.  
  21090.  ' Input a line:
  21091.  LINE INPUT "Enter a number with commas: "; A$
  21092.  
  21093.  ' Look only for valid numeric characters (0123456789.-)
  21094.  ' in the input string:
  21095.  CleanNum$ = Filter$(A$, "0123456789.-")
  21096.  
  21097.  ' Convert the string to a number:
  21098.  PRINT "The number's value = "; VAL(CleanNum$)
  21099.  END
  21100.  
  21101.  ' ========================== FILTER =======================
  21102.  '         Takes unwanted characters out of a string by
  21103.  '         comparing them with a filter string containing
  21104.  '         only acceptable numeric characters
  21105.  ' =========================================================
  21106.  
  21107.  FUNCTION Filter$ (Txt$, FilterString$) STATIC
  21108.     Temp$ = ""
  21109.     TxtLength = LEN(Txt$)
  21110.  
  21111.     FOR I = 1 TO TxtLength     ' Isolate each character in
  21112.        C$ = MID$(Txt$, I, 1)   ' the string.
  21113.  
  21114.        ' If the character is in the filter string, save it:
  21115.        IF INSTR(FilterString$, C$) <> 0 THEN
  21116.           Temp$ = Temp$ + C$
  21117.        END IF
  21118.     NEXT I
  21119.  
  21120.     Filter$ = Temp$
  21121.  END FUNCTION
  21122.  
  21123.  
  21124.  
  21125.  TERMINAL.BAS
  21126.  CD-ROM Disc Path:   \SAMPCODE\BASIC\TERMINAL.BAS
  21127.  
  21128.  DEFINT A-Z
  21129.  
  21130.  DECLARE        SUB Filter (InString$)
  21131.  
  21132.  COLOR 7, 1                        ' Set screen color.
  21133.  CLS
  21134.  
  21135.  Quit$ = CHR$(0) + CHR$(16)        ' Value returned by INKEY$
  21136.                                  ' when ALT+q is pressed.
  21137.  
  21138.  ' Set up prompt on bottom line of screen and turn cursor on:
  21139.  LOCATE 24, 1, 1
  21140.  PRINT STRING$(80, "_");
  21141.  LOCATE 25, 1
  21142.  PRINT TAB(30); "Press ALT+q to quit";
  21143.  
  21144.  VIEW PRINT 1 TO        23                ' Print between lines 1 & 23.
  21145.  
  21146.  ' Open communications (1200 baud, no parity, 8-bit data,
  21147.  ' 1 stop bit, 256-byte input buffer):
  21148.  OPEN "COM1:1200,N,8,1" FOR RANDOM AS #1        LEN = 256
  21149.  
  21150.  DO                                ' Main communications loop.
  21151.  
  21152.     KeyInput$ = INKEY$                ' Check the keyboard.
  21153.  
  21154.     IF KeyInput$        = Quit$        THEN        ' Exit the loop if the user
  21155.        EXIT DO                        ' pressed ALT+q.
  21156.  
  21157.     ELSEIF KeyInput$ <> "" THEN        ' Otherwise, if the user has
  21158.        PRINT #1,        KeyInput$;        ' pressed a key, send the
  21159.     END IF                        ' character typed to modem.
  21160.   ' Check the modem. If characters are waiting (EOF(1) is
  21161.   ' true), get them and print them to the screen:
  21162.   IF NOT EOF(1) THEN
  21163.  
  21164.        ' LOC(1) gives the number of characters waiting:
  21165.        ModemInput$ = INPUT$(LOC(1), #1)
  21166.  
  21167.        Filter ModemInput$        ' Filter out line feeds and
  21168.        PRINT ModemInput$;        ' backspaces, then print.
  21169.     END IF
  21170.  LOOP
  21171.  
  21172.  CLOSE                                ' End communications.
  21173.  CLS
  21174.  END
  21175.  '
  21176.  ' ========================= FILTER ========================
  21177.  '               Filters characters in an input string
  21178.  ' =========================================================
  21179.  '
  21180.  SUB Filter (InString$) STATIC
  21181.  
  21182.     ' Look for backspace characters and recode
  21183.     ' them to CHR$(29) (the LEFT cursor key):
  21184.     DO
  21185.        BackSpace = INSTR(InString$, CHR$(8))
  21186.        IF BackSpace THEN
  21187.        MID$(InString$, BackSpace) = CHR$(29)
  21188.        END IF
  21189.     LOOP WHILE BackSpace
  21190.  
  21191.     ' Look for line-feed characters and
  21192.     ' remove any found:
  21193.     DO
  21194.        LnFd = INSTR(InString$, CHR$(10))
  21195.        IF LnFd THEN
  21196.     InString$=LEFT$(InString$,LnFd-1)+MID$(InString$,LnFd+1)
  21197.        END IF
  21198.     LOOP WHILE LnFd
  21199.  
  21200.  END SUB
  21201.  
  21202.  
  21203.  
  21204.  TIMER.BAS
  21205.  CD-ROM Disc Path:   \SAMPCODE\BASIC\TIMER.BAS
  21206.  
  21207.  ' Declare external MASM procedures.
  21208.  DECLARE SUB SetInt
  21209.  DECLARE SUB RestInt
  21210.  
  21211.  ' Install new interrupt service routine.
  21212.  CALL SetInt
  21213.  
  21214.  ' Set up the BASIC event handler.
  21215.  ON UEVENT GOSUB SpecialTask
  21216.  UEVENT ON
  21217.  
  21218.  DO
  21219.  ' Normal program operation occurs here.
  21220.  ' Program ends when any key is pressed.
  21221.  LOOP UNTIL INKEY$ <> ""
  21222.  
  21223.  ' Restore old interrupt service routine before quitting.
  21224.  CALL RestInt
  21225.  
  21226.  END
  21227.  
  21228.  ' Program branches here every 4.5 seconds.
  21229.  SpecialTask:
  21230.  ' Code for performing the special task goes here, for example:
  21231.  PRINT "Arrived here after 4.5 seconds."
  21232.  RETURN
  21233.  
  21234.  
  21235.  
  21236.  TIMERA.ASM
  21237.  CD-ROM Disc Path:   \SAMPCODE\BASIC\TIMERA.ASM
  21238.  
  21239.  ;*************************  TIMERA.ASM  ******************************
  21240.  ; This program, along with TIMER.BAS, makes use of the BASIC SetUEvent
  21241.  ; routine to print a message on the screen every 4.5 seconds.
  21242.  ; This file has three procedures.  SetInt sets up the new DOS interrupt
  21243.  ; vector.  EventHandler increments a counter 18 times a second and
  21244.  ; notifies BASIC when 4.5 seconds have elapsed.  RestInt restores the
  21245.  ; old interrupt vector.
  21246.  
  21247.              .model  medium, basic           ;Stay compatible with BASIC.
  21248.              .code
  21249.  SetInt      proc    uses ds                 ;Get old interrupt vector
  21250.              mov     ax, 351CH               ;and save it.
  21251.                          int        21h
  21252.              mov     word ptr cs:OldVector, bx
  21253.              mov     word ptr cs:OldVector + 2, es
  21254.  
  21255.              push    cs                      ;Set the new
  21256.              pop ds                          ;interrupt vector
  21257.              lea dx, EventHandler            ;to the address
  21258.              mov ax, 251CH                   ;of our service
  21259.              int 21H                         ;routine.
  21260.                          ret
  21261.  SetInt      endp
  21262.  
  21263.  public  EventHandler                        ;Make this routine
  21264.                                              ;public for debugging--
  21265.  EventHandler    proc                        ;it will check to see if
  21266.                  extrn   SetUEvent: proc     ;4.5 seconds have passed.
  21267.  
  21268.              push    bx
  21269.              lea     bx, TimerTicks
  21270.              inc     byte ptr cs:[bx]        ;Have 4.5 seconds elapsed?
  21271.              cmp     byte ptr cs:[bx], 82
  21272.              jnz     Continue
  21273.              mov     byte ptr cs:[bx], 0     ;If true, reset counter,
  21274.              push    ax                      ;save registers, and
  21275.              push    cx                      ;have BASIC set the
  21276.              push    dx                      ;user event flag.
  21277.              push    es
  21278.              call    SetUevent
  21279.              pop     es
  21280.              pop     dx                      ;Restore registers.
  21281.              pop     cx
  21282.              pop     ax
  21283.  Continue:
  21284.              pop     bx
  21285.              jmp     cs:OldVector            ;Continue on with the
  21286.                                              ;old service routine.
  21287.  
  21288.  TimerTicks  db      0                       ;Keep data in code segment
  21289.  OldVector   dd      0                       ;where it can be found no
  21290.                                              ;matter where in memory the
  21291.  EventHandler    endp                        ;interrupt occurs.
  21292.  
  21293.  RestInt     proc    uses ds                 ;Restore the old
  21294.              lds     dx, cs:OldVector        ;interrupt vector
  21295.              mov     ax, 251CH               ;so things will
  21296.              int     21h                     ;keep working when
  21297.              ret                             ;this BASIC program is
  21298.  RestInt     endp                            ;finished.
  21299.                          end
  21300.  
  21301.  
  21302.  TOKEN.BAS
  21303.  CD-ROM Disc Path:   \SAMPCODE\BASIC\TOKEN.BAS
  21304.  
  21305.  ' TOKEN.BAS
  21306.  '
  21307.  ' Demonstrates a BASIC version of the strtok C function.
  21308.  '
  21309.  DECLARE FUNCTION StrTok$(Source$,Delimiters$)
  21310.  
  21311.  LINE INPUT "Enter string: ",P$
  21312.  ' Set up the characters that separate tokens.
  21313.  Delimiters$=" ,;:().?"+CHR$(9)+CHR$(34)
  21314.  ' Invoke StrTok$ with the string to tokenize.
  21315.  Token$=StrTok$(P$,Delimiters$)
  21316.  WHILE Token$<>""
  21317.     PRINT Token$
  21318.     ' Call StrTok$ with a null string so it knows this
  21319.     ' isn't the first call.
  21320.     Token$=StrTok$("",Delimiters$)
  21321.  WEND
  21322.  
  21323.  FUNCTION StrTok$(Srce$,Delim$)
  21324.  STATIC Start%, SaveStr$
  21325.  
  21326.     ' If first call, make a copy of the string.
  21327.     IF Srce$<>"" THEN
  21328.        Start%=1 : SaveStr$=Srce$
  21329.     END IF
  21330.  
  21331.     BegPos%=Start% : Ln%=LEN(SaveStr$)
  21332.     ' Look for start of a token (character that isn't delimiter).
  21333.     WHILE BegPos%<=Ln% AND INSTR(Delim$,MID$(SaveStr$,BegPos%,1))<>0
  21334.        BegPos%=BegPos%+1
  21335.     WEND
  21336.     ' Test for token start found.
  21337.     IF BegPos% > Ln% THEN
  21338.        StrTok$="" : EXIT FUNCTION
  21339.     END IF
  21340.     ' Find the end of the token.
  21341.     EndPos%=BegPos%
  21342.     WHILE EndPos% <= Ln% AND INSTR(Delim$,MID$(SaveStr$,EndPos%,1))=0
  21343.        EndPos%=EndPos%+1
  21344.     WEND
  21345.     StrTok$=MID$(SaveStr$,BegPos%,EndPos%-BegPos%)
  21346.     ' Set starting point for search for next token.
  21347.     Start%=EndPos%
  21348.  
  21349.  END FUNCTION
  21350.  
  21351.  
  21352.  TORUS.BAS
  21353.  CD-ROM Disc Path:   \SAMPCODE\BASIC\TORUS.BAS
  21354.  
  21355.  ' ======================================================================
  21356.  '                                TORUS
  21357.  '   This program draws a Torus figure. The program accepts user input
  21358.  '   to specify various TORUS parameters. It checks the current system
  21359.  '   configuration and takes appropriate action to set the best possible
  21360.  '   initial mode.
  21361.  ' ======================================================================
  21362.  
  21363.  DEFINT A-Z
  21364.  DECLARE SUB GetConfig ()
  21365.  DECLARE SUB SetPalette ()
  21366.  DECLARE SUB TorusDefine ()
  21367.  DECLARE SUB TorusCalc (T() AS ANY)
  21368.  DECLARE SUB TorusColor (T() AS ANY)
  21369.  DECLARE SUB TorusSort (Low, High)
  21370.  DECLARE SUB TorusDraw (T() AS ANY, Index())
  21371.  DECLARE SUB TileDraw (T AS ANY)
  21372.  DECLARE SUB TorusRotate (First)
  21373.  DECLARE SUB Delay (Seconds!)
  21374.  DECLARE SUB CountTiles (T1, T2)
  21375.  DECLARE SUB Message (Text$)
  21376.  DECLARE SUB SetConfig (mode)
  21377.  DECLARE FUNCTION Inside (T AS ANY)
  21378.  DECLARE FUNCTION DegToRad! (Degrees)
  21379.  DECLARE FUNCTION Rotated (Lower, Upper, Current, Inc)
  21380.  
  21381.  ' General purpose constants
  21382.  CONST PI = 3.14159
  21383.  CONST TRUE = -1, FALSE = 0
  21384.  CONST BACK = 0
  21385.  CONST TROW = 24, TCOL = 60
  21386.  
  21387.  ' Rotation flags
  21388.  CONST RNDM = -1
  21389.  CONST START = 0
  21390.  CONST CONTINUE = 1
  21391.  
  21392.  ' Constants for best available screen mode
  21393.  CONST VGA = 12
  21394.  CONST MCGA = 13
  21395.  CONST EGA256 = 9
  21396.  CONST EGA64 = 8
  21397.  CONST MONO = 10
  21398.  CONST HERC = 3
  21399.  CONST CGA = 1
  21400.  
  21401.  ' User-defined type for tiles - an array of these make a torus
  21402.  TYPE Tile
  21403.     x1    AS SINGLE
  21404.     x2    AS SINGLE
  21405.     x3    AS SINGLE
  21406.     x4    AS SINGLE
  21407.     y1    AS SINGLE
  21408.     y2    AS SINGLE
  21409.     y3    AS SINGLE
  21410.     y4    AS SINGLE
  21411.     z1    AS SINGLE
  21412.     xc    AS SINGLE
  21413.     yc    AS SINGLE
  21414.     TColor AS INTEGER
  21415.  END TYPE
  21416.  
  21417.  ' User-defined type to hold information about the mode
  21418.  TYPE Config
  21419.     Scrn     AS INTEGER
  21420.     Colors   AS INTEGER
  21421.     Atribs   AS INTEGER
  21422.     XPix     AS INTEGER
  21423.     YPix     AS INTEGER
  21424.     TCOL     AS INTEGER
  21425.     TROW     AS INTEGER
  21426.  END TYPE
  21427.  
  21428.  DIM VC AS Config
  21429.  
  21430.  ' User-defined type to hold information about current Torus
  21431.  TYPE TORUS
  21432.     Panel    AS INTEGER
  21433.     Sect     AS INTEGER
  21434.     Thick    AS SINGLE
  21435.     XDegree  AS INTEGER
  21436.     YDegree  AS INTEGER
  21437.     Bord     AS STRING * 3
  21438.     Delay    AS SINGLE
  21439.  END TYPE
  21440.  
  21441.  DIM TOR AS TORUS, Max AS INTEGER
  21442.  
  21443.  ' A palette of colors to paint with
  21444.  DIM Pal(0 TO 300) AS LONG
  21445.  
  21446.  ' Error variables to check screen type
  21447.  DIM InitRows AS INTEGER, BestMode AS INTEGER, Available AS STRING
  21448.  
  21449.  ' The code of the module-level program begins here
  21450.  
  21451.     ' Initialize defaults
  21452.     TOR.Thick = 3: TOR.Bord = "YES"
  21453.     TOR.Panel = 8: TOR.Sect = 14
  21454.     TOR.XDegree = 60: TOR.YDegree = 165
  21455.  
  21456.     ' Get best configuration and set initial graphics mode to it
  21457.     GetConfig
  21458.     VC.Scrn = BestMode
  21459.  
  21460.     DO WHILE TRUE           ' Loop forever (exit is from within a SUB)
  21461.  
  21462.        ' Get Torus definition from user
  21463.        TorusDefine
  21464.  
  21465.        ' Dynamically dimension arrays
  21466.        DO
  21467.           Tmp = TOR.Panel
  21468.           Max = TOR.Panel * TOR.Sect
  21469.  
  21470.           ' Array for indexes
  21471.           REDIM Index(0 TO Max - 1) AS INTEGER
  21472.           ' Turn on error trap for insufficient memory
  21473.           ON ERROR GOTO MemErr
  21474.           ' Array for tiles
  21475.           REDIM T(0 TO Max - 1) AS Tile
  21476.           ON ERROR GOTO 0
  21477.        LOOP UNTIL Tmp = TOR.Panel
  21478.  
  21479.        ' Initialize array of indexes
  21480.        FOR Til = 0 TO Max - 1
  21481.           Index(Til) = Til
  21482.        NEXT
  21483.  
  21484.        ' Calculate the points of each tile on the torus
  21485.        Message "Calculating"
  21486.        TorusCalc T()
  21487.  
  21488.        ' Color each tile in the torus.
  21489.        TorusColor T()
  21490.  
  21491.        ' Sort the tiles by their "distance" from the screen
  21492.        Message "Sorting"
  21493.        TorusSort 0, Max - 1
  21494.  
  21495.        ' Set the screen mode
  21496.        SCREEN VC.Scrn
  21497.  
  21498.        ' Mix a palette of colors
  21499.        SetPalette
  21500.  
  21501.        ' Set logical window with variable thickness
  21502.        ' Center is 0, up and right are positive, down and left are negative
  21503.        WINDOW (-(TOR.Thick + 1), -(TOR.Thick + 1))-(TOR.Thick + 1, TOR.Thick +
  21504.  
  21505.        ' Draw and paint the tiles, the farthest first and nearest last
  21506.        TorusDraw T(), Index()
  21507.  
  21508.        ' Rotate the torus by rotating the color palette
  21509.        DO WHILE INKEY$ = ""
  21510.           Delay (TOR.Delay)
  21511.           TorusRotate CONTINUE
  21512.        LOOP
  21513.        SCREEN 0
  21514.        WIDTH 80
  21515.     LOOP
  21516.  
  21517.     ' Restore original rows
  21518.     WIDTH 80, InitRows
  21519.  
  21520.  END
  21521.  
  21522.  ' Error trap to make torus screen independent
  21523.  VideoErr:
  21524.     SELECT CASE BestMode    ' Fall through until something works
  21525.        CASE VGA
  21526.           BestMode = MCGA
  21527.           Available = "12BD"
  21528.        CASE MCGA
  21529.           BestMode = EGA256
  21530.           Available = "12789"
  21531.        CASE EGA256
  21532.           BestMode = CGA
  21533.           Available = "12"
  21534.        CASE CGA
  21535.           BestMode = MONO
  21536.           Available = "A"
  21537.        CASE MONO
  21538.           BestMode = HERC
  21539.           Available = "3"
  21540.        CASE ELSE
  21541.           PRINT "Sorry. Graphics not available. Can't run Torus."
  21542.           END
  21543.     END SELECT
  21544.     RESUME
  21545.  
  21546.  ' Trap to detect 64K EGA
  21547.  EGAErr:
  21548.     BestMode = EGA64
  21549.     Available = "12789"
  21550.     RESUME NEXT
  21551.  
  21552.  ' Trap to detect insufficient memory for large Torus
  21553.  MemErr:
  21554.     LOCATE 22, 1
  21555.     PRINT "Out of memory"
  21556.     PRINT "Reducing panels from"; TOR.Panel; "to"; TOR.Panel - 1
  21557.     PRINT "Reducing sections from"; TOR.Sect; "to"; TOR.Sect - 1;
  21558.     DO WHILE INKEY$ = "": LOOP
  21559.     TOR.Panel = TOR.Panel - 1
  21560.     TOR.Sect = TOR.Sect - 1
  21561.     RESUME NEXT
  21562.  
  21563.  ' Trap to determine initial number of rows so they can be restored
  21564.  RowErr:
  21565.     IF InitRows = 50 THEN
  21566.        InitRows = 43
  21567.        RESUME
  21568.     ELSE
  21569.        InitRows = 25
  21570.        RESUME NEXT
  21571.     END IF
  21572.  
  21573.  ' ============================ CountTiles ==============================
  21574.  '   Displays number of the tiles currently being calculated or sorted.
  21575.  ' ======================================================================
  21576.  '
  21577.  SUB CountTiles (T1, T2) STATIC
  21578.  
  21579.     ' Erase previous
  21580.     LOCATE TROW - 1, TCOL: PRINT SPACE$(19);
  21581.     ' If positive, display - give negative values to erase
  21582.     IF T1 > 0 AND T2 > 0 THEN
  21583.        LOCATE TROW - 1, TCOL
  21584.        PRINT "Tile ";
  21585.        PRINT USING " ###"; T1;
  21586.        PRINT USING " ###"; T2;
  21587.     END IF
  21588.  
  21589.  END SUB
  21590.  
  21591.  ' ============================ DegToRad ================================
  21592.  '   Convert degrees to radians, since BASIC trigonometric functions
  21593.  '   require radians.
  21594.  ' ======================================================================
  21595.  '
  21596.  FUNCTION DegToRad! (Degrees) STATIC
  21597.  
  21598.     DegToRad! = (Degrees * 2 * PI) / 360
  21599.  
  21600.  END FUNCTION
  21601.  
  21602.  ' =============================== Delay ================================
  21603.  '   Delay based on time so that wait will be the same on any processor.
  21604.  '   Notice the check for negative numbers so that the delay won't
  21605.  '   freeze at midnight when the delay could become negative.
  21606.  ' ======================================================================
  21607.  '
  21608.  SUB Delay (Seconds!) STATIC
  21609.  
  21610.     Begin! = TIMER
  21611.     DO UNTIL (TIMER - Begin! > Seconds!) OR (TIMER - Begin! < 0)
  21612.     LOOP
  21613.  
  21614.  END SUB
  21615.  
  21616.  ' ============================ GetConfig ===============================
  21617.  '   Get the starting number of lines and the video adapter.
  21618.  ' ======================================================================
  21619.  '
  21620.  SUB GetConfig STATIC
  21621.  SHARED InitRows AS INTEGER, BestMode AS INTEGER, Available AS STRING
  21622.  
  21623.     ' Assume 50 line display and fall through error
  21624.     ' until we get the actual number
  21625.     InitRows = 50
  21626.     ON ERROR GOTO RowErr
  21627.     LOCATE InitRows, 1
  21628.  
  21629.     ' Assume best possible screen mode
  21630.     BestMode = VGA
  21631.     Available = "12789BCD"
  21632.  
  21633.     ON ERROR GOTO VideoErr
  21634.     ' Fall through error trap until a mode works
  21635.     SCREEN BestMode
  21636.     ' If EGA, then check pages to see whether more than 64K
  21637.     ON ERROR GOTO EGAErr
  21638.     IF BestMode = EGA256 THEN SCREEN 8, , 1
  21639.  
  21640.     ON ERROR GOTO 0
  21641.  
  21642.     ' Reset text mode
  21643.     SCREEN 0, , 0
  21644.     WIDTH 80, 25
  21645.  
  21646.  END SUB
  21647.  
  21648.  ' ============================== Inside ================================
  21649.  '   Finds a point, T.xc and T.yc, that is mathematically within a tile.
  21650.  '   Then check to see if the point is actually inside. Because of the
  21651.  '   jagged edges of tiles, the center point is often actually inside
  21652.  '   very thin tiles. Such tiles will not be painted, This causes
  21653.  '   imperfections that are often visible at the edge of the Torus.
  21654.  '
  21655.  '   Return FALSE if a center point is not found inside a tile.
  21656.  ' ======================================================================
  21657.  '
  21658.  FUNCTION Inside (T AS Tile) STATIC
  21659.  SHARED VC AS Config
  21660.  DIM Highest AS SINGLE, Lowest AS SINGLE
  21661.  
  21662.     Border = VC.Atribs - 1
  21663.  
  21664.     ' Find an inside point. Since some tiles are triangles, the
  21665.     ' diagonal center isn't good enough. Instead find the center
  21666.     ' by drawing a diagonal from the center of the outside to
  21667.     ' a bottom corner.
  21668.     T.xc = T.x2 + ((T.x3 + (T.x4 - T.x3) / 2 - T.x2) / 2)
  21669.     T.yc = T.y2 + ((T.y3 + (T.y4 - T.y3) / 2 - T.y2) / 2)
  21670.  
  21671.     ' If we're on a border, no need to fill
  21672.     IF POINT(T.xc, T.yc) = Border THEN
  21673.        Inside = FALSE
  21674.        EXIT FUNCTION
  21675.     END IF
  21676.  
  21677.     ' Find highest and lowest Y on the tile
  21678.     Highest = T.y1
  21679.     Lowest = T.y1
  21680.     IF T.y2 > Highest THEN Highest = T.y2
  21681.     IF T.y2 < Lowest THEN Lowest = T.y2
  21682.     IF T.y3 > Highest THEN Highest = T.y3
  21683.     IF T.y3 < Lowest THEN Lowest = T.y3
  21684.     IF T.y4 > Highest THEN Highest = T.y4
  21685.     IF T.y4 < Lowest THEN Lowest = T.y4
  21686.  
  21687.     ' Convert coordinates to pixels
  21688.     X = PMAP(T.xc, 0)
  21689.     YU = PMAP(T.yc, 1)
  21690.     YD = YU
  21691.     H = PMAP(Highest, 1)
  21692.     L = PMAP(Lowest, 1)
  21693.  
  21694.     ' Search for top and bottom tile borders until we either find them
  21695.     ' both, or check beyond the highest and lowest points.
  21696.  
  21697.     IsUp = FALSE
  21698.     IsDown = FALSE
  21699.  
  21700.     DO
  21701.        YU = YU - 1
  21702.        YD = YD + 1
  21703.  
  21704.        ' Search up
  21705.        IF NOT IsUp THEN
  21706.           IF POINT(T.xc, PMAP(YU, 3)) = Border THEN IsUp = TRUE
  21707.        END IF
  21708.  
  21709.        ' Search down
  21710.        IF NOT IsDown THEN
  21711.           IF POINT(T.xc, PMAP(YD, 3)) = Border THEN IsDown = TRUE
  21712.        END IF
  21713.  
  21714.        ' If top and bottom are found, we're inside
  21715.        IF IsUp AND IsDown THEN
  21716.           Inside = TRUE
  21717.           EXIT FUNCTION
  21718.        END IF
  21719.  
  21720.     LOOP UNTIL (YD > L) AND (YU < H)
  21721.     Inside = FALSE
  21722.  
  21723.  END FUNCTION
  21724.  
  21725.  ' ============================= Message ================================
  21726.  '   Displays a status message followed by blinking dots.
  21727.  ' ======================================================================
  21728.  '
  21729.  SUB Message (Text$) STATIC
  21730.  SHARED VC AS Config
  21731.  
  21732.     LOCATE TROW, TCOL: PRINT SPACE$(19);
  21733.     LOCATE TROW, TCOL
  21734.     COLOR 7       ' White
  21735.     PRINT Text$;
  21736.     COLOR 23      ' Blink
  21737.     PRINT " . . .";
  21738.     COLOR 7       ' White
  21739.  
  21740.  END SUB
  21741.  
  21742.  ' ============================ Rotated =================================
  21743.  '   Returns the Current value adjusted by Inc and rotated if necessary
  21744.  '   so that it falls within the range of Lower and Upper.
  21745.  ' ======================================================================
  21746.  '
  21747.  FUNCTION Rotated (Lower, Upper, Current, Inc)
  21748.  
  21749.     ' Calculate the next value
  21750.     Current = Current + Inc
  21751.  
  21752.     ' Handle special cases of rotating off top or bottom
  21753.     IF Current > Upper THEN Current = Lower
  21754.     IF Current < Lower THEN Current = Upper
  21755.     Rotated = Current
  21756.  
  21757.  END FUNCTION
  21758.  
  21759.  ' ============================ SetConfig ===============================
  21760.  '   Sets the correct values for each field of the VC variable. They
  21761.  '   vary depending on Mode and on the current configuration.
  21762.  ' ======================================================================
  21763.  '
  21764.  SUB SetConfig (mode AS INTEGER) STATIC
  21765.  SHARED VC AS Config, BestMode AS INTEGER
  21766.  
  21767.     SELECT CASE mode
  21768.        CASE 1   ' Four-color graphics for CGA, EGA, VGA, and MCGA
  21769.           IF BestMode = CGA OR BestMode = MCGA THEN
  21770.              VC.Colors = 0
  21771.           ELSE
  21772.              VC.Colors = 16
  21773.           END IF
  21774.           VC.Atribs = 4
  21775.           VC.XPix = 319
  21776.           VC.YPix = 199
  21777.           VC.TCOL = 40
  21778.           VC.TROW = 25
  21779.        CASE 2   ' Two-color medium-res graphics for CGA, EGA, VGA, and MCGA
  21780.           IF BestMode = CGA OR BestMode = MCGA THEN
  21781.              VC.Colors = 0
  21782.           ELSE
  21783.              VC.Colors = 16
  21784.           END IF
  21785.           VC.Atribs = 2
  21786.           VC.XPix = 639
  21787.           VC.YPix = 199
  21788.           VC.TCOL = 80
  21789.           VC.TROW = 25
  21790.        CASE 3   ' Two-color high-res graphics for Hercules
  21791.           VC.Colors = 0
  21792.           VC.Atribs = 2
  21793.           VC.XPix = 720
  21794.           VC.YPix = 348
  21795.           VC.TCOL = 80
  21796.           VC.TROW = 25
  21797.        CASE 7   ' 16-color medium-res graphics for EGA and VGA
  21798.           VC.Colors = 16
  21799.           VC.Atribs = 16
  21800.           VC.XPix = 319
  21801.           VC.YPix = 199
  21802.           VC.TCOL = 40
  21803.           VC.TROW = 25
  21804.        CASE 8   ' 16-color high-res graphics for EGA and VGA
  21805.           VC.Colors = 16
  21806.           VC.Atribs = 16
  21807.           VC.XPix = 639
  21808.           VC.YPix = 199
  21809.           VC.TCOL = 80
  21810.           VC.TROW = 25
  21811.        CASE 9   ' 16- or 4-color very high-res graphics for EGA and VGA
  21812.           VC.Colors = 64
  21813.           IF BestMode = EGA64 THEN VC.Atribs = 4 ELSE VC.Atribs = 16
  21814.           VC.XPix = 639
  21815.           VC.YPix = 349
  21816.           VC.TCOL = 80
  21817.           VC.TROW = 25
  21818.        CASE 10  ' Two-color high-res graphics for EGA or VGA monochrome
  21819.           VC.Colors = 0
  21820.           VC.Atribs = 2
  21821.           VC.XPix = 319
  21822.           VC.YPix = 199
  21823.           VC.TCOL = 80
  21824.           VC.TROW = 25
  21825.        CASE 11  ' Two-color very high-res graphics for VGA and MCGA
  21826.           ' Note that for VGA screens 11, 12, and 13, more colors are
  21827.           ' available, depending on how the colors are mixed.
  21828.           VC.Colors = 216
  21829.           VC.Atribs = 2
  21830.           VC.XPix = 639
  21831.           VC.YPix = 479
  21832.           VC.TCOL = 80
  21833.           VC.TROW = 30
  21834.        CASE 12  ' 16-color very high-res graphics for VGA
  21835.           VC.Colors = 216
  21836.           VC.Atribs = 16
  21837.           VC.XPix = 639
  21838.           VC.YPix = 479
  21839.           VC.TCOL = 80
  21840.           VC.TROW = 30
  21841.        CASE 13  ' 256-color medium-res graphics for VGA and MCGA
  21842.           VC.Colors = 216
  21843.           VC.Atribs = 256
  21844.           VC.XPix = 639
  21845.           VC.YPix = 479
  21846.           VC.TCOL = 40
  21847.           VC.TROW = 25
  21848.        CASE ELSE
  21849.           VC.Colors = 16
  21850.           VC.Atribs = 16
  21851.           VC.XPix = 0
  21852.           VC.YPix = 0
  21853.           VC.TCOL = 80
  21854.           VC.TROW = 25
  21855.           VC.Scrn = 0
  21856.           EXIT SUB
  21857.     END SELECT
  21858.     VC.Scrn = mode
  21859.  
  21860.  END SUB
  21861.  
  21862.  ' ============================ SetPalette ==============================
  21863.  '   Mixes palette colors in an array.
  21864.  ' ======================================================================
  21865.  '
  21866.  SUB SetPalette STATIC
  21867.  SHARED VC AS Config, Pal() AS LONG
  21868.  
  21869.     ' Mix only if the adapter supports color attributes
  21870.     IF VC.Colors THEN
  21871.        SELECT CASE VC.Scrn
  21872.           CASE 1, 2, 7, 8
  21873.              ' Red, green, blue, and intense in four bits of a byte
  21874.              ' Bits: 0000irgb
  21875.              ' Change the order of FOR loops to change color mix
  21876.              Index = 0
  21877.              FOR Bs = 0 TO 1
  21878.                 FOR Gs = 0 TO 1
  21879.                    FOR Rs = 0 TO 1
  21880.                       FOR Hs = 0 TO 1
  21881.                          Pal(Index) = Hs * 8 + Rs * 4 + Gs * 2 + Bs
  21882.                          Index = Index + 1
  21883.                       NEXT
  21884.                    NEXT
  21885.                 NEXT
  21886.              NEXT
  21887.           CASE 9
  21888.              ' EGA red, green, and blue colors in 6 bits of a byte
  21889.              ' Capital letters repesent intense, lowercase normal
  21890.              ' Bits:  00rgbRGB
  21891.              ' Change the order of FOR loops to change color mix
  21892.              Index = 0
  21893.              FOR Bs = 0 TO 1
  21894.                 FOR Gs = 0 TO 1
  21895.                    FOR Rs = 0 TO 1
  21896.                       FOR HRs = 0 TO 1
  21897.                          FOR HGs = 0 TO 1
  21898.                             FOR HBs = 0 TO 1
  21899.                                Pal(Index) = Rs * 32 + Gs * 16 + Bs * 8 + HRs *
  21900.                                Index = Index + 1
  21901.                             NEXT
  21902.                          NEXT
  21903.                       NEXT
  21904.                    NEXT
  21905.                 NEXT
  21906.              NEXT
  21907.           CASE 11, 12, 13
  21908.              ' VGA colors in 6 bits of 3 bytes of a long integer
  21909.              ' Bits:  000000000 00bbbbbb 00gggggg 00rrrrrr
  21910.              ' Change the order of FOR loops to change color mix
  21911.              ' Decrease the STEP and increase VC.Colors to get more colors
  21912.              Index = 0
  21913.              FOR Rs = 0 TO 63 STEP 11
  21914.                 FOR Bs = 0 TO 63 STEP 11
  21915.                    FOR Gs = 0 TO 63 STEP 11
  21916.                       Pal(Index) = (65536 * Bs) + (256 * Gs) + Rs
  21917.                       Index = Index + 1
  21918.                    NEXT
  21919.                 NEXT
  21920.              NEXT
  21921.           CASE ELSE
  21922.        END SELECT
  21923.        ' Assign colors
  21924.        IF VC.Atribs > 2 THEN TorusRotate RNDM
  21925.     END IF
  21926.  
  21927.  END SUB
  21928.  
  21929.  ' ============================ TileDraw ================================
  21930.  '   Draw and optionally paint a tile. Tiles are painted if there are
  21931.  '   more than two atributes and if the inside of the tile can be found.
  21932.  ' ======================================================================
  21933.  '
  21934.  SUB TileDraw (T AS Tile) STATIC
  21935.  SHARED VC AS Config, TOR AS TORUS
  21936.  
  21937.     'Set border
  21938.     Border = VC.Atribs - 1
  21939.  
  21940.     IF VC.Atribs = 2 THEN
  21941.        ' Draw and quit for two-color modes
  21942.        LINE (T.x1, T.y1)-(T.x2, T.y2), T.TColor
  21943.        LINE -(T.x3, T.y3), T.TColor
  21944.        LINE -(T.x4, T.y4), T.TColor
  21945.        LINE -(T.x1, T.y1), T.TColor
  21946.        EXIT SUB
  21947.     ELSE
  21948.        ' For other modes, draw in the border color
  21949.        ' (which must be different than any tile color)
  21950.        LINE (T.x1, T.y1)-(T.x2, T.y2), Border
  21951.        LINE -(T.x3, T.y3), Border
  21952.        LINE -(T.x4, T.y4), Border
  21953.        LINE -(T.x1, T.y1), Border
  21954.     END IF
  21955.  
  21956.     ' See if tile is large enough to be painted
  21957.     IF Inside(T) THEN
  21958.        'Black out the center to make sure it isn't paint color
  21959.        PRESET (T.xc, T.yc)
  21960.        ' Paint tile black so colors of underlying tiles can't interfere
  21961.        PAINT STEP(0, 0), BACK, Border
  21962.        ' Fill with the final tile color.
  21963.        PAINT STEP(0, 0), T.TColor, Border
  21964.     END IF
  21965.  
  21966.     ' A border drawn with the background color looks like a border.
  21967.     ' One drawn with the tile color doesn't look like a border.
  21968.     IF TOR.Bord = "YES" THEN
  21969.        Border = BACK
  21970.     ELSE
  21971.        Border = T.TColor
  21972.     END IF
  21973.  
  21974.     ' Redraw with the final border
  21975.     LINE (T.x1, T.y1)-(T.x2, T.y2), Border
  21976.     LINE -(T.x3, T.y3), Border
  21977.     LINE -(T.x4, T.y4), Border
  21978.     LINE -(T.x1, T.y1), Border
  21979.  
  21980.  END SUB
  21981.  
  21982.  DEFSNG A-Z
  21983.  ' =========================== TorusCalc ================================
  21984.  '   Calculates the x and y coordinates for each tile.
  21985.  ' ======================================================================
  21986.  '
  21987.  SUB TorusCalc (T() AS Tile) STATIC
  21988.  SHARED TOR AS TORUS, Max AS INTEGER
  21989.  DIM XSect AS INTEGER, YPanel AS INTEGER
  21990.  
  21991.     ' Calculate sine and cosine of the angles of rotation
  21992.     XRot = DegToRad(TOR.XDegree)
  21993.     YRot = DegToRad(TOR.YDegree)
  21994.     CXRot = COS(XRot)
  21995.     SXRot = SIN(XRot)
  21996.     CYRot = COS(YRot)
  21997.     SYRot = SIN(YRot)
  21998.  
  21999.     ' Calculate the angle to increment between one tile and the next.
  22000.     XInc = 2 * PI / TOR.Sect
  22001.     YInc = 2 * PI / TOR.Panel
  22002.  
  22003.     ' First calculate the first point, which will be used as a reference
  22004.     ' for future points. This point must be calculated separately because
  22005.     ' it is both the beginning and the end of the center seam.
  22006.     FirstY = (TOR.Thick + 1) * CYRot
  22007.  
  22008.     ' Starting point is x1 of 0 section, 0 panel     last     0
  22009.     T(0).x1 = FirstY                             ' +------+------+
  22010.     ' Also x2 of tile on last section, 0 panel   ' |      |      | last
  22011.     T(TOR.Sect - 1).x2 = FirstY                  ' |    x3|x4    |
  22012.     ' Also x3 of last section, last panel        ' +------+------+
  22013.     T(Max - 1).x3 = FirstY                       ' |    x2|x1    |  0
  22014.     ' Also x4 of 0 section, last panel           ' |      |      |
  22015.     T(Max - TOR.Sect).x4 = FirstY                ' +------+------+
  22016.     ' A similar pattern is used for assigning all points of Torus
  22017.  
  22018.     ' Starting Y point is 0 (center)
  22019.     T(0).y1 = 0
  22020.     T(TOR.Sect - 1).y2 = 0
  22021.     T(Max - 1).y3 = 0
  22022.     T(Max - TOR.Sect).y4 = 0
  22023.  
  22024.     ' Only one z coordinate is used in sort, so other three can be ignored
  22025.     T(0).z1 = -(TOR.Thick + 1) * SYRot
  22026.  
  22027.     ' Starting at first point, work around the center seam of the Torus.
  22028.     ' Assign points for each section. The seam must be calculated separately
  22029.     ' because it is both beginning and of each section.
  22030.     FOR XSect = 1 TO TOR.Sect - 1
  22031.  
  22032.        ' X, Y, and Z elements of equation
  22033.        sx = (TOR.Thick + 1) * COS(XSect * XInc)
  22034.        sy = (TOR.Thick + 1) * SIN(XSect * XInc) * CXRot
  22035.        sz = (TOR.Thick + 1) * SIN(XSect * XInc) * SXRot
  22036.        ssx = (sz * SYRot) + (sx * CYRot)
  22037.  
  22038.        T(XSect).x1 = ssx
  22039.        T(XSect - 1).x2 = ssx
  22040.        T(Max - TOR.Sect + XSect - 1).x3 = ssx
  22041.        T(Max - TOR.Sect + XSect).x4 = ssx
  22042.  
  22043.        T(XSect).y1 = sy
  22044.        T(XSect - 1).y2 = sy
  22045.        T(Max - TOR.Sect + XSect - 1).y3 = sy
  22046.        T(Max - TOR.Sect + XSect).y4 = sy
  22047.  
  22048.        T(XSect).z1 = (sz * CYRot) - (sx * SYRot)
  22049.     NEXT
  22050.  
  22051.     ' Now start at the first seam between panel and assign points for
  22052.     ' each section of each panel. The outer loop assigns the initial
  22053.     ' point for the panel. This point must be calculated separately
  22054.     ' since it is both the beginning and the end of the seam of panels.
  22055.     FOR YPanel = 1 TO TOR.Panel - 1
  22056.  
  22057.        ' X, Y, and Z elements of equation
  22058.        sx = TOR.Thick + COS(YPanel * YInc)
  22059.        sy = -SIN(YPanel * YInc) * SXRot
  22060.        sz = SIN(YPanel * YInc) * CXRot
  22061.        ssx = (sz * SYRot) + (sx * CYRot)
  22062.  
  22063.        ' Assign X points for each panel
  22064.        ' Current ring, current side
  22065.        T(TOR.Sect * YPanel).x1 = ssx
  22066.        ' Current ring minus 1, next side
  22067.        T(TOR.Sect * (YPanel + 1) - 1).x2 = ssx
  22068.        ' Current ring minus 1, previous side
  22069.        T(TOR.Sect * YPanel - 1).x3 = ssx
  22070.        ' Current ring, previous side
  22071.        T(TOR.Sect * (YPanel - 1)).x4 = ssx
  22072.  
  22073.        ' Assign Y points for each panel
  22074.        T(TOR.Sect * YPanel).y1 = sy
  22075.        T(TOR.Sect * (YPanel + 1) - 1).y2 = sy
  22076.        T(TOR.Sect * YPanel - 1).y3 = sy
  22077.        T(TOR.Sect * (YPanel - 1)).y4 = sy
  22078.  
  22079.        ' Z point for each panel
  22080.        T(TOR.Sect * YPanel).z1 = (sz * CYRot) - (sx * SYRot)
  22081.  
  22082.        ' The inner loop assigns points for each ring (except the first)
  22083.        ' on the current side.
  22084.        FOR XSect = 1 TO TOR.Sect - 1
  22085.  
  22086.           ' Display section and panel
  22087.           CountTiles XSect, YPanel
  22088.  
  22089.           ty = (TOR.Thick + COS(YPanel * YInc)) * SIN(XSect * XInc)
  22090.           tz = SIN(YPanel * YInc)
  22091.           sx = (TOR.Thick + COS(YPanel * YInc)) * COS(XSect * XInc)
  22092.           sy = ty * CXRot - tz * SXRot
  22093.           sz = ty * SXRot + tz * CXRot
  22094.           ssx = (sz * SYRot) + (sx * CYRot)
  22095.  
  22096.           T(TOR.Sect * YPanel + XSect).x1 = ssx
  22097.           T(TOR.Sect * YPanel + XSect - 1).x2 = ssx
  22098.           T(TOR.Sect * (YPanel - 1) + XSect - 1).x3 = ssx
  22099.           T(TOR.Sect * (YPanel - 1) + XSect).x4 = ssx
  22100.  
  22101.           T(TOR.Sect * YPanel + XSect).y1 = sy
  22102.           T(TOR.Sect * YPanel + XSect - 1).y2 = sy
  22103.           T(TOR.Sect * (YPanel - 1) + XSect - 1).y3 = sy
  22104.           T(TOR.Sect * (YPanel - 1) + XSect).y4 = sy
  22105.  
  22106.           T(TOR.Sect * YPanel + XSect).z1 = (sz * CYRot) - (sx * SYRot)
  22107.        NEXT
  22108.     NEXT
  22109.     ' Erase message
  22110.     CountTiles -1, -1
  22111.  
  22112.  END SUB
  22113.  
  22114.  DEFINT A-Z
  22115.  ' =========================== TorusColor ===============================
  22116.  '   Assigns color atributes to each tile.
  22117.  ' ======================================================================
  22118.  '
  22119.  SUB TorusColor (T() AS Tile) STATIC
  22120.  SHARED VC AS Config, Max AS INTEGER
  22121.  
  22122.     ' Skip first and last atributes
  22123.     LastAtr = VC.Atribs - 2
  22124.     Atr = 1
  22125.  
  22126.     ' Cycle through each attribute until all tiles are done
  22127.     FOR Til = 0 TO Max - 1
  22128.        IF (Atr >= LastAtr) THEN
  22129.           Atr = 1
  22130.        ELSE
  22131.           Atr = Atr + 1
  22132.        END IF
  22133.        T(Til).TColor = Atr
  22134.     NEXT
  22135.  
  22136.  END SUB
  22137.  
  22138.  ' ============================ TorusDefine =============================
  22139.  '   Define the attributes of a Torus based on information from the
  22140.  '   user, the video configuration, and the current screen mode.
  22141.  ' ======================================================================
  22142.  '
  22143.  SUB TorusDefine STATIC
  22144.  SHARED VC AS Config, TOR AS TORUS, Available AS STRING
  22145.  
  22146.  ' Constants for key codes and column positions
  22147.  CONST ENTER = 13, ESCAPE = 27
  22148.  CONST DOWNARROW = 80, UPARROW = 72, LEFTARROW = 75, RIGHTARROW = 77
  22149.  CONST COL1 = 20, COL2 = 50, ROW = 9
  22150.  
  22151.     ' Display key instructions
  22152.     LOCATE 1, COL1
  22153.     PRINT "UP .............. Move to next field"
  22154.     LOCATE 2, COL1
  22155.     PRINT "DOWN ........ Move to previous field"
  22156.     LOCATE 3, COL1
  22157.     PRINT "LEFT ......... Rotate field value up"
  22158.     LOCATE 4, COL1
  22159.     PRINT "RIGHT ...... Rotate field value down"
  22160.     LOCATE 5, COL1
  22161.     PRINT "ENTER .... Start with current values"
  22162.     LOCATE 6, COL1
  22163.     PRINT "ESCAPE .................. Quit Torus"
  22164.  
  22165.     ' Block cursor
  22166.     LOCATE ROW, COL1, 1, 1, 12
  22167.     ' Display fields
  22168.     LOCATE ROW, COL1: PRINT "Thickness";
  22169.     LOCATE ROW, COL2: PRINT USING "[ # ]"; TOR.Thick;
  22170.  
  22171.     LOCATE ROW + 2, COL1: PRINT "Panels per Section";
  22172.     LOCATE ROW + 2, COL2: PRINT USING "[ ## ]"; TOR.Panel;
  22173.  
  22174.     LOCATE ROW + 4, COL1: PRINT "Sections per Torus";
  22175.     LOCATE ROW + 4, COL2: PRINT USING "[ ## ]"; TOR.Sect;
  22176.  
  22177.     LOCATE ROW + 6, COL1: PRINT "Tilt around Horizontal Axis";
  22178.     LOCATE ROW + 6, COL2: PRINT USING "[ ### ]"; TOR.XDegree;
  22179.  
  22180.     LOCATE ROW + 8, COL1: PRINT "Tilt around Vertical Axis";
  22181.     LOCATE ROW + 8, COL2: PRINT USING "[ ### ]"; TOR.YDegree;
  22182.  
  22183.     LOCATE ROW + 10, COL1: PRINT "Tile Border";
  22184.     LOCATE ROW + 10, COL2: PRINT USING "[ & ] "; TOR.Bord;
  22185.  
  22186.     LOCATE ROW + 12, COL1: PRINT "Screen Mode";
  22187.     LOCATE ROW + 12, COL2: PRINT USING "[ ## ]"; VC.Scrn
  22188.  
  22189.     ' Skip field 10 if there's only one value
  22190.     IF LEN(Available$) = 1 THEN Fields = 10 ELSE Fields = 12
  22191.  
  22192.     ' Update field values and position based on keystrokes
  22193.     DO
  22194.        ' Put cursor on field
  22195.        LOCATE ROW + Fld, COL2 + 2
  22196.        ' Get a key and strip null off if it's an extended code
  22197.        DO
  22198.           K$ = INKEY$
  22199.        LOOP WHILE K$ = ""
  22200.        Ky = ASC(RIGHT$(K$, 1))
  22201.  
  22202.        SELECT CASE Ky
  22203.           CASE ESCAPE
  22204.              ' End program
  22205.              CLS : END
  22206.           CASE UPARROW, DOWNARROW
  22207.              ' Adjust field location
  22208.              IF Ky = DOWNARROW THEN Inc = 2 ELSE Inc = -2
  22209.              Fld = Rotated(0, Fields, Fld, Inc)
  22210.           CASE RIGHTARROW, LEFTARROW
  22211.              ' Adjust field
  22212.              IF Ky = RIGHTARROW THEN Inc = 1 ELSE Inc = -1
  22213.              SELECT CASE Fld
  22214.                 CASE 0
  22215.                    ' Thickness
  22216.                    TOR.Thick = Rotated(1, 9, INT(TOR.Thick), Inc)
  22217.                    PRINT USING "#"; TOR.Thick
  22218.                 CASE 2
  22219.                    ' Panels
  22220.                    TOR.Panel = Rotated(6, 20, TOR.Panel, Inc)
  22221.                    PRINT USING "##"; TOR.Panel
  22222.                 CASE 4
  22223.                    ' Sections
  22224.                    TOR.Sect = Rotated(6, 20, TOR.Sect, Inc)
  22225.                    PRINT USING "##"; TOR.Sect
  22226.                 CASE 6
  22227.                    ' Horizontal tilt
  22228.                    TOR.XDegree = Rotated(0, 345, TOR.XDegree, (15 * Inc))
  22229.                    PRINT USING "###"; TOR.XDegree
  22230.                 CASE 8
  22231.                    ' Vertical tilt
  22232.                    TOR.YDegree = Rotated(0, 345, TOR.YDegree, (15 * Inc))
  22233.                    PRINT USING "###"; TOR.YDegree
  22234.                 CASE 10
  22235.                    ' Border
  22236.                    IF VC.Atribs > 2 THEN
  22237.                       IF TOR.Bord = "YES" THEN
  22238.                          TOR.Bord = "NO"
  22239.                       ELSE
  22240.                          TOR.Bord = "YES"
  22241.                       END IF
  22242.                    END IF
  22243.                    PRINT TOR.Bord
  22244.                 CASE 12
  22245.                    ' Available screen modes
  22246.                    I = INSTR(Available$, HEX$(VC.Scrn))
  22247.                    I = Rotated(1, LEN(Available$), I, Inc)
  22248.                    VC.Scrn = VAL("&h" + MID$(Available$, I, 1))
  22249.                    PRINT USING "##"; VC.Scrn
  22250.                 CASE ELSE
  22251.              END SELECT
  22252.           CASE ELSE
  22253.        END SELECT
  22254.     ' Set configuration data for graphics mode
  22255.     SetConfig VC.Scrn
  22256.     ' Draw Torus if ENTER
  22257.     LOOP UNTIL Ky = ENTER
  22258.  
  22259.     ' Remove cursor
  22260.     LOCATE 1, 1, 0
  22261.  
  22262.     ' Set different delays depending on mode
  22263.     SELECT CASE VC.Scrn
  22264.        CASE 1
  22265.           TOR.Delay = .3
  22266.        CASE 2, 3, 10, 11, 13
  22267.           TOR.Delay = 0
  22268.        CASE ELSE
  22269.           TOR.Delay = .05
  22270.     END SELECT
  22271.  
  22272.     ' Get new random seed for this torus
  22273.     RANDOMIZE TIMER
  22274.  
  22275.  END SUB
  22276.  
  22277.  ' =========================== TorusDraw ================================
  22278.  '   Draws each tile of the torus starting with the farthest and working
  22279.  '   to the closest. Thus nearer tiles overwrite farther tiles to give
  22280.  '   a three-dimensional effect. Notice that the index of the tile being
  22281.  '   drawn is actually the index of an array of indexes. This is because
  22282.  '   the array of tiles is not sorted, but the parallel array of indexes
  22283.  '   is. See TorusSort for an explanation of how indexes are sorted.
  22284.  ' ======================================================================
  22285.  '
  22286.  SUB TorusDraw (T() AS Tile, Index() AS INTEGER)
  22287.  SHARED Max AS INTEGER
  22288.  
  22289.     FOR Til = 0 TO Max - 1
  22290.        TileDraw T(Index(Til))
  22291.     NEXT
  22292.  
  22293.  END SUB
  22294.  
  22295.  ' =========================== TorusRotate ==============================
  22296.  '   Rotates the Torus. This can be done more successfully in some modes
  22297.  '   than in others. There are three methods:
  22298.  '
  22299.  '     1. Rotate the palette colors assigned to each attribute
  22300.  '     2. Draw, erase, and redraw the torus (two-color modes)
  22301.  '     3. Rotate between two palettes (CGA and MCGA screen 1)
  22302.  '
  22303.  '   Note that for EGA and VGA screen 2, methods 1 and 2 are both used.
  22304.  ' ======================================================================
  22305.  '
  22306.  SUB TorusRotate (First) STATIC
  22307.  SHARED VC AS Config, TOR AS TORUS, Pal() AS LONG, Max AS INTEGER
  22308.  SHARED T() AS Tile, Index() AS INTEGER, BestMode AS INTEGER
  22309.  DIM Temp AS LONG
  22310.  
  22311.     ' For EGA and higher rotate colors through palette
  22312.     IF VC.Colors THEN
  22313.  
  22314.        ' Argument determines whether to start at next color, first color,
  22315.        ' or random color
  22316.        SELECT CASE First
  22317.           CASE RNDM
  22318.              FirstClr = INT(RND * VC.Colors)
  22319.           CASE START
  22320.              FirstClr = 0
  22321.           CASE ELSE
  22322.              FirstClr = FirstClr - 1
  22323.        END SELECT
  22324.  
  22325.        ' Set last color to smaller of last possible color or last tile
  22326.        IF VC.Colors > Max - 1 THEN
  22327.           LastClr = Max - 1
  22328.        ELSE
  22329.           LastClr = VC.Colors - 1
  22330.        END IF
  22331.  
  22332.        ' If color is too low, rotate to end
  22333.        IF FirstClr < 0 OR FirstClr >= LastClr THEN FirstClr = LastClr
  22334.  
  22335.        ' Set last attribute
  22336.        IF VC.Atribs = 2 THEN
  22337.           ' Last for two-color modes
  22338.           LastAtr = VC.Atribs - 1
  22339.        ELSE
  22340.           ' Smaller of last color or next-to-last attribute
  22341.           IF LastClr < VC.Atribs - 2 THEN
  22342.              LastAtr = LastClr
  22343.           ELSE
  22344.              LastAtr = VC.Atribs - 2
  22345.           END IF
  22346.        END IF
  22347.  
  22348.        ' Cycle through attributes, assigning colors
  22349.        Work = FirstClr
  22350.        FOR Atr = LastAtr TO 1 STEP -1
  22351.           PALETTE Atr, Pal(Work)
  22352.           Work = Work - 1
  22353.           IF Work < 0 THEN Work = LastClr
  22354.        NEXT
  22355.  
  22356.     END IF
  22357.  
  22358.     ' For two-color screens, the best we can do is erase and redraw the torus
  22359.     IF VC.Atribs = 2 THEN
  22360.  
  22361.        ' Set all tiles to color
  22362.        FOR I = 0 TO Max - 1
  22363.           T(I).TColor = Toggle
  22364.        NEXT
  22365.        ' Draw Torus
  22366.        TorusDraw T(), Index()
  22367.        ' Toggle between color and background
  22368.        Toggle = (Toggle + 1) MOD 2
  22369.  
  22370.     END IF
  22371.  
  22372.     ' For CGA or MCGA screen 1, toggle palettes using the COLOR statement
  22373.     ' (these modes do not allow the PALETTE statement)
  22374.     IF VC.Scrn = 1 AND (BestMode = CGA OR BestMode = MCGA) THEN
  22375.        COLOR , Toggle
  22376.        Toggle = (Toggle + 1) MOD 2
  22377.        EXIT SUB
  22378.     END IF
  22379.  
  22380.  END SUB
  22381.  
  22382.  ' ============================ TorusSort ===============================
  22383.  '   Sorts the tiles of the Torus according to their Z axis (distance
  22384.  '   from the "front" of the screen). When the tiles are drawn, the
  22385.  '   farthest will be drawn first, and nearer tiles will overwrite them
  22386.  '   to give a three-dimensional effect.
  22387.  '
  22388.  '   To make sorting as fast as possible, the Quick Sort algorithm is
  22389.  '   used. Also, the array of tiles is not actually sorted. Instead a
  22390.  '   parallel array of tile indexes is sorted. This complicates things,
  22391.  '   but makes the sort much faster, since two-byte integers are swapped
  22392.  '   instead of 46-byte Tile variables.
  22393.  ' ======================================================================
  22394.  '
  22395.  SUB TorusSort (Low, High)
  22396.  SHARED T() AS Tile, Index() AS INTEGER
  22397.  DIM Partition AS SINGLE
  22398.  
  22399.     IF Low < High THEN
  22400.        ' If only one, compare and swap if necessary
  22401.        ' The SUB procedure only stops recursing when it reaches this point
  22402.        IF High - Low = 1 THEN
  22403.           IF T(Index(Low)).z1 > T(Index(High)).z1 THEN
  22404.              CountTiles High, Low
  22405.              SWAP Index(Low), Index(High)
  22406.           END IF
  22407.        ELSE
  22408.        ' If more than one, separate into two random groups
  22409.           RandIndex = INT(RND * (High - Low + 1)) + Low
  22410.           CountTiles High, Low
  22411.           SWAP Index(High), Index(RandIndex%)
  22412.           Partition = T(Index(High)).z1
  22413.           ' Sort one group
  22414.           DO
  22415.              I = Low: J = High
  22416.              ' Find the largest
  22417.              DO WHILE (I < J) AND (T(Index(I)).z1 <= Partition)
  22418.                 I = I + 1
  22419.              LOOP
  22420.              ' Find the smallest
  22421.              DO WHILE (J > I) AND (T(Index(J)).z1 >= Partition)
  22422.                 J = J - 1
  22423.              LOOP
  22424.              ' Swap them if necessary
  22425.              IF I < J THEN
  22426.                 CountTiles High, Low
  22427.                 SWAP Index(I), Index(J)
  22428.              END IF
  22429.           LOOP WHILE I < J
  22430.  
  22431.           ' Now get the other group and recursively sort it
  22432.           CountTiles High, Low
  22433.           SWAP Index(I), Index(High)
  22434.           IF (I - Low) < (High - I) THEN
  22435.              TorusSort Low, I - 1
  22436.              TorusSort I + 1, High
  22437.           ELSE
  22438.              TorusSort I + 1, High
  22439.              TorusSort Low, I - 1
  22440.           END IF
  22441.        END IF
  22442.     END IF
  22443.  
  22444.  END SUB
  22445.  
  22446.  
  22447.  
  22448.  UIASM.ASM
  22449.  CD-ROM Disc Path:   \SAMPCODE\BASIC\UIASM.ASM
  22450.  
  22451.  ;----------------------------------------------------------------------------
  22452.  ;----------------------------------------------------------------------------
  22453.  ;
  22454.  ;  UIASM.ASM
  22455.  ;
  22456.  ;  Copyright (C) 1989 Microsoft Corporation, All Rights Reserved
  22457.  ;
  22458.  ;  GetCopyBox : Gets screen box info and places into string variable
  22459.  ;  PutCopyBox : Puts screen box info from string variable onto screen
  22460.  ;  AttrBox    : Changes the color attributes of all characters within a box
  22461.  ;
  22462.  ;----------------------------------------------------------------------------
  22463.  ;----------------------------------------------------------------------------
  22464.  
  22465.  ;NOTE: For optimum speed, these routines write directly to screen memory
  22466.  ;      without waiting for re-trace.  If "snow" is a problem, these routines
  22467.  ;      will need modification.
  22468.  
  22469.  .model medium
  22470.  
  22471.          extrn   STRINGADDRESS:far       ;BASIC RTL entry point for string inf
  22472.  
  22473.  .data
  22474.  
  22475.  attr    db      ?                       ;destination attribute (AttrBox)
  22476.  x0      db      ?                       ;x coord of upper-left
  22477.  y0      db      ?                       ;y coord of upper-left
  22478.  x1      db      ?                       ;x coord of lower-right
  22479.  y1      db      ?                       ;y coord of lower-right
  22480.  bwidth  db      ?                       ;box width
  22481.  height  db      ?                       ;box height
  22482.  strdoff dw      ?                       ;string pointer offset
  22483.  strdseg dw      ?                       ;string pointer segment
  22484.  scrseg  dw      ?                       ;screen segment
  22485.  movword dw      ?                       ;word count to move/change
  22486.  
  22487.  .code
  22488.  
  22489.  ;---------------------------------------place segment of screen memory
  22490.  ;---------------------------------------in SCRSEG
  22491.  get_scrseg      proc
  22492.  
  22493.          push    ax                      ;save value of AX
  22494.          mov     ah,0Fh
  22495.          int     10h                     ;INT 10H fn. 0Fh - Get Video Mode
  22496.          mov     dgroup:scrseg,0B800h    ;assume COLOR screen for now
  22497.          cmp     al,07h                  ;is it MONOCHROME mode?
  22498.          jne     arnd1
  22499.          mov     dgroup:scrseg,0B000h    ;yes, set for mono screen seg
  22500.  arnd1:  pop     ax                      ;restore AX
  22501.          ret                             ;and exit
  22502.  
  22503.  get_scrseg      endp
  22504.  
  22505.  
  22506.  ;----------------------------------------Given X and Y in AH and AL, find
  22507.  ;----------------------------------------the offset into screen memory and
  22508.  ;----------------------------------------return in AX
  22509.  get_memxy       proc
  22510.  
  22511.          push    dx                      ;save DX
  22512.          push    ax                      ;save coords
  22513.          mov     dl,160
  22514.          mul     dl                      ;multiply Y by 160
  22515.          pop     dx                      ;put coords in DX
  22516.          mov     dl,dh
  22517.          mov     dh,0
  22518.          add     dl,dl                   ;double X
  22519.          add     ax,dx                   ;and add to mult. result for final!
  22520.          pop     dx                      ;restore DX
  22521.          ret
  22522.  
  22523.  get_memxy       endp
  22524.  
  22525.  
  22526.  ;----------------------------------------------------------------------------
  22527.  ;----------------------------------------This is the routine that copies
  22528.  ;----------------------------------------screen info to the string variable
  22529.  ;----------------------------------------------------------------------------
  22530.          public  getcopybox
  22531.  getcopybox      proc    far
  22532.  
  22533.          push    bp
  22534.          mov     bp,sp
  22535.          push    ds
  22536.          push    es
  22537.          push    si
  22538.          push    di                      ;preserve registers
  22539.  
  22540.  get_start:
  22541.          mov     bx,[bp + 14]            ;get y0
  22542.          mov     ax,[bx]
  22543.          mov     y0,al
  22544.          mov     bx,[bp + 12]            ;...x0
  22545.          mov     ax,[bx]
  22546.          mov     x0,al
  22547.          mov     bx,[bp + 10]            ;...y1
  22548.          mov     ax,[bx]
  22549.          mov     y1,al
  22550.          mov     bx,[bp + 8]             ;...x1
  22551.          mov     ax,[bx]
  22552.          mov     x1,al
  22553.          mov     bx,[bp + 6]             ;...and the destination str desc.
  22554.  
  22555.          push    bx
  22556.          call    STRINGADDRESS           ;for both near and far string support
  22557.          mov     strdoff, ax
  22558.          mov     strdseg, dx
  22559.  
  22560.          dec     x0                      ;subtract 1 from
  22561.          dec     y0                      ;all coordinates
  22562.          dec     x1                      ;to reflect BASIC's
  22563.          dec     y1                      ;screen base of 1 (not 0)
  22564.  
  22565.  get_chkscr:
  22566.          call    get_scrseg              ;set up screen segment
  22567.  
  22568.  get_setstr:
  22569.          mov     al,x1
  22570.          sub     al,x0                   ;find width of box
  22571.          mov     bwidth,al               ;and save
  22572.          add     al,1                    ;add one to width
  22573.          mov     ah,0                    ;to find # words to move
  22574.          mov     movword,ax              ;MovWord = (width+1)
  22575.          mov     al,y1
  22576.          sub     al,y0                   ;find height of box
  22577.          mov     height,al               ;and save
  22578.          mov     es,strdseg
  22579.          mov     di,strdoff              ;string is the destination
  22580.          mov     si,offset bwidth        ;point to width
  22581.          movsb                           ;put width in string
  22582.          mov     si,offset height
  22583.          movsb                           ;and the height, too
  22584.  
  22585.  get_movstr:
  22586.          mov     al,y0
  22587.          mov     ah,x0                   ;put coords in AH and AL
  22588.          call    get_memxy               ;and find offset into screen mem
  22589.          mov     si,ax                   ;this will be the source
  22590.  
  22591.  get_domove:
  22592.          mov     cx,movword
  22593.          push    ds
  22594.          mov     ds,scrseg
  22595.          rep     movsw                   ;move a row into the string
  22596.          pop     ds
  22597.          add     si,160
  22598.          sub     si,movword              ;Add 160-(movword*2) to si
  22599.          sub     si,movword              ;to point to next row
  22600.          cmp     height,0                ;was that the last row?
  22601.          je      get_done                ;yes, we're done
  22602.          dec     height                  ;decrement height
  22603.          jmp     get_domove              ;and do another row
  22604.  
  22605.  get_done:
  22606.          pop     di
  22607.          pop     si
  22608.          pop     es
  22609.          pop     ds                      ;restore registers
  22610.          pop     bp
  22611.          ret     10                      ;there were 5 parameters
  22612.  
  22613.  getcopybox      endp
  22614.  
  22615.  
  22616.  ;----------------------------------------------------------------------------
  22617.  ;----------------------------------------This is the routine that copies the
  22618.  ;----------------------------------------information stored in the string to
  22619.  ;----------------------------------------the screen in the specified location
  22620.  ;----------------------------------------------------------------------------
  22621.          public  putcopybox
  22622.  putcopybox      proc    far
  22623.  
  22624.          push    bp
  22625.          mov     bp,sp
  22626.          push    ds
  22627.          push    es
  22628.          push    si
  22629.          push    di                      ;preserve registers
  22630.  
  22631.  
  22632.  put_start:
  22633.          mov     bx,[bp + 10]            ;get y0
  22634.          mov     ax,[bx]
  22635.          mov     y0,al
  22636.          mov     bx,[bp + 8]             ;...x0
  22637.          mov     ax,[bx]
  22638.          mov     x0,al
  22639.          mov     bx,[bp + 6]             ;...and the destination string
  22640.  
  22641.          push    bx
  22642.          call    STRINGADDRESS           ;for both near and far string support
  22643.          mov     strdoff, ax
  22644.          mov     strdseg, dx
  22645.  
  22646.          dec     x0                      ;subtract 1 from
  22647.          dec     y0                      ;all coordinates
  22648.  
  22649.  put_chkscr:
  22650.          call    get_scrseg              ;set up scrseg
  22651.  
  22652.  put_setstr:
  22653.          push    ds
  22654.          pop     es                      ;equate ES to DS
  22655.  
  22656.          mov     si,strdoff              ;point DS:SI to string mem
  22657.          push    ds
  22658.          mov     ds,strdseg
  22659.          mov     di,offset bwidth
  22660.          movsb                           ;get width
  22661.          mov     di,offset height
  22662.          movsb                           ;and height out of string
  22663.          pop     ds
  22664.  
  22665.          mov     al,bwidth
  22666.          add     al,1
  22667.          mov     ah,0
  22668.          mov     movword,ax              ;set movword to (bwidth+1)
  22669.  
  22670.          mov     ah,x0
  22671.          mov     al,y0                   ;get coords
  22672.          call    get_memxy               ;and find offset into screen mem
  22673.          mov     di,ax
  22674.          mov     es,scrseg               ;ES:DI -> screen mem (UL corner)
  22675.  
  22676.  put_domove:
  22677.          mov     cx,movword
  22678.          push    ds
  22679.          mov     ds,strdseg
  22680.          rep     movsw                   ;move a row onto the screen
  22681.          pop     ds
  22682.          add     di,160
  22683.          sub     di,movword              ;add 160-(movword*2) to DI
  22684.          sub     di,movword              ;to point to next row on screen
  22685.          cmp     height,0                ;was that the last row?
  22686.          je      put_done                ;yes, we're finished
  22687.          dec     height
  22688.          jmp     put_domove              ;no, decrement and do again
  22689.  
  22690.  put_done:
  22691.          pop     di
  22692.          pop     si
  22693.          pop     es
  22694.          pop     ds                      ;restore registers
  22695.          pop     bp
  22696.          ret     6                       ;pop off 3 parameters
  22697.  
  22698.  putcopybox      endp
  22699.  
  22700.  ;----------------------------------------------------------------------------
  22701.  ;----------------------------------------This is the routine that changes
  22702.  ;----------------------------------------the colors of the box's characters
  22703.  ;----------------------------------------------------------------------------
  22704.          public  attrbox
  22705.  attrbox         proc    far
  22706.  
  22707.          push    bp
  22708.          mov     bp, sp
  22709.          push    ds
  22710.          push    es
  22711.          push    si
  22712.          push    di                      ;preserve registers
  22713.  
  22714.  atr_start:
  22715.          mov     bx, [bp+14]             ;get y0
  22716.          mov     ax, [bx]
  22717.          mov     y0, al
  22718.          mov     bx, [bp+12]             ;...x0
  22719.          mov     ax, [bx]
  22720.          mov     x0, al
  22721.          mov     bx, [bp+10]             ;...y1
  22722.          mov     ax, [bx]
  22723.          mov     y1, al
  22724.          mov     bx, [bp+8]              ;...x1
  22725.          mov     ax, [bx]
  22726.          mov     x1, al
  22727.          mov     bx, [bp+6]              ;...and finally the new color value
  22728.          mov     ax, [bx]
  22729.          mov     attr, al
  22730.  
  22731.          dec     y0                      ;subtract 1 from
  22732.          dec     x0                      ;all coordinates
  22733.          dec     y1                      ;to reflect BASIC's
  22734.          dec     x1                      ;screen base of 1 (not 0)
  22735.  
  22736.  atr_chkscr:
  22737.          call    get_scrseg              ;set up screen segment
  22738.  
  22739.  atr_setup:
  22740.          mov     al, x1
  22741.          sub     al, x0                  ;find width of box
  22742.          inc     al
  22743.          xor     ah, ah
  22744.          mov     movword, ax             ;(width + 1 = movword)
  22745.          mov     al, y1
  22746.          sub     al, y0                  ;find height of box
  22747.          mov     height, al              ;and save
  22748.  
  22749.  atr_chgclr:
  22750.          mov     al, y0
  22751.          mov     ah, x0                  ;put coords in AH and AL
  22752.          call    get_memxy               ;find offset into screen memory
  22753.          mov     di, ax                  ;(which is our destination)
  22754.          mov     es, scrseg
  22755.          mov     al, attr                ;get the color value to store
  22756.  
  22757.  atr_doit:
  22758.          mov     cx, movword
  22759.  atr_loop:
  22760.          inc     di                      ;skip the character value
  22761.          stosb                           ;write new color value
  22762.          loop    atr_loop                ;cx times
  22763.          add     di, 160                 ;add 160-(movword*2) to di
  22764.          sub     di, movword
  22765.          sub     di, movword
  22766.          cmp     height, 0               ;was that the last row?
  22767.          je      atr_done                ;yes, we be finished
  22768.          dec     height                  ;no, decrement height
  22769.          jmp     atr_doit
  22770.  
  22771.  atr_done:
  22772.          pop     di
  22773.          pop     si
  22774.          pop     es
  22775.          pop     ds
  22776.          pop     bp                      ;restore registers
  22777.          ret     10                      ;pull off 5 paramters and return
  22778.  
  22779.  attrbox         endp
  22780.  
  22781.          END
  22782.  
  22783.  
  22784.  UIDEMO.BAS
  22785.  CD-ROM Disc Path:   \SAMPCODE\BASIC\UIDEMO.BAS
  22786.  
  22787.  ' ===========================================================================
  22788.  '
  22789.  ' UIDEMO.BAS Copyright (c) 1989 Microsoft Corporation
  22790.  '
  22791.  ' ===========================================================================
  22792.  ' ===========================================================================
  22793.  ' Decls, includes, and dimensions
  22794.  ' ===========================================================================
  22795.  DEFINT A-Z
  22796.  DECLARE SUB AboutDemo ()
  22797.  DECLARE SUB AboutUIP ()
  22798.  DECLARE SUB AboutMouse ()
  22799.  DECLARE SUB AboutAccess ()
  22800.  DECLARE SUB AboutQuick ()
  22801.  DECLARE SUB AboutWindows ()
  22802.  DECLARE SUB ColorDisplay ()
  22803.  DECLARE SUB DemoAlert ()
  22804.  DECLARE SUB DemoDialog ()
  22805.  DECLARE SUB DemoDialogEZ ()
  22806.  DECLARE SUB DemoFileNameListBox ()
  22807.  DECLARE SUB DemoListBox ()
  22808.  DECLARE SUB DemoWindow ()
  22809.  DECLARE SUB DemoScrollBar ()
  22810.  DECLARE SUB DemoResize ()
  22811.  DECLARE SUB MonoDisplay ()
  22812.  DECLARE SUB SetupDesktop ()
  22813.  DECLARE SUB SetupMenu ()
  22814.  DECLARE FUNCTION GetFileCount% (FileSpec$)
  22815.  
  22816.  '$INCLUDE: 'general.bi'
  22817.  '$INCLUDE: 'mouse.bi'
  22818.  '$INCLUDE: 'menu.bi'
  22819.  '$INCLUDE: 'window.bi'
  22820.  
  22821.  COMMON SHARED /uitools/ GloMenu           AS MenuMiscType
  22822.  COMMON SHARED /uitools/ GloTitle()        AS MenuTitleType
  22823.  COMMON SHARED /uitools/ GloItem()         AS MenuItemType
  22824.  COMMON SHARED /uitools/ GloWindow()       AS windowType
  22825.  COMMON SHARED /uitools/ GloButton()       AS buttonType
  22826.  COMMON SHARED /uitools/ GloEdit()         AS EditFieldType
  22827.  COMMON SHARED /uitools/ GloStorage        AS WindowStorageType
  22828.  COMMON SHARED /uitools/ GloWindowStack()  AS INTEGER
  22829.  COMMON SHARED /uitools/ GloBuffer$()
  22830.  
  22831.  DIM GloTitle(MAXMENU)           AS MenuTitleType
  22832.  DIM GloItem(MAXMENU, MAXITEM)   AS MenuItemType
  22833.  DIM GloWindow(MAXWINDOW)        AS windowType
  22834.  DIM GloButton(MAXBUTTON)        AS buttonType
  22835.  DIM GloEdit(MAXEDITFIELD)       AS EditFieldType
  22836.  DIM GloWindowStack(MAXWINDOW)   AS INTEGER
  22837.  DIM GloBuffer$(MAXWINDOW + 1, 2)
  22838.  
  22839.  DIM SHARED DisplayType          AS INTEGER
  22840.  
  22841.      ' =======================================================================
  22842.      ' Initialize Demo
  22843.      ' =======================================================================
  22844.  
  22845.      MenuInit
  22846.      WindowInit
  22847.      MouseShow
  22848.      MonoDisplay
  22849.  
  22850.      ' =======================================================================
  22851.      ' Show Opening alert window
  22852.      ' =======================================================================
  22853.  
  22854.  
  22855.           a$ = "User Interface Toolbox Demo|"
  22856.      a$ = a$ + "for|"
  22857.      a$ = a$ + "Microsoft BASIC 7.0 Professional Development System|"
  22858.      a$ = a$ + "Copyright (c) 1989 Microsoft Corporation|"
  22859.  
  22860.      x = Alert(4, a$, 9, 10, 14, 70, "Color", "Monochrome", "")
  22861.  
  22862.      IF x = 1 THEN
  22863.          DisplayType = TRUE
  22864.          ColorDisplay
  22865.      END IF
  22866.  
  22867.      ' =======================================================================
  22868.      ' Main Loop : Stay in loop until DemoFinished set to TRUE
  22869.      ' =======================================================================
  22870.  
  22871.      DemoFinished = FALSE
  22872.  
  22873.      WHILE NOT DemoFinished
  22874.          kbd$ = MenuInkey$
  22875.          WHILE MenuCheck(2)
  22876.              GOSUB MenuTrap
  22877.          WEND
  22878.      WEND
  22879.  
  22880.      ' =======================================================================
  22881.      ' End Program
  22882.      ' =======================================================================
  22883.  
  22884.      MouseHide
  22885.      COLOR 15, 0
  22886.      CLS
  22887.      END
  22888.  
  22889.  
  22890.  
  22891.  ' ===========================================================================
  22892.  ' If a menu event occured, call the proper demo, or if Exit, set demoFinished
  22893.  ' ===========================================================================
  22894.  
  22895.  MenuTrap:
  22896.      menu = MenuCheck(0)
  22897.      item = MenuCheck(1)
  22898.  
  22899.      SELECT CASE menu
  22900.          CASE 1
  22901.              SELECT CASE item
  22902.                  CASE 1:  DemoAlert
  22903.                  CASE 2:  DemoDialogEZ
  22904.                  CASE 3:  DemoDialog
  22905.                  CASE 4:  DemoListBox
  22906.                  CASE 5:  DemoFileNameListBox
  22907.                  CASE 6:  DemoScrollBar
  22908.                  CASE 7:  DemoWindow
  22909.                  CASE 8:  DemoResize
  22910.                  CASE 10: DemoFinished = TRUE
  22911.              END SELECT
  22912.          CASE 2
  22913.              SELECT CASE item
  22914.                  CASE 1: ColorDisplay
  22915.                  CASE 2: MonoDisplay
  22916.  
  22917.              END SELECT
  22918.          CASE 3
  22919.              SELECT CASE item
  22920.                  CASE 1: AboutDemo
  22921.                  CASE 2: AboutUIP
  22922.                  CASE 3: AboutWindows
  22923.                  CASE 4: AboutMouse
  22924.                  CASE 5: AboutAccess
  22925.                  CASE 6: AboutQuick
  22926.              END SELECT
  22927.          CASE ELSE
  22928.      END SELECT
  22929.  RETURN
  22930.  
  22931.  SUB AboutAccess
  22932.           a$ = "                      Access Keys||"
  22933.      a$ = a$ + "Access keys are the keys on the menu bar that are highlighted|
  22934.      a$ = a$ + "when you press the Alt key. If you press a letter that is|"
  22935.      a$ = a$ + "highlighted in a menu title, that menu will be selected.||"
  22936.      a$ = a$ + "Once a pull-down menu is displayed, each menu item also has a|
  22937.      a$ = a$ + "highlighted letter associated with each choice. Press the|"
  22938.      a$ = a$ + "letter that corresponds to the menu item you want to select.||
  22939.      a$ = a$ + "If, after you press Alt, you change your mind, press the Alt|"
  22940.      a$ = a$ + "key again to cancel."
  22941.  
  22942.      junk = Alert(1, a$, 7, 9, 20, 69, "", "", "")
  22943.  
  22944.  END SUB
  22945.  
  22946.  SUB AboutDemo
  22947.           a$ = "                      About This Demo||"
  22948.      a$ = a$ + "Running this program provides a visual demonstration of most|"
  22949.      a$ = a$ + "of the features implemented in the new User Interface Toolbox|
  22950.      a$ = a$ + "for the BASIC Compiler 7.0.||"
  22951.      a$ = a$ + "In addition to serving as a demo of toolbox features, the|"
  22952.      a$ = a$ + "source code that makes up this program can also serve as a|"
  22953.      a$ = a$ + "programming example of how to implement these features in|"
  22954.      a$ = a$ + "your programs. While the demo is relatively simple, it does|"
  22955.      a$ = a$ + "illustrate almost all the features available."
  22956.  
  22957.      junk = Alert(1, a$, 7, 9, 19, 69, "", "", "")
  22958.  END SUB
  22959.  
  22960.  SUB AboutMouse
  22961.           a$ = "                      Using the Mouse||"
  22962.      a$ = a$ + "Virtually all operations in the User Interface Toolbox can|"
  22963.      a$ = a$ + "be accomplished using the mouse. Move the mouse cursor to|"
  22964.      a$ = a$ + "whatever you want to select and press the left button.||"
  22965.      a$ = a$ + "In addition to being able to make a choice with a mouse,|"
  22966.      a$ = a$ + "you can also perform a number of operations on windows.|"
  22967.      a$ = a$ + "Using the mouse you can close, move, or resize windows|"
  22968.      a$ = a$ + "depending on the particular features of the window that is|"
  22969.      a$ = a$ + "active."
  22970.  
  22971.      junk = Alert(1, a$, 7, 9, 19, 69, "", "", "")
  22972.  
  22973.  END SUB
  22974.  
  22975.  SUB AboutQuick
  22976.           a$ = "                      Quick Keys||"
  22977.      a$ = a$ + "Quick keys are optional keys that you can define in addition|"
  22978.      a$ = a$ + "to the normal access keys that must be specified when you|"
  22979.      a$ = a$ + "set up the individual menu items.||"
  22980.      a$ = a$ + "Quick keys normally reduce selection of a menu item to one|"
  22981.      a$ = a$ + "keystroke. For example, this demo uses function keys F1 thru|"
  22982.      a$ = a$ + "F8 to select menu choices that demonstrate different features|
  22983.      a$ = a$ + "of the User Interface Toolbox.  Additionally, Ctrl-X is the|"
  22984.      a$ = a$ + "Quick key that exits this demonstration program."
  22985.  
  22986.      junk = Alert(1, a$, 7, 9, 19, 69, "", "", "")
  22987.  
  22988.  END SUB
  22989.  
  22990.  SUB AboutUIP
  22991.           a$ = "                 About the User Interface||"
  22992.      a$ = a$ + "The user interface provided with this toolbox is designed to|"
  22993.      a$ = a$ + "provide much the same functionality as that found in the QBX|"
  22994.      a$ = a$ + "programming environment. The menus, check boxes, option|"
  22995.      a$ = a$ + "buttons, and other interface features operate similarly to|"
  22996.      a$ = a$ + "their QBX counterparts. ||"
  22997.      a$ = a$ + "If you know how to navigate QBX, you know how to navigate|"
  22998.      a$ = a$ + "the interface provided by the User Interface Toolbox."
  22999.  
  23000.      junk = Alert(1, a$, 7, 9, 18, 69, "", "", "")
  23001.  END SUB
  23002.  
  23003.  SUB AboutWindows
  23004.           a$ = "                     About the Windows||"
  23005.      a$ = a$ + "Several border characters used by the windows in the User|"
  23006.      a$ = a$ + "Interface Toolbox have special significance.  Any window that|
  23007.      a$ = a$ + "has a '=' in the upper-left corner can be closed by selecting|
  23008.      a$ = a$ + "that character with the mouse. Windows with the '░' character|
  23009.      a$ = a$ + "across the window's top row can be moved around the screen by|
  23010.      a$ = a$ + "selecting that area with the mouse.  The '+' character in the|
  23011.      a$ = a$ + "lower-right corner means that the window can be resized by|"
  23012.      a$ = a$ + "selecting the '+' character with the mouse.||"
  23013.      a$ = a$ + "Note that none of these features can be accessed without a|"
  23014.      a$ = a$ + "mouse. "
  23015.  
  23016.      junk = Alert(1, a$, 7, 9, 21, 69, "", "", "")
  23017.  
  23018.  END SUB
  23019.  
  23020.  SUB ColorDisplay
  23021.      DisplayType = TRUE
  23022.      MouseHide
  23023.      SetupMenu
  23024.      MenuSetState 2, 1, 2
  23025.      MenuSetState 2, 2, 1
  23026.      SetupDesktop
  23027.      MenuShow
  23028.      MouseShow
  23029.  END SUB
  23030.  
  23031.  SUB DemoAlert
  23032.  
  23033.      ' =======================================================================
  23034.      ' Simple little demo of how easy alerts are to use.
  23035.      ' =======================================================================
  23036.  
  23037.           a$ = "|"
  23038.      a$ = a$ + "This is an Alert Box.| |"
  23039.      a$ = a$ + "It was created using a simple one|"
  23040.      a$ = a$ + "line command.  Notice the buttons|"
  23041.      a$ = a$ + "below.  They are user definable|"
  23042.      a$ = a$ + "yet their spacing is automatic."
  23043.  
  23044.      B$ = "You Selected OK"
  23045.  
  23046.      C$ = "You Selected Cancel"
  23047.  
  23048.      SELECT CASE Alert(4, a$, 6, 20, 15, 60, "OK", "Cancel", "")
  23049.          CASE 1
  23050.              x = Alert(4, B$, 10, 25, 12, 55, "OK", "", "")
  23051.          CASE 2
  23052.              x = Alert(4, C$, 10, 25, 12, 55, "OK", "", "")
  23053.      END SELECT
  23054.  
  23055.  END SUB
  23056.  
  23057.  SUB DemoDialog
  23058.  
  23059.      ' =======================================================================
  23060.      ' This is about as complex as they get.  As you can see it is still very
  23061.      ' simple - just a lot bigger.  This sub exactly duplicates the
  23062.      ' functionality of the QuickBASIC Search-Change dialog box.
  23063.      ' =======================================================================
  23064.  
  23065.      ' =======================================================================
  23066.      ' Open Window, place a horizontal line on row 13
  23067.      ' =======================================================================
  23068.  
  23069.      WindowOpen 1, 6, 11, 19, 67, 0, 7, 0, 7, 15, FALSE, FALSE, FALSE, TRUE, 1
  23070.  
  23071.      WindowLine 13
  23072.  
  23073.      ' =======================================================================
  23074.      ' Print the text, and boxes for the edit fields
  23075.      ' =======================================================================
  23076.  
  23077.      WindowLocate 2, 2
  23078.      WindowPrint 2, "Find What:"
  23079.      WindowBox 1, 14, 3, 56
  23080.  
  23081.      WindowLocate 5, 2
  23082.      WindowPrint 2, "Change To:"
  23083.      WindowBox 4, 14, 6, 56
  23084.  
  23085.  
  23086.      ' =======================================================================
  23087.      ' Print the title of the window -- This overides the string in WindowOpen
  23088.      ' =======================================================================
  23089.  
  23090.      WindowLocate 0, 26
  23091.      WindowPrint 1, " Change "
  23092.  
  23093.      WindowBox 8, 32, 12, 56
  23094.  
  23095.      ' =======================================================================
  23096.      ' Open Edit fields
  23097.      ' =======================================================================
  23098.  
  23099.      search$ = ""
  23100.      replace$ = ""
  23101.      EditFieldOpen 1, search$, 2, 15, 0, 0, 40, 39
  23102.  
  23103.      EditFieldOpen 2, replace$, 5, 15, 0, 0, 40, 39
  23104.  
  23105.      ' =======================================================================
  23106.      ' Open all buttons
  23107.      ' =======================================================================
  23108.  
  23109.      ButtonOpen 1, 1, "Match Upper/Lowercase", 9, 2, 0, 0, 2
  23110.      ButtonOpen 2, 1, "Whole Word", 10, 2, 0, 0, 2
  23111.      ButtonOpen 3, 1, "1. Active Window", 9, 34, 0, 0, 3
  23112.      ButtonOpen 4, 2, "2. Current Module", 10, 34, 0, 0, 3
  23113.      ButtonOpen 5, 1, "3. All Modules", 11, 34, 0, 0, 3
  23114.      ButtonOpen 6, 2, "Find and Verify", 14, 2, 0, 0, 1
  23115.      ButtonOpen 7, 1, "Change All", 14, 22, 0, 0, 1
  23116.      ButtonOpen 8, 1, "Cancel", 14, 38, 0, 0, 1
  23117.      ButtonOpen 9, 1, "Help", 14, 49, 0, 0, 1
  23118.  
  23119.      ' =======================================================================
  23120.      ' Set initial states to match initial button settings
  23121.      ' =======================================================================
  23122.  
  23123.      MatchState = FALSE
  23124.      WordState = FALSE
  23125.      searchState = 2
  23126.      pushButton = 1
  23127.      currButton = 0
  23128.      currEditField = 1
  23129.  
  23130.      ' =======================================================================
  23131.      ' Do until exitFlag is set
  23132.      ' =======================================================================
  23133.  
  23134.      ExitFlag = FALSE
  23135.      WHILE NOT ExitFlag
  23136.          WindowDo currButton, currEditField
  23137.          SELECT CASE Dialog(0)
  23138.              CASE 0, 3, 4, 5, 20
  23139.  
  23140.              ' ==============================================================
  23141.              ' If edit field clicked, assign currEditField to Dialog(2)
  23142.              ' ==============================================================
  23143.  
  23144.              CASE 2
  23145.                  currButton = 0
  23146.                  currEditField = Dialog(2)
  23147.  
  23148.              ' ==============================================================
  23149.              ' If escape is hit,  set pushbutton = 0 and exit flag
  23150.              ' ==============================================================
  23151.  
  23152.              CASE 9  '(Escape)
  23153.                  pushButton = 3
  23154.                  ExitFlag = TRUE
  23155.  
  23156.              ' ==============================================================
  23157.              ' If return is hit, perform action based on the current button
  23158.              ' Button 9 is the help button.  In that case, show help, else jus
  23159.              ' exit
  23160.              ' ==============================================================
  23161.  
  23162.              CASE 6
  23163.                  SELECT CASE currButton
  23164.                      CASE 9
  23165.                          a$ = "Sample Help Window"
  23166.                          ButtonSetState pushButton + 5, 1
  23167.                          pushButton = 4
  23168.                          ButtonSetState pushButton + 5, 2
  23169.                          junk = Alert(4, a$, 7, 9, 19, 69, "", "", "")
  23170.                      CASE ELSE
  23171.                          ExitFlag = TRUE
  23172.                  END SELECT
  23173.  
  23174.  
  23175.              ' ==============================================================
  23176.              ' A Button was pushed with mouse. Perform the desired action
  23177.              ' based on Button
  23178.              ' ==============================================================
  23179.  
  23180.              CASE 1
  23181.                  currButton = Dialog(1)
  23182.                  currEditField = 0
  23183.                  SELECT CASE currButton
  23184.                      CASE 1
  23185.                          MatchState = NOT MatchState
  23186.                          ButtonToggle 1
  23187.                      CASE 2
  23188.                          WordState = NOT WordState
  23189.                          ButtonToggle 2
  23190.                      CASE 3, 4, 5
  23191.                          ButtonSetState searchState + 2, 1
  23192.                          searchState = Dialog(1) - 2
  23193.                          ButtonSetState searchState + 2, 2
  23194.                      CASE 6, 7, 8
  23195.                          pushButton = Dialog(1) - 5
  23196.                          ExitFlag = TRUE
  23197.                      CASE 9
  23198.                          a$ = "Sample Help Window"
  23199.                          ButtonSetState pushButton + 5, 1
  23200.                          pushButton = Dialog(1) - 5
  23201.                          ButtonSetState pushButton + 5, 2
  23202.                          junk = Alert(4, a$, 7, 9, 19, 69, "", "", "")
  23203.                      CASE ELSE
  23204.                  END SELECT
  23205.  
  23206.  
  23207.              ' ==============================================================
  23208.              ' Tab was hit.  Depending upon the current button, or current edi
  23209.              ' assign the new values to currButton, and currEditField
  23210.              ' ==============================================================
  23211.  
  23212.              CASE 7  'tab
  23213.                  SELECT CASE currButton
  23214.                      CASE 0
  23215.                          SELECT CASE currEditField
  23216.                              CASE 1
  23217.                                  currEditField = 2
  23218.  
  23219.                              CASE ELSE
  23220.                                  currButton = 1
  23221.                                  currEditField = 0
  23222.                          END SELECT
  23223.                      CASE 1
  23224.                          currButton = 2
  23225.                      CASE 6, 7, 8
  23226.                          currButton = currButton + 1
  23227.                          ButtonSetState pushButton + 5, 1
  23228.                          pushButton = currButton - 5
  23229.                          ButtonSetState pushButton + 5, 2
  23230.                      CASE 3, 4, 5
  23231.                          currButton = 6
  23232.                      CASE 2
  23233.                          currButton = 2 + searchState
  23234.                      CASE 9
  23235.                          currButton = 0
  23236.                          ButtonSetState pushButton + 5, 1
  23237.                          pushButton = 1
  23238.                          ButtonSetState pushButton + 5, 2
  23239.                          currEditField = 1
  23240.                  END SELECT
  23241.  
  23242.  
  23243.              ' ==============================================================
  23244.              ' Same for Back Tab, only reverse.
  23245.              ' ==============================================================
  23246.  
  23247.              CASE 8 'back tab
  23248.                  SELECT CASE currButton
  23249.                      CASE 0
  23250.                          SELECT CASE currEditField
  23251.                              CASE 1
  23252.                                  currButton = 9
  23253.                                  ButtonSetState pushButton + 5, 1
  23254.                                  pushButton = currButton - 5
  23255.                                  ButtonSetState pushButton + 5, 2
  23256.                                  currEditField = 0
  23257.                              CASE 2
  23258.                                  currEditField = 1
  23259.                          END SELECT
  23260.                      CASE 1
  23261.                          currButton = 0
  23262.                          currEditField = 2
  23263.                      CASE 7, 8, 9
  23264.                          currButton = currButton - 1
  23265.                          ButtonSetState pushButton + 5, 1
  23266.                          pushButton = currButton - 5
  23267.                          ButtonSetState pushButton + 5, 2
  23268.                      CASE 3, 4, 5
  23269.                          currButton = 2
  23270.                      CASE 6
  23271.                          currButton = 2 + searchState
  23272.                      CASE 2
  23273.                          currButton = 1
  23274.                  END SELECT
  23275.  
  23276.  
  23277.              ' ==============================================================
  23278.              ' Up arrow only affects buttons 1,2,3,4,5  (the radial and check
  23279.              ' buttons)
  23280.              ' ==============================================================
  23281.  
  23282.              CASE 10 'up arrow
  23283.                  SELECT CASE currButton
  23284.                      CASE 1
  23285.                          IF NOT MatchState THEN
  23286.                              MatchState = TRUE
  23287.                              ButtonToggle 1
  23288.                          END IF
  23289.                      CASE 2
  23290.                          IF NOT WordState THEN
  23291.                              WordState = TRUE
  23292.                              ButtonToggle 2
  23293.                          END IF
  23294.                      CASE 3
  23295.                          ButtonSetState searchState + 2, 1
  23296.                          searchState = 3
  23297.                          currButton = 5
  23298.                          ButtonSetState searchState + 2, 2
  23299.                      CASE 4, 5
  23300.                          ButtonSetState searchState + 2, 1
  23301.                          searchState = searchState - 1
  23302.                          currButton = currButton - 1
  23303.                          ButtonSetState searchState + 2, 2
  23304.                  END SELECT
  23305.  
  23306.  
  23307.              ' ==============================================================
  23308.              ' Same with down arrow, only reverse
  23309.              ' ==============================================================
  23310.  
  23311.              CASE 11 'down
  23312.                  SELECT CASE currButton
  23313.                      CASE 1
  23314.                          IF MatchState THEN
  23315.                              MatchState = NOT MatchState
  23316.                              ButtonToggle 1
  23317.                          END IF
  23318.                      CASE 2
  23319.                          IF WordState THEN
  23320.                              WordState = NOT WordState
  23321.                              ButtonToggle 2
  23322.                          END IF
  23323.                      CASE 3, 4
  23324.                          ButtonSetState searchState + 2, 1
  23325.                          searchState = searchState + 1
  23326.                          currButton = currButton + 1
  23327.                          ButtonSetState searchState + 2, 2
  23328.                      CASE 5
  23329.                          ButtonSetState searchState + 2, 1
  23330.                          searchState = 1
  23331.                          currButton = 3
  23332.                          ButtonSetState searchState + 2, 2
  23333.                  END SELECT
  23334.  
  23335.              ' ==============================================================
  23336.              ' Left arrow only affects button 1 and 2  (the check buttons)
  23337.              ' ==============================================================
  23338.  
  23339.              CASE 12 'Left Arrow
  23340.                 SELECT CASE currButton
  23341.                      CASE 1
  23342.                          IF NOT MatchState THEN
  23343.                              MatchState = TRUE
  23344.                              ButtonToggle 1
  23345.                          END IF
  23346.                      CASE 2
  23347.                          IF NOT WordState THEN
  23348.                              WordState = TRUE
  23349.                              ButtonToggle 2
  23350.                          END IF
  23351.                      CASE 3
  23352.                          ButtonSetState searchState + 2, 1
  23353.                          searchState = 3
  23354.                          currButton = 5
  23355.                          ButtonSetState searchState + 2, 2
  23356.  
  23357.                      CASE 4, 5
  23358.                          ButtonSetState searchState + 2, 1
  23359.                          searchState = searchState - 1
  23360.                          currButton = currButton - 1
  23361.                          ButtonSetState searchState + 2, 2
  23362.  
  23363.                 END SELECT
  23364.  
  23365.  
  23366.              ' ==============================================================
  23367.              ' Right arrow only affects button 1 and 2  (the check buttons)
  23368.              ' ==============================================================
  23369.  
  23370.              CASE 13 'Right Arrow
  23371.                  SELECT CASE currButton
  23372.                      CASE 1
  23373.                          IF MatchState THEN
  23374.                              MatchState = NOT MatchState
  23375.                              ButtonToggle 1
  23376.                          END IF
  23377.                      CASE 2
  23378.                          IF WordState THEN
  23379.                              WordState = NOT WordState
  23380.                              ButtonToggle 2
  23381.                          END IF
  23382.                      CASE 3, 4
  23383.                          ButtonSetState searchState + 2, 1
  23384.                          searchState = searchState + 1
  23385.                          currButton = currButton + 1
  23386.                          ButtonSetState searchState + 2, 2
  23387.                      CASE 5
  23388.                          ButtonSetState searchState + 2, 1
  23389.                          searchState = 1
  23390.                          currButton = 3
  23391.                          ButtonSetState searchState + 2, 2
  23392.  
  23393.                  END SELECT
  23394.  
  23395.              ' ==============================================================
  23396.              ' Space will toggle a check button, or select a push button (incl
  23397.              ' ==============================================================
  23398.  
  23399.              CASE 14 'space
  23400.                  SELECT CASE currButton
  23401.                      CASE 1
  23402.                          MatchState = NOT MatchState
  23403.                          ButtonToggle 1
  23404.                      CASE 2
  23405.                          WordState = NOT WordState
  23406.                          ButtonToggle 2
  23407.                      CASE 6, 7, 8
  23408.                          pushButton = currButton - 5
  23409.                          ExitFlag = TRUE
  23410.                      CASE 9
  23411.                          a$ = "Sample Help Window"
  23412.                          ButtonSetState pushButton + 5, 1
  23413.                          pushButton = 4
  23414.                          ButtonSetState pushButton + 5, 2
  23415.                          junk = Alert(4, a$, 7, 9, 19, 69, "", "", "")
  23416.                      CASE ELSE
  23417.                  END SELECT
  23418.              CASE ELSE
  23419.          END SELECT
  23420.      WEND
  23421.  
  23422.  
  23423.      ' =======================================================================
  23424.      ' Prepare data for final alert box that says what the final state was.
  23425.      ' =======================================================================
  23426.  
  23427.      search$ = EditFieldInquire(1)
  23428.      replace$ = EditFieldInquire(2)
  23429.  
  23430.  
  23431.      WindowClose 1
  23432.      IF pushButton = 3 THEN
  23433.          a$ = "You Selected CANCEL"
  23434.          x = Alert(4, a$, 10, 25, 12, 55, "OK", "", "")
  23435.      ELSE
  23436.          IF pushButton = 1 THEN
  23437.              a$ = "You selected VERIFY.  Here are your other selections:| |"
  23438.          ELSE
  23439.              a$ = "You selected CHANGE ALL.  Here are your other selections:|
  23440.          END IF
  23441.  
  23442.          IF MatchState THEN
  23443.              a$ = a$ + "   Match Upper/Lowercase = Yes|"
  23444.          ELSE
  23445.              a$ = a$ + "   Match Upper/Lowercase = No|"
  23446.          END IF
  23447.  
  23448.          IF WordState THEN
  23449.              a$ = a$ + "   Whole Word            = Yes|"
  23450.          ELSE
  23451.              a$ = a$ + "   Whole Word            = No|"
  23452.          END IF
  23453.  
  23454.          SELECT CASE searchState
  23455.              CASE 1: a$ = a$ + "   Search space          = Active Window|"
  23456.              CASE 2: a$ = a$ + "   Search space          = Current Module|"
  23457.              CASE 3: a$ = a$ + "   Search space          = All Modules|"
  23458.          END SELECT
  23459.  
  23460.          a$ = a$ + "   Search string : " + search$ + "|"
  23461.          a$ = a$ + "   Replace string: " + replace$ + "|"
  23462.  
  23463.          x = Alert(2, a$, 7, 11, 15, 69, "OK", "", "")
  23464.      END IF
  23465.  END SUB
  23466.  
  23467.  SUB DemoDialogEZ
  23468.  
  23469.  
  23470.      ' =======================================================================
  23471.      ' Open Window, write text, and open button and edit field
  23472.      ' =======================================================================
  23473.  
  23474.      WindowOpen 1, 8, 20, 13, 60, 0, 7, 0, 7, 15, FALSE, FALSE, FALSE, TRUE, 1
  23475.  
  23476.      WindowLocate 2, 2
  23477.      WindowPrint 2, "Your Name:"
  23478.      WindowBox 1, 14, 3, 38
  23479.  
  23480.      EditFieldOpen 1, "", 2, 15, 0, 0, 23, 22
  23481.      WindowLine 5
  23482.      ButtonOpen 1, 2, "OK", 6, 17, 0, 0, 1
  23483.  
  23484.  
  23485.      ' =======================================================================
  23486.      ' Set initial state + go into main loop
  23487.      ' =======================================================================
  23488.  
  23489.      currButton = 0
  23490.      currEditField = 1
  23491.  
  23492.      ExitFlag = FALSE
  23493.  
  23494.      WHILE NOT ExitFlag
  23495.          WindowDo currButton, currEditField
  23496.          SELECT CASE Dialog(0)
  23497.              CASE 1, 6                       'Button, or Enter, exit loop
  23498.                  ExitFlag = TRUE
  23499.              CASE 2                          'EditField, switch to edit field
  23500.                  currButton = 0
  23501.                  currEditField = 1
  23502.              CASE 7, 8                       'tab and backTab, flip/flop state
  23503.                  IF currButton = 1 THEN
  23504.                      currButton = 0
  23505.                      currEditField = 1
  23506.                  ELSE
  23507.                      currButton = 1
  23508.                      currEditField = 0
  23509.                  END IF
  23510.              CASE 14                         'space - if on button then exit
  23511.                  IF currButton = 1 THEN
  23512.                      ExitFlag = TRUE
  23513.                  END IF
  23514.              CASE 9                          'escape
  23515.                  WindowClose 1
  23516.                  EXIT SUB
  23517.              CASE ELSE
  23518.          END SELECT
  23519.      WEND
  23520.  
  23521.      ' =======================================================================
  23522.      ' Assign the variable before closing the window, and close the window
  23523.      ' =======================================================================
  23524.  
  23525.      yourName$ = EditFieldInquire$(1)
  23526.  
  23527.      WindowClose 1
  23528.  
  23529.      IF LEN(yourName$) <> 0 THEN
  23530.          junk = Alert(4, "Hello " + yourName$ + ".", 10, 20, 12, 60, "OK", "",
  23531.      ELSE
  23532.          junk = Alert(4, "I understand. You wish to remain anonymous!", 10, 15
  23533.      END IF
  23534.  
  23535.  END SUB
  23536.  
  23537.  SUB DemoFileNameListBox
  23538.  
  23539.      WindowOpen 1, 8, 20, 15, 60, 0, 7, 0, 7, 15, FALSE, FALSE, FALSE, TRUE, 1
  23540.  
  23541.      WindowLocate 2, 4
  23542.      WindowPrint 4, "Enter a file specification:"
  23543.      WindowBox 3, 4, 5, 38
  23544.  
  23545.      EditFieldOpen 1, "*.*", 4, 5, 0, 0, 23, 22
  23546.      WindowLine 7
  23547.      ButtonOpen 1, 2, "OK", 8, 17, 0, 0, 1
  23548.  
  23549.      ' =======================================================================
  23550.      ' Set initial state + go into main loop
  23551.      ' =======================================================================
  23552.  
  23553.      currButton = 0
  23554.      currEditField = 1
  23555.  
  23556.      ExitFlag = FALSE
  23557.      WHILE NOT ExitFlag
  23558.          WindowDo currButton, currEditField
  23559.          SELECT CASE Dialog(0)
  23560.              CASE 1, 6                       'Button, or Enter, exit loop
  23561.                  ExitFlag = TRUE
  23562.              CASE 2                          'EditField, switch to edit field
  23563.                  currButton = 0
  23564.                  currEditField = 1
  23565.              CASE 7, 8                       'tab and backTab, flip/flop state
  23566.                  IF currButton = 1 THEN
  23567.                      currButton = 0
  23568.                      currEditField = 1
  23569.                  ELSE
  23570.                      currButton = 1
  23571.                      currEditField = 0
  23572.                  END IF
  23573.              CASE 9                          'escape
  23574.                  WindowClose 1
  23575.                  EXIT SUB
  23576.              CASE 14                         'space - if on button then exit
  23577.                  IF currButton = 1 THEN
  23578.                      ExitFlag = TRUE
  23579.                  END IF
  23580.              CASE ELSE
  23581.          END SELECT
  23582.      WEND
  23583.  
  23584.      ' =======================================================================
  23585.      ' Assign the variable before closing the window, and close the window
  23586.      ' =======================================================================
  23587.  
  23588.      FileSpec$ = EditFieldInquire$(1)
  23589.  
  23590.      ' =======================================================================
  23591.      ' Make sure its a valid file name
  23592.      ' =======================================================================
  23593.  
  23594.      delimit = INSTR(FileSpec$, ".")
  23595.  
  23596.      IF delimit THEN
  23597.          fileName$ = LEFT$(FileSpec$, delimit - 1)
  23598.          fileExt$ = RIGHT$(FileSpec$, LEN(FileSpec$) - (delimit))
  23599.      ELSE
  23600.          fileName$ = FileSpec$
  23601.          fileExt$ = ""
  23602.      END IF
  23603.  
  23604.      IF LEN(FileSpec$) = 0 OR LEN(fileName$) > 8 OR LEN(fileExt$) > 3 THEN
  23605.          WindowClose 1
  23606.          junk = Alert(4, "You didn't enter a valid file specification.", 10, 1
  23607.          EXIT SUB
  23608.      END IF
  23609.  
  23610.      FileCount = GetFileCount(FileSpec$)
  23611.  
  23612.      IF FileCount THEN
  23613.  
  23614.          REDIM FileList$(FileCount)
  23615.  
  23616.      ELSE
  23617.  
  23618.          WindowClose 1
  23619.          junk = Alert(4, "No match to your file specification could be found."
  23620.          EXIT SUB
  23621.      END IF
  23622.  
  23623.      FileList$(1) = DIR$(FileSpec$)
  23624.  
  23625.      FOR Indx = 2 TO FileCount
  23626.          FileList$(Indx) = DIR$
  23627.      NEXT Indx
  23628.  
  23629.      x = ListBox(FileList$(), UBOUND(FileList$))
  23630.  
  23631.      SELECT CASE x
  23632.          CASE 0
  23633.              junk = Alert(4, "You selected CANCEL", 10, 25, 12, 55, "OK", "",
  23634.          CASE ELSE
  23635.              junk = Alert(4, "You selected " + FileList$(x), 10, 25, 12, 55, "
  23636.      END SELECT
  23637.  
  23638.      WindowClose 1
  23639.  END SUB
  23640.  
  23641.  SUB DemoListBox
  23642.  
  23643.      REDIM x$(30), y$(30)
  23644.  
  23645.      x$(1) = "Orange":                 y$(1) = "Orange you glad I didn't say B
  23646.      x$(2) = "Butter":                 y$(2) = "Try margarine! less cholestero
  23647.      x$(3) = "Corn":                   y$(3) = "Some people call it maize."
  23648.      x$(4) = "Potato":                 y$(4) = "Wouldn't you prefer stuffing?"
  23649.      x$(5) = "Grape":                  y$(5) = "Grape balls of fire!"
  23650.      x$(6) = "Cherry":                 y$(6) = "Don't chop down the tree!"
  23651.      x$(7) = "Lettuce":                y$(7) = "Two heads are better than one.
  23652.      x$(8) = "Lima bean":              y$(8) = "Who's Lima? and why do I have
  23653.      x$(9) = "Carrot":                 y$(9) = "What's up Doc?"
  23654.      x$(10) = "Rice":                  y$(10) = "Yes, but can you use chopstic
  23655.      x$(11) = "Steak":                 y$(11) = "Ooo.. Big spender."
  23656.      x$(12) = "Meatloaf":              y$(12) = "It must be Thursday."
  23657.      x$(13) = "Stuffing":              y$(13) = "Wouldn't you prefer potatoes?
  23658.      x$(14) = "Wine":                  y$(14) = "Remember: 'Party Responsibly.
  23659.      x$(15) = "Pea":                   y$(15) = "Comes with the princess."
  23660.      x$(16) = "Gravy":                 y$(16) = "like home made! (Only no lump
  23661.      x$(17) = "Pancake":               y$(17) = "Three for a dollar!"
  23662.      x$(18) = "Waffle":                y$(18) = "Syrup on your waffle sir?"
  23663.      x$(19) = "Broccoli":              y$(19) = "Little trees..."
  23664.      x$(20) = "Oatmeal":               y$(20) = "Yuck.."
  23665.  
  23666.      x = ListBox(x$(), 20)
  23667.  
  23668.      SELECT CASE x
  23669.          CASE 0
  23670.              y = Alert(4, "You Selected Cancel", 10, 25, 12, 55, "OK", "", "")
  23671.          CASE ELSE
  23672.              y = Alert(4, y$(x), 10, 38 - LEN(y$(x)) \ 2, 12, 43 + LEN(y$(x))
  23673.      END SELECT
  23674.  
  23675.  END SUB
  23676.  
  23677.  SUB DemoResize
  23678.  
  23679.      ' =======================================================================
  23680.      ' Define Window's text string
  23681.      ' =======================================================================
  23682.  
  23683.      REDIM x$(19)
  23684.      x$(1) = "Resize Me!  Hello there!  Welcome to the wonderful world"
  23685.      x$(2) = "of Windows.  This demo shows how BASIC programmers can"
  23686.      x$(3) = "use a re-sizable window in their own applications."
  23687.      x$(4) = ""
  23688.      x$(5) = "This demo consists of a single window (this window) which"
  23689.      x$(6) = "can be moved, closed, or re-sized.  When the user resizes"
  23690.      x$(7) = "a window, an event code of 5 is returned.  Upon receiving"
  23691.      x$(8) = "the event code, the programmer can then do whatever is"
  23692.      x$(9) = "needed to refresh the window. "
  23693.      x$(10) = ""
  23694.      x$(11) = "The text in this window simply truncates when the window"
  23695.      x$(12) = "is made smaller, but text can be made to wrap either by"
  23696.      x$(13) = "character, or at the spaces between words. The choice is"
  23697.      x$(14) = "the programmer's."
  23698.      x$(15) = ""
  23699.      x$(16) = "The programmer has many tools available to make the"
  23700.      x$(17) = "job very easy such as functions that return the window"
  23701.      x$(18) = "size, and simple one-line calls to perform actions like"
  23702.      x$(19) = "opening or closing a window. "
  23703.  
  23704.  
  23705.      ' =======================================================================
  23706.      ' Open up a resizeable window
  23707.      ' =======================================================================
  23708.  
  23709.      WindowOpen 1, 4, 5, 4, 16, 0, 7, 0, 7, 8, TRUE, TRUE, TRUE, FALSE, 1, "-W
  23710.  
  23711.      GOSUB DemoResizeDrawText
  23712.  
  23713.      ExitFlag = FALSE
  23714.  
  23715.      ' =======================================================================
  23716.      ' Process window events...
  23717.      '  IMPORTANT:  Window moving, and re-sizing is handled automatically
  23718.      '  The window type dictates when this is allowed to happen.
  23719.      ' =======================================================================
  23720.  
  23721.      WHILE NOT ExitFlag
  23722.          WindowDo 0, 0
  23723.          SELECT CASE Dialog(0)
  23724.              CASE 4, 9
  23725.                  WindowClose WindowCurrent         'Close current window
  23726.                  ExitFlag = TRUE
  23727.              CASE 5
  23728.                  GOSUB DemoResizeDrawText
  23729.              CASE 20
  23730.                  ExitFlag = TRUE                   'Exit if menu action
  23731.              CASE ELSE
  23732.          END SELECT
  23733.      WEND
  23734.  
  23735.      WindowClose 0
  23736.  
  23737.  EXIT SUB
  23738.  
  23739.  DemoResizeDrawText:
  23740.      WindowCls
  23741.  
  23742.      FOR a = 1 TO 19
  23743.          IF a <= WindowRows(1) THEN
  23744.              WindowLocate a, 1
  23745.              WindowPrint -1, x$(a)
  23746.         END IF
  23747.      NEXT a
  23748.  RETURN
  23749.  
  23750.  END SUB
  23751.  
  23752.  SUB DemoScrollBar
  23753.  
  23754.      ' =======================================================================
  23755.      ' Open up a closeable window
  23756.      ' =======================================================================
  23757.  
  23758.      IF NOT DisplayType THEN
  23759.          WindowOpen 1, 4, 10, 20, 70, 0, 7, 0, 7, 15, FALSE, TRUE, FALSE, FALS
  23760.      ELSE
  23761.          WindowOpen 1, 4, 10, 20, 70, 15, 5, 15, 5, 14, FALSE, TRUE, FALSE, FA
  23762.      END IF
  23763.  
  23764.      ButtonOpen 1, 3, "", 4, 4, 14, 4, 6
  23765.      ButtonOpen 2, 4, "", 4, 6, 14, 6, 6
  23766.      ButtonOpen 3, 5, "", 4, 8, 14, 8, 6
  23767.      ButtonOpen 4, 4, "", 4, 10, 14, 10, 6
  23768.      ButtonOpen 5, 4, "", 4, 12, 14, 12, 6
  23769.      ButtonOpen 6, 9, "", 4, 16, 4, 50, 7
  23770.      ButtonOpen 7, 9, "", 6, 16, 6, 50, 7
  23771.      ButtonOpen 8, 8, "", 8, 16, 8, 50, 7
  23772.      ButtonOpen 9, 10, "", 10, 16, 10, 50, 7
  23773.      ButtonOpen 10, 12, "", 12, 16, 12, 50, 7
  23774.      ButtonOpen 11, 11, "", 14, 16, 14, 50, 7
  23775.  
  23776.      ExitFlag = FALSE
  23777.  
  23778.      ' =======================================================================
  23779.      ' Process window events...
  23780.      '   IMPORTANT:  Window moving, and re-sizing is handled automatically
  23781.      '   The window type dictates when this is allowed to happen.
  23782.      ' =======================================================================
  23783.  
  23784.      WHILE NOT ExitFlag
  23785.          WindowDo 0, 0
  23786.          x = Dialog(0)
  23787.  
  23788.          SELECT CASE x
  23789.              CASE 1
  23790.                  button = Dialog(1)
  23791.  
  23792.                  scrollCode = Dialog(19)
  23793.                  currState = ButtonInquire(button)
  23794.  
  23795.                  SELECT CASE scrollCode
  23796.                      CASE -1
  23797.                          IF currState > 1 THEN
  23798.                              newState = currState - 1
  23799.                          END IF
  23800.                      CASE -2
  23801.                          IF currState < MaxScrollLength(button) THEN
  23802.                              newState = currState + 1
  23803.                          END IF
  23804.                      CASE ELSE
  23805.                          newState = scrollCode
  23806.                  END SELECT
  23807.  
  23808.                  ButtonSetState button, newState
  23809.  
  23810.              CASE 4, 9
  23811.                  WindowClose WindowCurrent         'Close current window
  23812.                  ExitFlag = TRUE
  23813.              CASE 20
  23814.                  ExitFlag = TRUE                   'Exit if menu action
  23815.              CASE ELSE
  23816.          END SELECT
  23817.      WEND
  23818.  
  23819.      WindowClose 0
  23820.  
  23821.  END SUB
  23822.  
  23823.  SUB DemoWindow
  23824.  
  23825.      REDIM z$(4 TO 6, 6)
  23826.  
  23827.      ' =======================================================================
  23828.      ' Open up 6 windows, showcase the features, and make each a different col
  23829.      ' =======================================================================
  23830.      IF NOT DisplayType THEN
  23831.          WindowOpen 1, 6, 5, 12, 25, 0, 7, 0, 7, 15, FALSE, FALSE, FALSE, FALS
  23832.      ELSE
  23833.          WindowOpen 1, 6, 5, 12, 25, 0, 4, 0, 4, 15, FALSE, FALSE, FALSE, FALS
  23834.      END IF
  23835.      WindowPrint 1, "Features:"
  23836.      WindowPrint 1, "No Title bar"
  23837.      WindowPrint 1, "No border"
  23838.  
  23839.      IF NOT DisplayType THEN
  23840.          WindowOpen 2, 8, 15, 14, 35, 0, 7, 0, 7, 15, TRUE, FALSE, FALSE, FALS
  23841.      ELSE
  23842.          WindowOpen 2, 8, 15, 14, 35, 0, 2, 0, 2, 15, TRUE, FALSE, FALSE, FALS
  23843.      END IF
  23844.      WindowPrint 1, "Features:"
  23845.      WindowPrint 1, "Title bar"
  23846.      WindowPrint 1, "Moveable window"
  23847.      WindowPrint 1, "Single-line border"
  23848.  
  23849.      IF NOT DisplayType THEN
  23850.          WindowOpen 3, 10, 25, 16, 45, 0, 7, 0, 7, 15, FALSE, TRUE, FALSE, FAL
  23851.      ELSE
  23852.          WindowOpen 3, 10, 25, 16, 45, 0, 3, 0, 3, 15, FALSE, TRUE, FALSE, FAL
  23853.      END IF
  23854.      WindowPrint 1, "Features:"
  23855.      WindowPrint 1, "Title bar"
  23856.      WindowPrint 1, "Closeable window"
  23857.      WindowPrint 1, "Single-line border"
  23858.  
  23859.      WindowOpen 4, 12, 35, 18, 55, 0, 7, 0, 7, 15, FALSE, FALSE, TRUE, FALSE,
  23860.      z$(4, 1) = "Features:"
  23861.      z$(4, 2) = "Title bar"
  23862.      z$(4, 3) = "Resizeable window"
  23863.      z$(4, 4) = "Single-line border"
  23864.      ValidLines = 4
  23865.      GOSUB DemoReDrawText
  23866.  
  23867.      IF NOT DisplayType THEN
  23868.          WindowOpen 5, 14, 45, 20, 65, 0, 7, 0, 7, 15, TRUE, TRUE, TRUE, FALSE
  23869.      ELSE
  23870.          WindowOpen 5, 14, 45, 20, 65, 0, 5, 0, 5, 15, TRUE, TRUE, TRUE, FALSE
  23871.      END IF
  23872.      z$(5, 1) = "Features:"
  23873.      z$(5, 2) = "Title bar"
  23874.      z$(5, 3) = "Moveable window"
  23875.      z$(5, 4) = "Closeable window"
  23876.      z$(5, 5) = "Resizeable window"
  23877.      z$(5, 6) = "Single-line border"
  23878.      ValidLines = 6
  23879.      GOSUB DemoReDrawText
  23880.  
  23881.      IF NOT DisplayType THEN
  23882.          WindowOpen 6, 16, 55, 22, 75, 0, 7, 0, 7, 15, TRUE, TRUE, TRUE, FALSE
  23883.      ELSE
  23884.          WindowOpen 6, 16, 55, 22, 75, 0, 6, 0, 6, 15, TRUE, TRUE, TRUE, FALSE
  23885.      END IF
  23886.      z$(6, 1) = "Features:"
  23887.      z$(6, 2) = "Title bar"
  23888.      z$(6, 3) = "Moveable window"
  23889.      z$(6, 4) = "Closeable window"
  23890.      z$(6, 5) = "Resizeable window"
  23891.      z$(6, 6) = "Double-line border"
  23892.      ValidLines = 6
  23893.      GOSUB DemoReDrawText
  23894.  
  23895.      ' =======================================================================
  23896.      ' Show alert box describing what is going on
  23897.      ' =======================================================================
  23898.  
  23899.           a$ = "WINDOWS:  This demo displays six windows, each representing on
  23900.      a$ = a$ + "or more of the features that are available.  You may use the "
  23901.      a$ = a$ + "mouse to select windows, move windows, resize windows, or clos
  23902.      a$ = a$ + "windows. You can also select border characters and define your
  23903.      a$ = a$ + "window title.| |You should know that this demo "
  23904.      a$ = a$ + "consists of only six window open commands, and a 12 line "
  23905.      a$ = a$ + "Select Case statement to handle the actual processing."
  23906.  
  23907.      choice = Alert(3, a$, 7, 15, 18, 65, "OK", "Cancel", "")
  23908.  
  23909.      IF choice = 1 THEN
  23910.          ExitFlag = FALSE
  23911.      ELSE
  23912.          ExitFlag = TRUE
  23913.      END IF
  23914.  
  23915.      ' =======================================================================
  23916.      ' Process window events...
  23917.      '  IMPORTANT:  Window moving, and re-sizing is handled automatically
  23918.      '  The windowtype dictates when this is allowed to happen.
  23919.      ' =======================================================================
  23920.  
  23921.      WHILE NOT ExitFlag
  23922.          WindowDo 0, 0
  23923.          SELECT CASE Dialog(0)
  23924.              CASE 3
  23925.                  WindowSetCurrent Dialog(3)        'Change current window
  23926.              CASE 4
  23927.                  WindowClose WindowCurrent         'Close current window
  23928.              CASE 5
  23929.                  GOSUB DemoReDrawText              'Redraw text when resizing
  23930.              CASE 9
  23931.                  ExitFlag = TRUE                   'Exit if escape key pressed
  23932.              CASE 20
  23933.                  ExitFlag = TRUE                   'Exit if menu action
  23934.              CASE ELSE
  23935.          END SELECT
  23936.      WEND
  23937.  
  23938.      WindowClose 0
  23939.  
  23940.  EXIT SUB
  23941.  
  23942.  DemoReDrawText:
  23943.      WindowCls
  23944.  
  23945.      FOR a = 1 TO ValidLines
  23946.          IF a <= WindowRows(WindowCurrent) THEN
  23947.              WindowLocate a, 1
  23948.              WindowPrint -1, z$(WindowCurrent, a)
  23949.          END IF
  23950.      NEXT a
  23951.  RETURN
  23952.  
  23953.  END SUB
  23954.  
  23955.  FUNCTION GetFileCount (FileSpec$)
  23956.      count = 0
  23957.      fileName$ = DIR$(FileSpec$)
  23958.      DO WHILE fileName$ <> ""
  23959.          count = count + 1
  23960.          fileName$ = DIR$
  23961.      LOOP
  23962.      GetFileCount = count
  23963.  END FUNCTION
  23964.  
  23965.  SUB MonoDisplay
  23966.      DisplayType = FALSE
  23967.      MouseHide
  23968.      SetupMenu
  23969.      MenuSetState 2, 1, 1
  23970.      MenuSetState 2, 2, 2
  23971.      SetupDesktop
  23972.      MenuShow
  23973.      MouseShow
  23974.  END SUB
  23975.  
  23976.  DEFSNG A-Z
  23977.  SUB SetupDesktop STATIC
  23978.  
  23979.      MouseHide
  23980.  
  23981.      WIDTH , 25
  23982.  
  23983.      IF DisplayType THEN
  23984.          COLOR 15, 1      'Color
  23985.      ELSE
  23986.          COLOR 15, 0      'Monochrome
  23987.      END IF
  23988.      CLS
  23989.  
  23990.      FOR a = 2 TO 80 STEP 4
  23991.          FOR B = 2 TO 25 STEP 2
  23992.              LOCATE B, a
  23993.              PRINT CHR$(250);
  23994.          NEXT B
  23995.      NEXT a
  23996.  
  23997.      MouseShow
  23998.  END SUB
  23999.  
  24000.  DEFINT A-Z
  24001.  SUB SetupMenu
  24002.  
  24003.      MenuSet 1, 0, 1, "Demos", 1
  24004.      MenuSet 1, 1, 1, "Alert Window         F1", 1
  24005.      MenuSet 1, 2, 1, "Dialog Box (Simple)  F2", 13
  24006.      MenuSet 1, 3, 1, "Dialog Box (Complex) F3", 13
  24007.      MenuSet 1, 4, 1, "List Boxes           F4", 1
  24008.      MenuSet 1, 5, 1, "List Box w/File List F5", 12
  24009.      MenuSet 1, 6, 1, "Scroll Bars          F6", 8
  24010.      MenuSet 1, 7, 1, "Windows - Multiple   F7", 11
  24011.      MenuSet 1, 8, 1, "Window - Resizable   F8", 10
  24012.      MenuSet 1, 9, 1, "-", 1
  24013.      MenuSet 1, 10, 1, "Exit             Ctrl-X", 2
  24014.  
  24015.      MenuSet 2, 0, 1, "Options", 1
  24016.      MenuSet 2, 1, 1, "Color", 1
  24017.      MenuSet 2, 2, 1, "Monochrome", 1
  24018.  
  24019.  
  24020.      MenuSet 3, 0, 1, "Help", 1
  24021.      MenuSet 3, 1, 1, "About This Demo", 12
  24022.      MenuSet 3, 2, 1, "About The User Interface", 11
  24023.      MenuSet 3, 3, 1, "About the Windows", 11
  24024.      MenuSet 3, 4, 1, "Using the Mouse", 11
  24025.      MenuSet 3, 5, 1, "Using Access Keys", 7
  24026.      MenuSet 3, 6, 1, "Using Quick Keys", 7
  24027.  
  24028.      ShortCutKeySet 1, 1, CHR$(0) + CHR$(59) ' F1
  24029.      ShortCutKeySet 1, 2, CHR$(0) + CHR$(60) ' F2
  24030.      ShortCutKeySet 1, 3, CHR$(0) + CHR$(61) ' F3
  24031.      ShortCutKeySet 1, 4, CHR$(0) + CHR$(62) ' F4
  24032.      ShortCutKeySet 1, 5, CHR$(0) + CHR$(63) ' F5
  24033.      ShortCutKeySet 1, 6, CHR$(0) + CHR$(64) ' F6
  24034.      ShortCutKeySet 1, 7, CHR$(0) + CHR$(65) ' F7
  24035.      ShortCutKeySet 1, 8, CHR$(0) + CHR$(66) ' F8
  24036.  
  24037.      ShortCutKeySet 1, 10, CHR$(24)          ' Ctrl-X
  24038.  
  24039.      IF NOT DisplayType THEN
  24040.          MenuColor 0, 7, 15, 8, 7, 0, 15   'Best for monochrome and colors
  24041.      ELSE
  24042.          MenuColor 0, 7, 4, 8, 15, 0, 12   'Best for color
  24043.      END IF
  24044.  
  24045.      MenuPreProcess
  24046.  
  24047.  END SUB
  24048.  
  24049.  
  24050.  
  24051.  WHEREIS.BAS
  24052.  CD-ROM Disc Path:   \SAMPCODE\BASIC\WHEREIS.BAS
  24053.  
  24054.  DEFINT A-Z
  24055.  
  24056.  ' Declare symbolic constants used in program:
  24057.  CONST EOFTYPE = 0, FILETYPE = 1, DIRTYPE = 2, ROOT = "TWH"
  24058.  
  24059.  DECLARE SUB ScanDir (PathSpec$, Level, FileSpec$, Row)
  24060.  
  24061.  DECLARE FUNCTION MakeFileName$ (Num)
  24062.  DECLARE FUNCTION GetEntry$ (FileNum, EntryType)
  24063.  CLS
  24064.  INPUT "File to look for"; FileSpec$
  24065.  PRINT
  24066.  PRINT "Enter the directory where the search should start"
  24067.  PRINT "(optional drive + directories). Press <ENTER> to "
  24068.  PRINT "begin search in root directory of current drive."
  24069.  PRINT
  24070.  INPUT "Starting directory"; PathSpec$
  24071.  CLS
  24072.  
  24073.  RightCh$ = RIGHT$(PathSpec$, 1)
  24074.  
  24075.  IF PathSpec$ = "" OR RightCh$ = ":" OR RightCh$ <> "\" THEN
  24076.          PathSpec$ = PathSpec$ + "\"
  24077.  END IF
  24078.  
  24079.  FileSpec$ = UCASE$(FileSpec$)
  24080.  PathSpec$ = UCASE$(PathSpec$)
  24081.  Level = 1
  24082.  Row = 3
  24083.  
  24084.  ' Make the top level call (level 1) to begin the search:
  24085.  ScanDir PathSpec$, Level, FileSpec$, Row
  24086.  
  24087.  KILL ROOT + ".*"        ' Delete all temporary files created
  24088.                          ' by the program.
  24089.  
  24090.  LOCATE Row + 1, 1: PRINT "Search complete."
  24091.  END
  24092.  ' ======================= GETENTRY ========================
  24093.  '    This procedure processes entry lines in a DIR listing
  24094.  '    saved to a file.
  24095.  
  24096.  '    This procedure returns the following values:
  24097.  ' ===================== MAKEFILENAME$ =====================
  24098.  '    This procedure makes a file name from a root string
  24099.  '    ("TWH," defined as a symbolic constant at the module
  24100.  '    level) and a number passed to it as an argument (Num).
  24101.  ' =========================================================
  24102.  
  24103.  ' ======================= SCANDIR =========================
  24104.  '   This procedure recursively scans a directory for the
  24105.  '   file name entered by the user.
  24106.  
  24107.  '   NOTE: The SUB header doesn't use the STATIC keyword
  24108.  '         since this procedure needs a new set of variables
  24109.  '         each time it is invoked.
  24110.  ' =========================================================
  24111.  
  24112.  '  GetEntry$   A valid file or directory name
  24113.  '  EntryType   If equal to 1, then GetEntry$
  24114.  '        is a file.
  24115.  '        If equal to 2, then GetEntry$
  24116.  '        is a directory.
  24117.  ' =========================================================
  24118.  FUNCTION GetEntry$ (FileNum, EntryType) STATIC
  24119.  
  24120.          ' Loop until a valid entry or end-of-file (EOF) is read:
  24121.          DO UNTIL EOF(FileNum)
  24122.                  LINE INPUT #FileNum, EntryLine$
  24123.                  IF EntryLine$ <> "" THEN
  24124.  
  24125.                          ' Get first character from the line for test:
  24126.           TestCh$ = LEFT$(EntryLine$, 1)
  24127.           IF TestCh$ <> " " AND TestCh$ <> "." THEN EXIT DO
  24128.                  END IF
  24129.          LOOP
  24130.  
  24131.          ' Entry or EOF found, decide which:
  24132.          IF EOF(FileNum) THEN    ' EOF, so return EOFTYPE
  24133.                  EntryType = EOFTYPE  ' in EntryType.
  24134.                  GetEntry$ = ""
  24135.  
  24136.          ELSE                 ' Not EOF, so it must be a
  24137.                                          ' file or a directory.
  24138.  
  24139.                  ' Build and return the entry name:
  24140.                  EntryName$ = RTRIM$(LEFT$(EntryLine$, 8))
  24141.  
  24142.                  ' Test for extension and add to name if there is one:
  24143.                  EntryExt$ = RTRIM$(MID$(EntryLine$, 10, 3))
  24144.                  IF EntryExt$ <> "" THEN
  24145.                          GetEntry$ = EntryName$ + "." + EntryExt$
  24146.                  ELSE
  24147.           GetEntry$ = EntryName$
  24148.                  END IF
  24149.  
  24150.                  ' Determine the entry type, and return that value
  24151.                  ' to the point where GetEntry$ was called:
  24152.                  IF MID$(EntryLine$, 15, 3) = "DIR" THEN
  24153.           EntryType = DIRTYPE            ' Directory
  24154.                  ELSE
  24155.           EntryType = FILETYPE           ' File
  24156.                  END IF
  24157.  
  24158.          END IF
  24159.  
  24160.  END FUNCTION
  24161.  
  24162.  FUNCTION MakeFileName$ (Num) STATIC
  24163.  
  24164.          MakeFileName$ = ROOT + "." + LTRIM$(STR$(Num))
  24165.  
  24166.  END FUNCTION
  24167.  
  24168.  SUB ScanDir (PathSpec$, Level, FileSpec$, Row)
  24169.  
  24170.          LOCATE 1, 1: PRINT "Now searching"; SPACE$(50);
  24171.          LOCATE 1, 15: PRINT PathSpec$;
  24172.  
  24173.          ' Make a file specification for the temporary file:
  24174.          TempSpec$ = MakeFileName$(Level)
  24175.  
  24176.          ' Get a directory listing of the current directory,
  24177.          ' and save it in the temporary file:
  24178.          SHELL "DIR " + PathSpec$ + " > " + TempSpec$
  24179.  
  24180.          ' Get the next available file number:
  24181.          FileNum = FREEFILE
  24182.  
  24183.          ' Open the DIR listing file and scan it:
  24184.          OPEN TempSpec$ FOR INPUT AS #FileNum
  24185.  ' Process the file, one line at a time:
  24186.          DO
  24187.  
  24188.                  ' Input an entry from the DIR listing file:
  24189.                  DirEntry$ = GetEntry$(FileNum, EntryType)
  24190.  
  24191.                  ' If entry is a file:
  24192.                  IF EntryType = FILETYPE THEN
  24193.  
  24194.           ' If the FileSpec$ string matches,
  24195.           ' print entry and exit this loop:
  24196.           IF DirEntry$ = FileSpec$ THEN
  24197.                   LOCATE Row, 1: PRINT PathSpec$; DirEntry$;
  24198.                   Row = Row + 1
  24199.                   EntryType = EOFTYPE
  24200.           END IF
  24201.  
  24202.                  ' If the entry is a directory, then make a recursive
  24203.                  ' call to ScanDir with the new directory:
  24204.                  ELSEIF EntryType = DIRTYPE THEN
  24205.           NewPath$ = PathSpec$ + DirEntry$ + "\"
  24206.           ScanDir NewPath$, Level + 1, FileSpec$, Row
  24207.           LOCATE 1, 1: PRINT "Now searching"; SPACE$(50);
  24208.           LOCATE 1, 15: PRINT PathSpec$;
  24209.                  END IF
  24210.  
  24211.          LOOP UNTIL EntryType = EOFTYPE
  24212.  
  24213.          ' Scan on this DIR listing file is finished, so close it:
  24214.          CLOSE FileNum
  24215.  END SUB
  24216.  
  24217.  
  24218.  
  24219.  WINDOW.BAS
  24220.  CD-ROM Disc Path:   \SAMPCODE\BASIC\WINDOW.BAS
  24221.  
  24222.  '============================================================================
  24223.  '
  24224.  '    WINDOW.BAS - Window Routines for the User Interface Toolbox in
  24225.  '           Microsoft BASIC 7.0, Professional Development System
  24226.  '              Copyright (C) 1987-1989, Microsoft Corporation
  24227.  '
  24228.  '  NOTE:
  24229.  '           This sample source code toolbox is intended to demonstrate some
  24230.  '           of the extended capabilities of Microsoft BASIC 7.0 Professional
  24231.  '           Development system that can help to leverage the professional
  24232.  '           developer's time more effectively.  While you are free to use,
  24233.  '           modify, or distribute the routines in this module in any way you
  24234.  '           find useful, it should be noted that these are examples only and
  24235.  '           should not be relied upon as a fully-tested "add-on" library.
  24236.  '
  24237.  '  PURPOSE: These routines provide dialog box and window support to the
  24238.  '           user interface toolbox.
  24239.  '
  24240.  '  For information on creating a library and QuickLib from the routines
  24241.  '  contained in this file, read the comment header of GENERAL.BAS.
  24242.  '
  24243.  '==========================================================================
  24244.  
  24245.  DEFINT A-Z
  24246.  
  24247.  '$INCLUDE: 'general.bi'
  24248.  '$INCLUDE: 'mouse.bi'
  24249.  '$INCLUDE: 'menu.bi'
  24250.  '$INCLUDE: 'window.bi'
  24251.  
  24252.  
  24253.  COMMON SHARED /uitools/ GloMenu           AS MenuMiscType
  24254.  COMMON SHARED /uitools/ GloTitle()        AS MenuTitleType
  24255.  COMMON SHARED /uitools/ GloItem()         AS MenuItemType
  24256.  COMMON SHARED /uitools/ GloWindow()       AS windowType
  24257.  COMMON SHARED /uitools/ GloButton()       AS buttonType
  24258.  COMMON SHARED /uitools/ GloEdit()         AS EditFieldType
  24259.  COMMON SHARED /uitools/ GloStorage        AS WindowStorageType
  24260.  COMMON SHARED /uitools/ GloWindowStack()  AS INTEGER
  24261.  COMMON SHARED /uitools/ GloBuffer$()
  24262.  
  24263.  FUNCTION Alert (style, text$, row1, col1, row2, col2, b1$, b2$, b3$)
  24264.  
  24265.      ' =======================================================================
  24266.      ' Open an alert window, then return the button that was pushed
  24267.      ' =======================================================================
  24268.  
  24269.      Alert = 0
  24270.  
  24271.      ' =======================================================================
  24272.      ' Make sure coordinates and butttons are valid
  24273.      ' =======================================================================
  24274.  
  24275.      IF row1 >= MINROW AND row2 <= MAXROW AND col1 >= MINCOL AND col2 <= MAXCO
  24276.  
  24277.          IF b1$ = "" THEN
  24278.              b1$ = "OK"
  24279.              b2$ = ""
  24280.              b3$ = ""
  24281.          END IF
  24282.  
  24283.          IF b2$ = "" THEN
  24284.              b3$ = ""
  24285.          END IF
  24286.  
  24287.          ' ===================================================================
  24288.          ' If a window is available, compute button locations
  24289.          ' ===================================================================
  24290.  
  24291.          alertWindow = WindowNext
  24292.  
  24293.          IF alertWindow <> 0 THEN
  24294.  
  24295.              minWidth = 3
  24296.              buttonTotal = 0
  24297.  
  24298.              IF b1$ <> "" THEN
  24299.                  minWidth = minWidth + 7 + LEN(b1$):
  24300.                  buttonTotal = buttonTotal + 1
  24301.              END IF
  24302.  
  24303.              IF b2$ <> "" THEN
  24304.                  minWidth = minWidth + 7 + LEN(b2$)
  24305.                  buttonTotal = buttonTotal + 1
  24306.              END IF
  24307.  
  24308.              IF b3$ <> "" THEN
  24309.                  minWidth = minWidth + 7 + LEN(b3$)
  24310.                  buttonTotal = buttonTotal + 1
  24311.              END IF
  24312.  
  24313.              actualWidth = col2 - col1 + 1
  24314.              actualHeight = row2 - row1 + 1
  24315.  
  24316.              ' ===============================================================
  24317.              ' If size is valid, open window, print text, open buttons
  24318.              ' ===============================================================
  24319.  
  24320.              IF actualWidth >= minWidth AND actualHeight >= 3 THEN
  24321.  
  24322.                  WindowOpen alertWindow, row1, col1, row2, col2, 0, 7, 0, 7, 1
  24323.                  WindowLine actualHeight - 1
  24324.  
  24325.                  text$ = text$ + "|"
  24326.                  WHILE text$ <> ""
  24327.                      x$ = LEFT$(text$, INSTR(text$, "|") - 1)
  24328.                      text$ = RIGHT$(text$, LEN(text$) - LEN(x$) - 1)
  24329.                      WindowPrint style, x$
  24330.                  WEND
  24331.  
  24332.                  charTotal = LEN(b1$) + LEN(b2$) + LEN(b3$) + 4 * buttonTotal
  24333.                  avgSpace = INT((actualWidth - charTotal) / (buttonTotal + 1))
  24334.  
  24335.                  IF LEN(b1$) > 0 THEN
  24336.                      ButtonOpen 1, 2, b1$, actualHeight, avgSpace + 1, 0, 0, 1
  24337.                  END IF
  24338.  
  24339.                  IF LEN(b2$) > 0 THEN
  24340.                      ButtonOpen 2, 1, b2$, actualHeight, avgSpace * 2 + LEN(b1
  24341.                  END IF
  24342.  
  24343.                  IF LEN(b3$) > 0 THEN
  24344.                      ButtonOpen 3, 1, b3$, actualHeight, avgSpace * 3 + LEN(b1
  24345.                  END IF
  24346.  
  24347.                  ' ===========================================================
  24348.                  ' Main window processing loop
  24349.                  ' ===========================================================
  24350.  
  24351.                  currButton = 1
  24352.  
  24353.                  ExitFlag = FALSE
  24354.                  WHILE NOT ExitFlag
  24355.                      WindowDo currButton, 0
  24356.                      SELECT CASE Dialog(0)
  24357.                          CASE 1                      'Button Pressed
  24358.                              Alert = Dialog(1)
  24359.                              ExitFlag = TRUE
  24360.                          CASE 6, 14                  'Enter or Space
  24361.                              Alert = currButton
  24362.                              ExitFlag = TRUE
  24363.                          CASE 7                      'Tab
  24364.                              ButtonSetState currButton, 1
  24365.                              currButton = (currButton) MOD buttonTotal + 1
  24366.                              ButtonSetState currButton, 2
  24367.                          CASE 8                      'BackTab
  24368.                              ButtonSetState currButton, 1
  24369.                              currButton = (currButton + buttonTotal - 2) MOD b
  24370.                              ButtonSetState currButton, 2
  24371.                          CASE 9
  24372.                              IF UCASE$(b1$) = "CANCEL" THEN
  24373.                                  Alert = 1
  24374.                              END IF
  24375.                              IF UCASE$(b2$) = "CANCEL" THEN
  24376.                                  Alert = 2
  24377.                              END IF
  24378.                              IF UCASE$(b3$) = "CANCEL" THEN
  24379.                                  Alert = 3
  24380.                              END IF
  24381.                              ExitFlag = TRUE
  24382.                          CASE ELSE
  24383.                      END SELECT
  24384.                  WEND
  24385.  
  24386.                  WindowClose alertWindow
  24387.  
  24388.              END IF
  24389.          END IF
  24390.      END IF
  24391.  
  24392.  END FUNCTION
  24393.  
  24394.  SUB BackgroundRefresh (handle)
  24395.  
  24396.      ' =======================================================================
  24397.      ' Refresh the background behind a window
  24398.      ' =======================================================================
  24399.  
  24400.      IF GloWindow(handle).handle > 0 THEN
  24401.          MouseHide
  24402.          PutBackground GloWindow(handle).row1 - 1, GloWindow(handle).col1 - 1,
  24403.          MouseShow
  24404.      END IF
  24405.  END SUB
  24406.  
  24407.  SUB BackgroundSave (handle)
  24408.  
  24409.      ' =======================================================================
  24410.      ' Save the background before a window opens, or is moved... etc
  24411.      ' =======================================================================
  24412.  
  24413.      IF GloWindow(handle).handle > 0 THEN
  24414.          MouseHide
  24415.          GetBackground GloWindow(handle).row1 - 1, GloWindow(handle).col1 - 1,
  24416.          MouseShow
  24417.      END IF
  24418.  END SUB
  24419.  
  24420.  SUB ButtonClose (handle)
  24421.  
  24422.      ' =======================================================================
  24423.      ' Make sure a window is actually opened
  24424.      ' =======================================================================
  24425.  
  24426.      windo = WindowCurrent
  24427.  
  24428.      IF windo > 0 THEN
  24429.  
  24430.          ' ===================================================================
  24431.          ' If handle=0, recursively close all buttons in the CURRENT WINDOW on
  24432.          ' ===================================================================
  24433.  
  24434.          IF handle = 0 THEN
  24435.              IF GloStorage.numButtonsOpen > 0 THEN
  24436.                  FOR A = GloStorage.numButtonsOpen TO 1 STEP -1
  24437.                      IF GloButton(A).windowHandle = windo THEN
  24438.                          ButtonClose GloButton(A).handle
  24439.                      END IF
  24440.                  NEXT A
  24441.              END IF
  24442.          ELSE
  24443.              ' ===============================================================
  24444.              ' Get the index into the global array based on handle, and
  24445.              ' currWindow
  24446.              ' ===============================================================
  24447.  
  24448.              button = FindButton(handle)
  24449.  
  24450.              ' ===============================================================
  24451.              ' If valid, hide button, then squeeze array, decrement totals
  24452.              ' ===============================================================
  24453.  
  24454.              IF button > 0 THEN
  24455.  
  24456.                  COLOR GloWindow(windo).fore, GloWindow(windo).back
  24457.                  SELECT CASE GloButton(button).buttonType
  24458.                      CASE 1, 2, 3
  24459.                          LOCATE GloWindow(windo).row1 + GloButton(button).row1
  24460.                          MouseHide
  24461.                          PRINT SPACE$(4 + LEN(RTRIM$(GloButton(button).text$))
  24462.                          MouseShow
  24463.                      CASE 6
  24464.                          MouseHide
  24465.                          FOR A = 1 TO GloButton(button).row2 - GloButton(butto
  24466.                              LOCATE GloWindow(windo).row1 + GloButton(button).
  24467.                              PRINT " ";
  24468.                          NEXT A
  24469.                          MouseShow
  24470.                      CASE 7
  24471.                          LOCATE GloWindow(windo).row1 + GloButton(button).row1
  24472.                          MouseHide
  24473.                          PRINT SPACE$(GloButton(button).col2 - GloButton(butto
  24474.                          MouseShow
  24475.                      CASE ELSE
  24476.                  END SELECT
  24477.  
  24478.  
  24479.                  GloStorage.numButtonsOpen = GloStorage.numButtonsOpen - 1
  24480.                  WHILE button <= GloStorage.numButtonsOpen
  24481.                      GloButton(button).row1 = GloButton(button + 1).row1
  24482.                      GloButton(button).col1 = GloButton(button + 1).col1
  24483.                      GloButton(button).row2 = GloButton(button + 1).row2
  24484.                      GloButton(button).col2 = GloButton(button + 1).col2
  24485.                      GloButton(button).text = GloButton(button + 1).text
  24486.                      GloButton(button).handle = GloButton(button + 1).handle
  24487.                      GloButton(button).state = GloButton(button + 1).state
  24488.                      GloButton(button).buttonType = GloButton(button + 1).butt
  24489.                      GloButton(button).windowHandle = GloButton(button + 1).wi
  24490.                      button = button + 1
  24491.                  WEND
  24492.              END IF
  24493.          END IF
  24494.      END IF
  24495.  
  24496.  END SUB
  24497.  
  24498.  FUNCTION ButtonInquire (handle)
  24499.  
  24500.      ' =======================================================================
  24501.      ' If valid, return then state of the button
  24502.      ' =======================================================================
  24503.  
  24504.      button = FindButton(handle)
  24505.  
  24506.      IF button > 0 THEN
  24507.          ButtonInquire = GloButton(button).state
  24508.      ELSE
  24509.          ButtonInquire = 0
  24510.      END IF
  24511.  
  24512.  END FUNCTION
  24513.  
  24514.  SUB ButtonOpen (handle, state, title$, row1, col1, row2, col2, buttonType)
  24515.  
  24516.      ' =======================================================================
  24517.      ' Open a button - first check if window can be resized - If so, do not
  24518.      ' open!
  24519.      ' =======================================================================
  24520.  
  24521.      IF MID$(WindowBorder$(GloWindow(WindowCurrent).windowType), 9, 1) = "+" T
  24522.          resize = TRUE
  24523.      END IF
  24524.  
  24525.      IF (resize AND buttonType >= 6) OR NOT resize THEN
  24526.  
  24527.          ' ===================================================================
  24528.          ' If scroll bar, then make sure "state" is valid, given bar length
  24529.          ' ===================================================================
  24530.  
  24531.          IF buttonType = 6 THEN
  24532.              length = (row2 - row1) - 1
  24533.              IF state < 1 THEN state = 1
  24534.              IF state > length THEN state = length
  24535.          END IF
  24536.  
  24537.          IF buttonType = 7 THEN
  24538.              length = (col2 - col1) - 1
  24539.              IF state < 1 THEN state = 1
  24540.              IF state > length THEN state = length
  24541.          END IF
  24542.  
  24543.  
  24544.          ' ===================================================================
  24545.          ' If valid state and type, increment totals, and store button info
  24546.          ' ===================================================================
  24547.  
  24548.          IF (buttonType = 1 AND state >= 1 AND state <= 3) OR (buttonType >= 2
  24549.              ButtonClose handle
  24550.  
  24551.              GloStorage.numButtonsOpen = GloStorage.numButtonsOpen + 1
  24552.              GloButton(GloStorage.numButtonsOpen).row1 = row1
  24553.              GloButton(GloStorage.numButtonsOpen).col1 = col1
  24554.              GloButton(GloStorage.numButtonsOpen).row2 = row2
  24555.              GloButton(GloStorage.numButtonsOpen).col2 = col2
  24556.              GloButton(GloStorage.numButtonsOpen).text = title$
  24557.              GloButton(GloStorage.numButtonsOpen).state = state
  24558.              GloButton(GloStorage.numButtonsOpen).handle = handle
  24559.              GloButton(GloStorage.numButtonsOpen).buttonType = buttonType
  24560.              GloButton(GloStorage.numButtonsOpen).windowHandle = WindowCurrent
  24561.              ButtonShow handle
  24562.          ELSE
  24563.              PRINT "Cannot open button on window that can be re-sized!"
  24564.              END
  24565.          END IF
  24566.      END IF
  24567.  END SUB
  24568.  
  24569.  SUB ButtonSetState (handle, state)
  24570.  
  24571.      button = FindButton(handle)
  24572.      windo = WindowCurrent
  24573.  
  24574.      ' =======================================================================
  24575.      ' If valid state for the type of button, assign the new state, and re-sho
  24576.      ' =======================================================================
  24577.  
  24578.      IF button > 0 AND windo > 0 THEN
  24579.          SELECT CASE GloButton(button).buttonType
  24580.              CASE 1
  24581.                  IF state >= 1 AND state <= 3 THEN
  24582.                      GloButton(button).state = state
  24583.                  END IF
  24584.              CASE 2, 3
  24585.                  IF state = 1 OR state = 2 THEN
  24586.                      GloButton(button).state = state
  24587.                  END IF
  24588.              CASE 4, 5
  24589.              CASE 6
  24590.                  IF state <> GloButton(button).state THEN
  24591.                      MouseHide
  24592.                      COLOR 0, 7
  24593.                      LOCATE GloWindow(windo).row1 + GloButton(button).row1 - 1
  24594.                      PRINT CHR$(176);
  24595.                      GloButton(button).state = state
  24596.                      LOCATE GloWindow(windo).row1 + GloButton(button).row1 - 1
  24597.                      PRINT CHR$(219);
  24598.                      MouseShow
  24599.                  END IF
  24600.              CASE 7
  24601.                  IF state <> GloButton(button).state THEN
  24602.                      MouseHide
  24603.                      COLOR 0, 7
  24604.                      LOCATE GloWindow(windo).row1 + GloButton(button).row1 - 1
  24605.                      PRINT CHR$(176);
  24606.                      GloButton(button).state = state
  24607.                      LOCATE GloWindow(windo).row1 + GloButton(button).row1 - 1
  24608.                      PRINT CHR$(219);
  24609.                      MouseShow
  24610.                  END IF
  24611.              CASE ELSE
  24612.          END SELECT
  24613.      END IF
  24614.  
  24615.      ButtonShow handle
  24616.  END SUB
  24617.  
  24618.  SUB ButtonShow (handle)
  24619.  
  24620.      button = FindButton(handle)
  24621.      windo = WindowCurrent
  24622.  
  24623.      ' =======================================================================
  24624.      ' If valid, show the button based on button type and button state
  24625.      ' =======================================================================
  24626.  
  24627.      IF button > 0 THEN
  24628.          LOCATE GloWindow(windo).row1 + GloButton(button).row1 - 1, GloWindow(
  24629.  
  24630.          MouseHide
  24631.          SELECT CASE GloButton(button).buttonType
  24632.              CASE 1
  24633.                  SELECT CASE GloButton(button).state
  24634.                      CASE 1
  24635.                          COLOR GloWindow(windo).textFore, GloWindow(windo).tex
  24636.                          PRINT "< " + RTRIM$(GloButton(button).text$) + " >";
  24637.                      CASE 2
  24638.                          COLOR GloWindow(windo).highlight, GloWindow(windo).te
  24639.                          PRINT "<";
  24640.                          COLOR GloWindow(windo).textFore, GloWindow(windo).tex
  24641.                          PRINT " "; RTRIM$(GloButton(button).text$); " ";
  24642.                          COLOR GloWindow(windo).highlight, GloWindow(windo).te
  24643.                          PRINT ">";
  24644.                      CASE 3
  24645.                          COLOR GloWindow(windo).textBack, GloWindow(windo).tex
  24646.                          PRINT "< " + RTRIM$(GloButton(button).text$) + " >";
  24647.                  END SELECT
  24648.              CASE 2
  24649.                  SELECT CASE GloButton(button).state
  24650.                      CASE 1
  24651.                          COLOR GloWindow(windo).textFore, GloWindow(windo).tex
  24652.                          PRINT "[ ] " + RTRIM$(GloButton(button).text$);
  24653.                      CASE 2
  24654.                          COLOR GloWindow(windo).textFore, GloWindow(windo).tex
  24655.                          PRINT "[X] " + RTRIM$(GloButton(button).text$);
  24656.                  END SELECT
  24657.              CASE 3
  24658.                  SELECT CASE GloButton(button).state
  24659.                      CASE 1
  24660.                          COLOR GloWindow(windo).textFore, GloWindow(windo).tex
  24661.                          PRINT "( ) " + RTRIM$(GloButton(button).text$);
  24662.                      CASE 2
  24663.                          COLOR GloWindow(windo).textFore, GloWindow(windo).tex
  24664.                          PRINT "() " + RTRIM$(GloButton(button).text$);
  24665.                  END SELECT
  24666.              CASE 4, 5
  24667.              CASE 6
  24668.                  COLOR 0, 7
  24669.                  PRINT CHR$(24);
  24670.                  FOR A = 1 TO GloButton(button).row2 - GloButton(button).row1
  24671.                      LOCATE GloWindow(windo).row1 + GloButton(button).row1 - 1
  24672.                      IF A = GloButton(button).state THEN
  24673.                          PRINT CHR$(219);
  24674.                      ELSE
  24675.                          PRINT CHR$(176);
  24676.                      END IF
  24677.                  NEXT A
  24678.                  LOCATE GloWindow(windo).row1 + GloButton(button).row1 - 1 + A
  24679.                  PRINT CHR$(25);
  24680.              CASE 7
  24681.                  COLOR 0, 7
  24682.                  PRINT CHR$(27); STRING$(GloButton(button).col2 - GloButton(bu
  24683.                  LOCATE GloWindow(windo).row1 + GloButton(button).row1 - 1, Gl
  24684.                  PRINT CHR$(219);
  24685.              CASE ELSE
  24686.                  PRINT "Error in Button Parameter";
  24687.          END SELECT
  24688.          MouseShow
  24689.      END IF
  24690.  END SUB
  24691.  
  24692.  SUB ButtonToggle (handle)
  24693.  
  24694.      button = FindButton(handle)
  24695.      windo = WindowCurrent
  24696.  
  24697.      ' =======================================================================
  24698.      ' If valid button, and state is 1 or 2, toggle button
  24699.      ' =======================================================================
  24700.  
  24701.      IF button > 0 THEN
  24702.          IF GloButton(button).state = 1 OR GloButton(button).state = 2 THEN
  24703.             GloButton(button).state = 3 - GloButton(button).state
  24704.          END IF
  24705.      END IF
  24706.  
  24707.      ButtonShow handle
  24708.  
  24709.  END SUB
  24710.  
  24711.  FUNCTION Dialog (op)
  24712.  
  24713.      ' =======================================================================
  24714.      ' Based on global variables set in WindowDo, return proper event ID/Info
  24715.      ' =======================================================================
  24716.  
  24717.      SELECT CASE op
  24718.  
  24719.          ' ===================================================================
  24720.          ' Return event ID, and reset all variables.
  24721.          ' ===================================================================
  24722.  
  24723.          CASE 0
  24724.              GloStorage.DialogButton = GloStorage.oldDialogButton
  24725.              GloStorage.DialogEdit = GloStorage.oldDialogEdit
  24726.              GloStorage.DialogWindow = GloStorage.oldDialogWindow
  24727.              GloStorage.DialogClose = GloStorage.oldDialogClose
  24728.              GloStorage.DialogScroll = GloStorage.oldDialogScroll
  24729.              GloStorage.DialogRow = GloStorage.oldDialogRow
  24730.              GloStorage.DialogCol = GloStorage.oldDialogCol
  24731.              Dialog = GloStorage.oldDialogEvent
  24732.  
  24733.              GloStorage.oldDialogButton = 0
  24734.              GloStorage.oldDialogEdit = 0
  24735.              GloStorage.oldDialogWindow = 0
  24736.              GloStorage.oldDialogClose = 0
  24737.              GloStorage.oldDialogScroll = 0
  24738.              GloStorage.oldDialogRow = 0
  24739.              GloStorage.oldDialogCol = 0
  24740.  
  24741.          ' ===================================================================
  24742.          ' If button is pressed, dialog(0) is 1, and dialog(1) is the button
  24743.          ' number
  24744.          ' ===================================================================
  24745.  
  24746.          CASE 1
  24747.              Dialog = GloStorage.DialogButton
  24748.  
  24749.  
  24750.          ' ===================================================================
  24751.          ' If edit field is clicked, dialog(0) is 2, and dialog(2) is the edit
  24752.          ' field number
  24753.          ' ===================================================================
  24754.  
  24755.          CASE 2
  24756.              Dialog = GloStorage.DialogEdit
  24757.  
  24758.          ' ===================================================================
  24759.          ' If another window is clicked, dialog(0)=3, and dialog(3)=window
  24760.          ' number
  24761.          ' ===================================================================
  24762.  
  24763.          CASE 3
  24764.              Dialog = GloStorage.DialogWindow
  24765.  
  24766.          ' ===================================================================
  24767.          ' If a field button was pressed This returns the row (relative to
  24768.          ' window position) of the click
  24769.          ' ===================================================================
  24770.  
  24771.          CASE 17
  24772.              Dialog = GloStorage.DialogRow
  24773.  
  24774.          ' ===================================================================
  24775.          ' If a field button was pressed This returns the column (relative to
  24776.          ' window position) of the click
  24777.          ' ===================================================================
  24778.  
  24779.          CASE 18
  24780.              Dialog = GloStorage.DialogCol
  24781.  
  24782.          ' ===================================================================
  24783.          ' If a scroll bar was clicked, return new position of marker
  24784.          ' ===================================================================
  24785.  
  24786.          CASE 19
  24787.              Dialog = GloStorage.DialogScroll
  24788.  
  24789.          ' ===================================================================
  24790.          ' Bad call, so return 0
  24791.          ' ===================================================================
  24792.  
  24793.          CASE ELSE
  24794.              Dialog = 0
  24795.      END SELECT
  24796.  
  24797.  
  24798.  END FUNCTION
  24799.  
  24800.  SUB EditFieldClose (handle)
  24801.  
  24802.      ' =======================================================================
  24803.      ' Close an edit field
  24804.      ' =======================================================================
  24805.  
  24806.      windo = WindowCurrent
  24807.  
  24808.      IF windo > 0 THEN
  24809.          IF handle = 0 THEN
  24810.  
  24811.              ' ===============================================================
  24812.              ' If handle = 0, then recursivily close all edit fields
  24813.              ' ===============================================================
  24814.  
  24815.              IF GloStorage.numEditFieldsOpen > 0 THEN
  24816.                  FOR A = GloStorage.numEditFieldsOpen TO 1 STEP -1
  24817.                      IF GloEdit(A).windowHandle = windo THEN
  24818.                          EditFieldClose GloEdit(A).handle
  24819.                      END IF
  24820.                  NEXT A
  24821.              END IF
  24822.          ELSE
  24823.  
  24824.              ' ===============================================================
  24825.              ' else, erase edit field, then squeeze array, decrement total
  24826.              ' variables
  24827.              ' ===============================================================
  24828.  
  24829.              editField = FindEditField(handle)
  24830.  
  24831.              IF editField > 0 THEN
  24832.                  LOCATE GloWindow(windo).row1 + GloEdit(editField).row - 1, Gl
  24833.                  COLOR GloWindow(windo).fore, GloWindow(windo).back
  24834.                  MouseHide
  24835.                  PRINT SPACE$(GloEdit(editField).visLength);
  24836.                  MouseShow
  24837.  
  24838.                  GloStorage.numEditFieldsOpen = GloStorage.numEditFieldsOpen -
  24839.                  WHILE editField <= GloStorage.numEditFieldsOpen
  24840.                      GloEdit(editField).row = GloEdit(editField + 1).row
  24841.                      GloEdit(editField).col = GloEdit(editField + 1).col
  24842.                      GloEdit(editField).text = GloEdit(editField + 1).text
  24843.                      GloEdit(editField).handle = GloEdit(editField + 1).handle
  24844.                      GloEdit(editField).visLength = GloEdit(editField + 1).vis
  24845.                      GloEdit(editField).maxLength = GloEdit(editField + 1).max
  24846.                      GloEdit(editField).windowHandle = GloEdit(editField + 1).
  24847.                      editField = editField + 1
  24848.                  WEND
  24849.              END IF
  24850.          END IF
  24851.      END IF
  24852.  END SUB
  24853.  
  24854.  FUNCTION EditFieldInquire$ (handle)
  24855.  
  24856.      ' =======================================================================
  24857.      ' If valid edit field, return the value.  Note edit$ is terminated
  24858.      ' by a CHR$(0), or maxLength, or 255 chars.
  24859.      ' =======================================================================
  24860.  
  24861.      editField = FindEditField(handle)
  24862.      windo = WindowCurrent
  24863.      EditFieldInquire$ = ""
  24864.  
  24865.      IF editField > 0 THEN
  24866.          x$ = GloEdit(editField).text$
  24867.          x = INSTR(x$, CHR$(0)) - 1
  24868.          IF x >= 0 THEN
  24869.              EditFieldInquire$ = LEFT$(x$, x)
  24870.          ELSE
  24871.              EditFieldInquire$ = x$
  24872.          END IF
  24873.      END IF
  24874.  
  24875.  END FUNCTION
  24876.  
  24877.  SUB EditFieldOpen (handle, text$, row, col, fore, back, visLength, maxLength)
  24878.  
  24879.      ' =======================================================================
  24880.      ' If window can be re-sized, do not open edit field
  24881.      ' =======================================================================
  24882.  
  24883.      IF MID$(WindowBorder$(GloWindow(WindowCurrent).windowType), 9, 1) <> "+"
  24884.  
  24885.          ' ===================================================================
  24886.          ' Close edit field by the same handle if it exists
  24887.          ' ===================================================================
  24888.  
  24889.          EditFieldClose handle
  24890.  
  24891.          windo = WindowCurrent
  24892.  
  24893.          ' ===================================================================
  24894.          ' If no colors given, use default window colors
  24895.          ' ===================================================================
  24896.  
  24897.          IF fore = 0 AND back = 0 THEN
  24898.              fore = GloWindow(windo).fore
  24899.              back = GloWindow(windo).back
  24900.          END IF
  24901.  
  24902.          ' ===================================================================
  24903.          ' Increment totals, and store edit field info
  24904.          ' ===================================================================
  24905.  
  24906.          GloStorage.numEditFieldsOpen = GloStorage.numEditFieldsOpen + 1
  24907.          GloEdit(GloStorage.numEditFieldsOpen).row = row
  24908.          GloEdit(GloStorage.numEditFieldsOpen).col = col
  24909.          GloEdit(GloStorage.numEditFieldsOpen).fore = fore
  24910.          GloEdit(GloStorage.numEditFieldsOpen).back = back
  24911.          GloEdit(GloStorage.numEditFieldsOpen).text = text$ + CHR$(0)
  24912.          GloEdit(GloStorage.numEditFieldsOpen).visLength = visLength
  24913.          GloEdit(GloStorage.numEditFieldsOpen).maxLength = maxLength
  24914.          GloEdit(GloStorage.numEditFieldsOpen).windowHandle = windo
  24915.          GloEdit(GloStorage.numEditFieldsOpen).handle = handle
  24916.  
  24917.          LOCATE GloWindow(windo).row1 + row - 1, GloWindow(windo).col1 + col -
  24918.          COLOR fore, back
  24919.  
  24920.          'Create temp$ so that padding with spaces doesn't alter the original
  24921.          IF LEN(text$) < visLength THEN
  24922.              temp$ = text$ + SPACE$(visLength - LEN(text$))
  24923.          ELSE
  24924.              temp$ = LEFT$(text$, visLength)
  24925.          END IF
  24926.          PRINT temp$;
  24927.  
  24928.      ELSE
  24929.          PRINT "EditField cannot be opened on a window that can be re-sized!"
  24930.      END IF
  24931.  END SUB
  24932.  
  24933.  FUNCTION FindButton (handle)
  24934.  
  24935.      ' =======================================================================
  24936.      ' Given a handle, return the index into the global array that stores
  24937.      ' buttons.  Each button is uniquely described by a handle, and a window#
  24938.      ' This SUB program assumes that you want the current window.
  24939.      ' =======================================================================
  24940.  
  24941.      FindButton = 0
  24942.  
  24943.      IF GloStorage.numButtonsOpen > 0 THEN
  24944.          A = 0
  24945.          curr = WindowCurrent
  24946.          DO
  24947.              A = A + 1
  24948.          LOOP UNTIL (GloButton(A).handle = handle AND GloButton(A).windowHandl
  24949.  
  24950.          IF GloButton(A).handle = handle AND GloButton(A).windowHandle = curr
  24951.              FindButton = A
  24952.          END IF
  24953.      END IF
  24954.  
  24955.  END FUNCTION
  24956.  
  24957.  FUNCTION FindEditField (handle)
  24958.  
  24959.      ' =======================================================================
  24960.      ' Given a handle, return the index into the global array that stores
  24961.      ' edit fields.  Each button is uniquely described by a handle, and a
  24962.      ' window number. This SUB program assumes the you want the current window
  24963.      ' =======================================================================
  24964.  
  24965.      FindEditField = 0
  24966.  
  24967.      IF GloStorage.numEditFieldsOpen > 0 THEN
  24968.          A = 0
  24969.          curr = WindowCurrent
  24970.          DO
  24971.              A = A + 1
  24972.          LOOP UNTIL (GloEdit(A).handle = handle AND GloEdit(A).windowHandle =
  24973.  
  24974.          IF GloEdit(A).handle = handle AND GloEdit(A).windowHandle = curr THEN
  24975.              FindEditField = A
  24976.          END IF
  24977.      END IF
  24978.  
  24979.  END FUNCTION
  24980.  
  24981.  ' ==========================================================================
  24982.  ' The ListBox FUNCTION can be modified to accept a box width parameter. This
  24983.  ' will enable you to specify the width of a listbox when you call the ListBox
  24984.  ' FUNCTION. Below you will find two FUNCTION statements. The first is the
  24985.  ' default ListBox FUNCTION that takes only two arguments.  The second allows
  24986.  ' you to specify a box width parameter. As configured, the listbox width is
  24987.  ' assumed to be 14. This default is idal for listboxes that contain file
  24988.  ' names. To use the second form of the ListBox FUNCTION, that
  24989.  ' lets you specify the listbox width, comment out the first FUNCTION
  24990.  ' statement and remove the ' from the beginning of the second FUNCTION
  24991.  ' statement. Change the WINDOW.BI file so that the DECLARE statement matches
  24992.  ' the actual FUNCTION as follows:
  24993.  '
  24994.  ' DECLARE FUNCTION ListBox (Text$(), MaxRec%, BoxWidth%)
  24995.  '
  24996.  ' You also need to comment out the "BoxWidth = 14" statement that occurs just
  24997.  ' after second FUNCTION statement.
  24998.  '
  24999.  ' When you use the ListBox FUNCTION be sure to include a box width parameter
  25000.  ' as the third argument.  All calculations will be automatically performed
  25001.  ' to properly display the listbox.
  25002.  '
  25003.  ' ===========================================================================
  25004.  '
  25005.  FUNCTION ListBox (text$(), MaxRec)
  25006.  'FUNCTION ListBox (text$(), MaxRec, BoxWidth)
  25007.  
  25008.      ' Comment out the following line if you modify this function to allow
  25009.      ' specification of a ListBox width parameter in the function call.
  25010.  
  25011.      BoxWidth = 14
  25012.  
  25013.      GOSUB ListBoxWidthCalc
  25014.  
  25015.      ' =======================================================================
  25016.      ' Open up a modal window and put the right buttons in it
  25017.      ' =======================================================================
  25018.  
  25019.      WindowOpen 1, 4, StartRowPos, 20, StopRowPos, 0, 7, 0, 7, 15, 0, 0, 0, 1,
  25020.  
  25021.      WindowBox 1, 6, 14, BoxEndPos
  25022.      ButtonOpen 1, 1, "", 2, BoxEndPos, 13, BoxEndPos, 6     'Scroll Bar
  25023.      ButtonOpen 2, 2, "OK", 16, 6, 0, 0, 1                   'OK button
  25024.      ButtonOpen 3, 1, "Cancel", 16, BoxEndPos - 9, 0, 0, 1   'Cancel button
  25025.      ButtonOpen 4, 1, "", 1, 8, 1, AreaEndPos, 4             'Area above box
  25026.      ButtonOpen 5, 1, "", 2, 7, 13, AreaEndPos + 1, 4        'Area of box
  25027.      ButtonOpen 6, 1, "", 14, 8, 14, AreaEndPos, 4           'Area below box
  25028.  
  25029.      currTop = 1
  25030.      currPos = 1
  25031.      currButton = 2
  25032.  
  25033.      GOSUB ListBoxDrawText
  25034.  
  25035.      ExitFlag = FALSE
  25036.  
  25037.      ' =======================================================================
  25038.      ' Process window events...
  25039.      '  IMPORTANT:  Window moving, and re-sizing is handled automatically
  25040.      '  The window type dictates when this is allowed to happen.
  25041.      ' =======================================================================
  25042.  
  25043.      WHILE NOT ExitFlag
  25044.          WindowDo currButton, 0
  25045.          x = Dialog(0)
  25046.  
  25047.          SELECT CASE x
  25048.              CASE 1
  25049.                  button = Dialog(1)
  25050.                  SELECT CASE button
  25051.                      CASE 1
  25052.                          scrollCode = Dialog(19)
  25053.                          SELECT CASE scrollCode
  25054.                              CASE -1:   GOSUB ListBoxUp
  25055.                              CASE -2:   GOSUB ListBoxDown
  25056.                              CASE ELSE: GOSUB ListBoxMove
  25057.                          END SELECT
  25058.                      CASE 2
  25059.                          ListBox = currTop + currPos - 1
  25060.                          ExitFlag = TRUE
  25061.                      CASE 3
  25062.                          ListBox = 0
  25063.                          ExitFlag = TRUE
  25064.                      CASE 4
  25065.                          GOSUB ListBoxUp
  25066.                      CASE 5
  25067.                          GOSUB ListBoxAssign
  25068.                      CASE 6
  25069.                          GOSUB ListBoxDown
  25070.                  END SELECT
  25071.              CASE 6, 14
  25072.                  SELECT CASE currButton
  25073.                      CASE 0, 2
  25074.                          ListBox = currTop + currPos - 1
  25075.                          ExitFlag = TRUE
  25076.                      CASE 3
  25077.                          ListBox = 0
  25078.                          ExitFlag = TRUE
  25079.                      CASE ELSE
  25080.                  END SELECT
  25081.              CASE 7
  25082.                  SELECT CASE currButton
  25083.                      CASE 0
  25084.                          currButton = 2
  25085.                      CASE 2
  25086.                          ButtonToggle 2
  25087.                          ButtonToggle 3
  25088.                          currButton = 3
  25089.                      CASE 3
  25090.                          ButtonToggle 2
  25091.                          ButtonToggle 3
  25092.                          currButton = 0
  25093.                  END SELECT
  25094.              CASE 8
  25095.                  SELECT CASE currButton
  25096.                      CASE 0
  25097.                          ButtonToggle 2
  25098.                          ButtonToggle 3
  25099.                          currButton = 3
  25100.                      CASE 2
  25101.                          currButton = 0
  25102.                      CASE 3
  25103.                          ButtonToggle 2
  25104.                          ButtonToggle 3
  25105.                          currButton = 2
  25106.                  END SELECT
  25107.              CASE 9
  25108.                  ListBox = 0
  25109.                  ExitFlag = TRUE
  25110.              CASE 10, 12
  25111.                  IF currButton = 0 THEN
  25112.                      GOSUB ListBoxUp
  25113.                  END IF
  25114.              CASE 11, 13
  25115.                  IF currButton = 0 THEN
  25116.                      GOSUB ListBoxDown
  25117.                  END IF
  25118.              CASE 16
  25119.                  scrollCode = 1
  25120.                  GOSUB ListBoxMove
  25121.              CASE 17
  25122.                  scrollCode = 10
  25123.                  GOSUB ListBoxMove
  25124.              CASE 18
  25125.                  GOSUB ListBoxPgUp
  25126.              CASE 19
  25127.                  GOSUB ListBoxPgDn
  25128.              CASE ELSE
  25129.          END SELECT
  25130.      WEND
  25131.  
  25132.      WindowClose 0
  25133.      EXIT FUNCTION
  25134.  
  25135.  ListBoxUp:
  25136.      oldRec = currTop + currPos - 1
  25137.      currPos = currPos - 1
  25138.      IF currPos < 1 THEN
  25139.          currPos = 1
  25140.          currTop = currTop - 1
  25141.          IF currTop < 1 THEN
  25142.              currTop = 1
  25143.          END IF
  25144.      END IF
  25145.      newRec = currTop + currPos - 1
  25146.      IF oldRec <> newRec THEN
  25147.          GOSUB ListBoxDrawText
  25148.          GOSUB ListBoxNewBarPos
  25149.      END IF
  25150.  RETURN
  25151.  
  25152.  ListBoxDown:
  25153.      oldRec = currTop + currPos - 1
  25154.      IF MaxRec > 12 THEN
  25155.          currPos = currPos + 1
  25156.          IF currPos > 12 THEN
  25157.              currPos = 12
  25158.              currTop = currTop + 1
  25159.              IF currTop + currPos - 1 > MaxRec THEN
  25160.                  currTop = currTop - 1
  25161.              END IF
  25162.          END IF
  25163.      ELSE
  25164.          IF currPos + 1 <= MaxRec THEN
  25165.              currPos = currPos + 1
  25166.          END IF
  25167.      END IF
  25168.  
  25169.      newRec = currTop + currPos - 1
  25170.      IF oldRec <> newRec THEN
  25171.          GOSUB ListBoxDrawText
  25172.          GOSUB ListBoxNewBarPos
  25173.      END IF
  25174.  RETURN
  25175.  
  25176.  ListBoxPgUp:
  25177.      oldRec = currTop + currPos - 1
  25178.      currTop = currTop - 12
  25179.      IF currTop < 1 THEN
  25180.          currTop = 1
  25181.          currPos = 1
  25182.      END IF
  25183.      newRec = currTop + currPos - 1
  25184.      IF oldRec <> newRec THEN
  25185.          GOSUB ListBoxDrawText
  25186.          GOSUB ListBoxNewBarPos
  25187.      END IF
  25188.  RETURN
  25189.  
  25190.  ListBoxPgDn:
  25191.      oldRec = currTop + currPos - 1
  25192.      IF MaxRec > 12 THEN
  25193.          currTop = currTop + 12
  25194.              IF currTop + 12 > MaxRec THEN
  25195.                  currTop = MaxRec - 11
  25196.                  currPos = 12
  25197.              END IF
  25198.      ELSE
  25199.          currPos = MaxRec
  25200.      END IF
  25201.  
  25202.      newRec = currTop + currPos - 1
  25203.      IF oldRec <> newRec THEN
  25204.          GOSUB ListBoxDrawText
  25205.          GOSUB ListBoxNewBarPos
  25206.      END IF
  25207.  RETURN
  25208.  
  25209.  ListBoxAssign:
  25210.      currPos = Dialog(17)
  25211.      IF currPos > MaxRec THEN currPos = MaxRec
  25212.      GOSUB ListBoxDrawText
  25213.      GOSUB ListBoxNewBarPos
  25214.  
  25215.  RETURN
  25216.  
  25217.  ListBoxMove:
  25218.      SELECT CASE scrollCode
  25219.          CASE 1:      newPos = 1
  25220.          CASE 2 TO 9: newPos = scrollCode * MaxRec / 10
  25221.          CASE 10:     newPos = MaxRec
  25222.      END SELECT
  25223.  
  25224.      IF newPos < 1 THEN newPos = 1
  25225.      IF newPos > MaxRec THEN newPos = MaxRec
  25226.  
  25227.      currPos = newPos - currTop + 1
  25228.      IF currPos <= 0 THEN
  25229.          currTop = newPos
  25230.          currPos = 1
  25231.      ELSEIF currPos > 12 THEN
  25232.          currPos = 12
  25233.          currTop = newPos - 11
  25234.      END IF
  25235.      GOSUB ListBoxDrawText
  25236.      GOSUB ListBoxNewBarPos
  25237.  RETURN
  25238.  
  25239.  ListBoxDrawText:
  25240.      FOR A = currTop TO currTop + 11
  25241.          IF A <= MaxRec THEN
  25242.              IF currTop + currPos - 1 = A THEN
  25243.                  WindowColor 7, 0
  25244.              ELSE
  25245.                  WindowColor 0, 7
  25246.              END IF
  25247.  
  25248.              WindowLocate A - currTop + 2, 8
  25249.              WindowPrint -1, LEFT$(text$(A) + STRING$(BoxWidth + 1, " "), BoxW
  25250.          END IF
  25251.      NEXT A
  25252.      WindowColor 0, 7
  25253.      RETURN
  25254.  
  25255.  ListBoxNewBarPos:
  25256.      IF currPos = 1 AND currTop = 1 THEN
  25257.          newState = 1
  25258.      ELSE
  25259.          newState = (currTop + currPos - 1) * 10 / MaxRec
  25260.          IF newState < 1 THEN newState = 1
  25261.          IF newState > 10 THEN newState = 10
  25262.      END IF
  25263.      ButtonSetState 1, newState
  25264.  RETURN
  25265.  
  25266.  ListBoxWidthCalc:
  25267.      IF BoxWidth < 14 THEN BoxWidth = 14
  25268.      IF BoxWidth > 55 THEN BoxWidth = 55
  25269.      StartRowPos = 40 - ((BoxWidth + 14) / 2)
  25270.      StopRowPos = StartRowPos + BoxWidth + 14
  25271.      BoxEndPos = BoxWidth + 10
  25272.      AreaEndPos = BoxWidth + 8
  25273.  RETURN
  25274.  
  25275.  END FUNCTION
  25276.  
  25277.  FUNCTION MaxScrollLength (handle)
  25278.  
  25279.      ' =======================================================================
  25280.      ' If valid, return then maximum length of scroll bar
  25281.      ' =======================================================================
  25282.  
  25283.      button = FindButton(handle)
  25284.  
  25285.      IF button > 0 THEN
  25286.          SELECT CASE GloButton(button).buttonType
  25287.              CASE 6
  25288.                  MaxScrollLength = GloButton(button).row2 - GloButton(button).
  25289.              CASE 7
  25290.                  MaxScrollLength = GloButton(button).col2 - GloButton(button).
  25291.              CASE ELSE
  25292.                  MaxScrollLength = 0
  25293.          END SELECT
  25294.      ELSE
  25295.          MaxScrollLength = 0
  25296.      END IF
  25297.  
  25298.  END FUNCTION
  25299.  
  25300.  FUNCTION WhichWindow (row, col)
  25301.  
  25302.      ' =======================================================================
  25303.      ' Returns the window number where the row,col points to.  Takes into
  25304.      ' account which windows overlap which other windows by going down
  25305.      ' the GloWindowStack from the top.
  25306.      ' =======================================================================
  25307.  
  25308.      x = GloStorage.numWindowsOpen
  25309.      Found = FALSE
  25310.      WhichWindow = 0
  25311.  
  25312.      WHILE x > 0 AND NOT Found
  25313.          handle = GloWindowStack(x)
  25314.          row1 = GloWindow(handle).row1 - 1
  25315.          col1 = GloWindow(handle).col1 - 1
  25316.          row2 = GloWindow(handle).row2 + 1
  25317.          col2 = GloWindow(handle).col2 + 1
  25318.  
  25319.          IF row >= row1 AND row <= row2 AND col >= col1 AND col <= col2 THEN
  25320.              WhichWindow = handle
  25321.              Found = TRUE
  25322.          ELSE
  25323.              x = x - 1
  25324.          END IF
  25325.      WEND
  25326.  
  25327.  END FUNCTION
  25328.  
  25329.  FUNCTION WindowBorder$ (windowType)
  25330.  
  25331.      ' =======================================================================
  25332.      ' Returns a window border for the given window type.
  25333.      ' You may customize this to create custom windows.  See external
  25334.      ' documentation for a discussion of window borders
  25335.      ' =======================================================================
  25336.  
  25337.      SELECT CASE ABS(windowType)
  25338.          CASE 1
  25339.              WindowBorder$ = " ░          ST"
  25340.          CASE 2
  25341.              WindowBorder$ = "=           ST"
  25342.          CASE 3
  25343.              WindowBorder$ = "=░          ST"
  25344.          CASE 4
  25345.              WindowBorder$ = "        +   ST"
  25346.          CASE 5
  25347.              WindowBorder$ = " ░      +   ST"
  25348.          CASE 6
  25349.              WindowBorder$ = "=       +   ST"
  25350.          CASE 7
  25351.              WindowBorder$ = "=░      +   ST"
  25352.          CASE 8
  25353.              WindowBorder$ = "┌─┐│ │└─┘├─┤ST"
  25354.          CASE 9
  25355.              WindowBorder$ = "┌░┐│ │└─┘├─┤ST"
  25356.          CASE 10
  25357.              WindowBorder$ = "=─┐│ │└─┘├─┤ST"
  25358.          CASE 11
  25359.              WindowBorder$ = "=░┐│ │└─┘├─┤ST"
  25360.          CASE 12
  25361.              WindowBorder$ = "┌─┐│ │└─+├─┤ST"
  25362.          CASE 13
  25363.              WindowBorder$ = "┌░┐│ │└─+├─┤ST"
  25364.          CASE 14
  25365.              WindowBorder$ = "=─┐│ │└─+├─┤ST"
  25366.          CASE 15
  25367.              WindowBorder$ = "=░┐│ │└─+├─┤ST"
  25368.          CASE 16
  25369.              WindowBorder$ = "╔═╗║ ║╚═╝╠═╣ST"
  25370.          CASE 17
  25371.              WindowBorder$ = "╔░╗║ ║╚═╝╠═╣ST"
  25372.          CASE 18
  25373.              WindowBorder$ = "=═╗║ ║╚═╝╠═╣ST"
  25374.          CASE 19
  25375.              WindowBorder$ = "=░╗║ ║╚═╝╠═╣ST"
  25376.          CASE 20
  25377.              WindowBorder$ = "╔═╗║ ║╚═+╠═╣ST"
  25378.          CASE 21
  25379.              WindowBorder$ = "╔░╗║ ║╚═+╠═╣ST"
  25380.          CASE 22
  25381.              WindowBorder$ = "=═╗║ ║╚═+╠═╣ST"
  25382.          CASE 23
  25383.              WindowBorder$ = "=░╗║ ║╚═+╠═╣ST"
  25384.  
  25385.          ' ===================================================================
  25386.          ' Put any custom-designed border styles after this point and before
  25387.          ' the CASE ELSE statement.
  25388.          ' ===================================================================
  25389.  
  25390.          CASE ELSE
  25391.              WindowBorder$ = "            ST"
  25392.  
  25393.      END SELECT
  25394.  
  25395.  END FUNCTION
  25396.  
  25397.  SUB WindowBox (boxRow1, boxCol1, boxRow2, boxCol2)
  25398.  
  25399.      ' =======================================================================
  25400.      ' Draw a box, given coordinates based on the current window
  25401.      ' =======================================================================
  25402.  
  25403.      windo = WindowCurrent
  25404.      IF windo > 0 THEN
  25405.          row1 = GloWindow(windo).row1 + boxRow1 - 1
  25406.          row2 = GloWindow(windo).row1 + boxRow2 - 1
  25407.          col1 = GloWindow(windo).col1 + boxCol1 - 1
  25408.          col2 = GloWindow(windo).col1 + boxCol2 - 1
  25409.          fore = GloWindow(windo).fore
  25410.          back = GloWindow(windo).back
  25411.          border$ = "┌─┐│ │└─┘"
  25412.  
  25413.          Box row1, col1, row2, col2, fore, back, border$, 0
  25414.      END IF
  25415.  
  25416.  END SUB
  25417.  
  25418.  SUB WindowClose (handle)
  25419.  
  25420.      ' =======================================================================
  25421.      ' Close window # handle.  If handle is 0, recursively close all windows
  25422.      ' =======================================================================
  25423.  
  25424.      IF handle = 0 THEN
  25425.          IF GloStorage.numWindowsOpen > 0 THEN
  25426.              FOR x = GloStorage.numWindowsOpen TO 1 STEP -1
  25427.                  WindowClose GloWindowStack(x)
  25428.              NEXT x
  25429.          END IF
  25430.      ELSE
  25431.  
  25432.          ' ===================================================================
  25433.          ' If valid window,
  25434.          ' ===================================================================
  25435.  
  25436.          IF GloWindow(handle).handle <> -1 THEN
  25437.  
  25438.              ' ===============================================================
  25439.              ' Make the window you want to close the top window
  25440.              ' ===============================================================
  25441.  
  25442.              WindowSetCurrent handle
  25443.  
  25444.              ' ===============================================================
  25445.              ' If top window has shadow, hide shadow
  25446.              ' ===============================================================
  25447.  
  25448.              IF INSTR(WindowBorder$(GloWindow(GloStorage.currWindow).windowTyp
  25449.                  WindowShadowRefresh
  25450.              END IF
  25451.  
  25452.              ' ===============================================================
  25453.              ' Close all edit fields, and button on top window
  25454.              ' ===============================================================
  25455.  
  25456.              EditFieldClose 0
  25457.              ButtonClose 0
  25458.              MouseHide
  25459.  
  25460.              ' ===============================================================
  25461.              ' Restore the background of the window + clear data
  25462.              ' ===============================================================
  25463.  
  25464.              BackgroundRefresh handle
  25465.  
  25466.              GloBuffer$(handle, 1) = ""
  25467.              GloBuffer$(handle, 2) = ""
  25468.  
  25469.              GloWindow(handle).handle = -1
  25470.  
  25471.              ' ===============================================================
  25472.              ' Decrement total number of windows
  25473.              ' ===============================================================
  25474.  
  25475.              GloStorage.numWindowsOpen = GloStorage.numWindowsOpen - 1
  25476.  
  25477.              ' ===============================================================
  25478.              ' If some windows still open, assign curr Window to top window,
  25479.              ' show shadow is the currWindow has a shadow
  25480.              ' ===============================================================
  25481.  
  25482.              IF GloStorage.numWindowsOpen > 0 THEN
  25483.                  GloStorage.currWindow = GloWindowStack(GloStorage.numWindowsO
  25484.  
  25485.                  IF INSTR(WindowBorder$(GloWindow(GloStorage.currWindow).windo
  25486.                      WindowShadowSave
  25487.                  END IF
  25488.              ELSE
  25489.  
  25490.                  ' ===========================================================
  25491.                  ' If no more windows open, assign 0 to the currWindow variabl
  25492.                  ' ===========================================================
  25493.  
  25494.                  GloStorage.currWindow = 0
  25495.              END IF
  25496.              MouseShow
  25497.          END IF
  25498.      END IF
  25499.  
  25500.  END SUB
  25501.  
  25502.  SUB WindowCls
  25503.  
  25504.      ' =======================================================================
  25505.      ' If curr window is valid, clear the window
  25506.      ' =======================================================================
  25507.  
  25508.      windo = WindowCurrent
  25509.      IF windo > 0 THEN
  25510.          WindowScroll 0
  25511.      END IF
  25512.  
  25513.  END SUB
  25514.  
  25515.  SUB WindowColor (fore, back)
  25516.  
  25517.      ' =======================================================================
  25518.      ' If curr window is valid, assign the colors to the variables
  25519.      ' =======================================================================
  25520.  
  25521.      windo = WindowCurrent
  25522.      IF windo > 0 THEN
  25523.          GloWindow(windo).textFore = fore
  25524.          GloWindow(windo).textBack = back
  25525.      END IF
  25526.  
  25527.  END SUB
  25528.  
  25529.  FUNCTION WindowCols (handle)
  25530.  
  25531.      ' =======================================================================
  25532.      ' If window Handle is valid, return number of columns in that window
  25533.      ' =======================================================================
  25534.  
  25535.      IF GloWindow(handle).handle > 0 THEN
  25536.          WindowCols = GloWindow(handle).col2 - GloWindow(handle).col1 + 1
  25537.      END IF
  25538.  
  25539.  END FUNCTION
  25540.  
  25541.  FUNCTION WindowCurrent
  25542.  
  25543.      ' =======================================================================
  25544.      ' Simply return the current window, as stored in the global array
  25545.      ' =======================================================================
  25546.  
  25547.      WindowCurrent = GloStorage.currWindow
  25548.  
  25549.  END FUNCTION
  25550.  
  25551.  SUB WindowDo (startButton, startEdit)
  25552.  
  25553.    DIM RB$(1 TO 4)
  25554.  
  25555.      ' =======================================================================
  25556.      ' Main Processing loop.  Init, go to proper mode, exit
  25557.      ' =======================================================================
  25558.  
  25559.      GOSUB WindowDoInit
  25560.      GOSUB WindowDoComputeHotSpots
  25561.  
  25562.      IF WindowDoMode = 1 THEN
  25563.          GOSUB WindowDoEditMode
  25564.      ELSE
  25565.          GOSUB WindowDoButtonMode
  25566.      END IF
  25567.      LOCATE , , 0
  25568.      EXIT SUB
  25569.  
  25570.  ' ===========================================================================
  25571.  ' If startEdit is=0 then do button mode.  In button mode, we wait
  25572.  ' for any keyboard event or mouse event that flips the ExitFlag.
  25573.  ' Then we exit.  It's very simple really, don't try to make it complicated.
  25574.  ' ===========================================================================
  25575.  
  25576.  WindowDoButtonMode:
  25577.      GOSUB WindowDoShowTextCursor
  25578.      WHILE NOT ExitFlag
  25579.          GOSUB WindowDoMouse
  25580.          GOSUB WindowDoButtonKbd
  25581.      WEND
  25582.      GOSUB WindowDoHideTextCursor
  25583.  RETURN
  25584.  
  25585.  ' ===========================================================================
  25586.  ' If startEdit>0 then go to edit mode.  Here we also wait for a mouse event
  25587.  ' or kbd event to flip the ExitFlag, but in the mean time, we trap the
  25588.  ' alphanumberic keys, and arrow keys, and use them to edit the current
  25589.  ' edit field.  (StartEdit is the current edit field.)  Again, there's no magi
  25590.  ' (well maybe just a little...)
  25591.  ' ===========================================================================
  25592.  
  25593.  WindowDoEditMode:
  25594.      GOSUB WindowDoEditInit
  25595.      WHILE NOT ExitFlag
  25596.          GOSUB WindowDoMouse
  25597.          GOSUB WindowDoEditKbd
  25598.      WEND
  25599.      GOSUB WindowDoEditExit
  25600.  RETURN
  25601.  
  25602.  ' ===========================================================================
  25603.  ' Set initial flags, determine where cursor should be located, and figure
  25604.  ' out which mode we should be in (edit mode or button mode)
  25605.  ' ===========================================================================
  25606.  
  25607.  WindowDoInit:
  25608.  
  25609.      ' =======================================================================
  25610.      ' Simply abort if there is no window open.
  25611.      ' =======================================================================
  25612.  
  25613.      windo = WindowCurrent
  25614.      IF windo = 0 THEN EXIT SUB
  25615.  
  25616.      REDIM HSClick(MAXHOTSPOT) AS hotSpotType
  25617.      REDIM HSRel(MAXHOTSPOT) AS hotSpotType
  25618.  
  25619.      ExitFlag = FALSE
  25620.      ButtonHighLight = FALSE
  25621.  
  25622.      border$ = WindowBorder$(GloWindow(windo).windowType)
  25623.      WindowDoMode = 2
  25624.  
  25625.      ' =======================================================================
  25626.      ' If startEdit>0, assign the index value to currEditField, and set
  25627.      ' WindowDoMode to 1
  25628.      ' =======================================================================
  25629.  
  25630.      IF startEdit > 0 THEN
  25631.          index = FindEditField(startEdit)
  25632.          IF index > 0 THEN
  25633.              currEditField = index
  25634.              WindowDoMode = 1
  25635.              origCursorRow = GloWindow(windo).row1 + GloEdit(index).row - 1
  25636.              origCursorCol = GloWindow(windo).col1 + GloEdit(index).col - 1
  25637.          END IF
  25638.      END IF
  25639.  
  25640.      ' =======================================================================
  25641.      ' If start button>0, then set current cursor location properly
  25642.      ' =======================================================================
  25643.  
  25644.      IF startButton > 0 THEN
  25645.          index = FindButton(startButton)
  25646.          IF index > 0 THEN
  25647.              currButton = index
  25648.              origCursorRow = GloWindow(windo).row1 + GloButton(index).row1 - 1
  25649.              origCursorCol = GloWindow(windo).col1 + GloButton(index).col1
  25650.  
  25651.              ' ===============================================================
  25652.              ' For area buttons decrement the cursor position
  25653.              ' ===============================================================
  25654.  
  25655.              SELECT CASE GloButton(index).buttonType
  25656.                  CASE 4
  25657.                      origCursorCol = origCursorCol - 1
  25658.                  CASE ELSE
  25659.              END SELECT
  25660.  
  25661.          END IF
  25662.      END IF
  25663.  
  25664.      currCursorRow = origCursorRow
  25665.      currCursorCol = origCursorCol
  25666.  RETURN
  25667.  
  25668.  ' ===========================================================================
  25669.  ' Checks buttons, editfields, etc. and stores where the hot spots are
  25670.  ' ===========================================================================
  25671.  
  25672.  WindowDoComputeHotSpots:
  25673.      numHSClick = 0
  25674.      numHSRel = 0
  25675.  
  25676.      ' =======================================================================
  25677.      ' If upper left corder of border is "=", then that's a close box
  25678.      ' Furthermore, a close box is a release type event so store in HSRel
  25679.      ' =======================================================================
  25680.  
  25681.      IF MID$(border$, 1, 1) = "=" THEN
  25682.          numHSRel = numHSRel + 1
  25683.          HSRel(numHSRel).row1 = GloWindow(windo).row1 - 1
  25684.          HSRel(numHSRel).row2 = GloWindow(windo).row1 - 1
  25685.          HSRel(numHSRel).col1 = GloWindow(windo).col1 - 1
  25686.          HSRel(numHSRel).col2 = GloWindow(windo).col1 - 1
  25687.          HSRel(numHSRel).action = 4
  25688.          HSRel(numHSRel).misc = windo
  25689.      END IF
  25690.  
  25691.      ' =======================================================================
  25692.      ' If lower right corner is "+", then that's a re-size box
  25693.      ' Further more, a re-size box is a click event, so store in HSClick
  25694.      ' =======================================================================
  25695.  
  25696.      IF MID$(border$, 9, 1) = "+" THEN
  25697.          numHSClick = numHSClick + 1
  25698.          HSClick(numHSClick).row1 = GloWindow(windo).row2 + 1
  25699.          HSClick(numHSClick).row2 = GloWindow(windo).row2 + 1
  25700.          HSClick(numHSClick).col1 = GloWindow(windo).col2 + 1
  25701.          HSClick(numHSClick).col2 = GloWindow(windo).col2 + 1
  25702.          HSClick(numHSClick).action = 5
  25703.          HSClick(numHSClick).misc = 0
  25704.      END IF
  25705.  
  25706.      ' =======================================================================
  25707.      ' Likewise, a "░" chr$(176) is a move bar.  That's also a click event
  25708.      ' =======================================================================
  25709.  
  25710.      IF MID$(border$, 2, 1) = "░" THEN
  25711.          numHSClick = numHSClick + 1
  25712.          HSClick(numHSClick).row1 = GloWindow(windo).row1 - 1
  25713.          HSClick(numHSClick).row2 = GloWindow(windo).row1 - 1
  25714.          HSClick(numHSClick).col1 = GloWindow(windo).col1
  25715.          HSClick(numHSClick).col2 = GloWindow(windo).col2
  25716.          HSClick(numHSClick).action = 15
  25717.          HSClick(numHSClick).misc = 0
  25718.      END IF
  25719.  
  25720.      ' =======================================================================
  25721.      ' Buttons are click, and release events.
  25722.      ' Click, and the cursor goes there, and the button is highlighted.
  25723.      ' Release, and the selection is made
  25724.      ' =======================================================================
  25725.  
  25726.      IF GloStorage.numButtonsOpen > 0 THEN
  25727.          button = 0
  25728.          WHILE button < GloStorage.numButtonsOpen
  25729.              button = button + 1
  25730.              IF GloButton(button).windowHandle = windo THEN
  25731.                  numHSClick = numHSClick + 1
  25732.                  HSClick(numHSClick).row1 = GloWindow(windo).row1 + GloButton(
  25733.                  HSClick(numHSClick).row2 = GloWindow(windo).row1 + GloButton(
  25734.                  HSClick(numHSClick).col1 = GloWindow(windo).col1 + GloButton(
  25735.                  HSClick(numHSClick).col2 = GloWindow(windo).col1 + GloButton(
  25736.                  HSClick(numHSClick).action = 1
  25737.                  HSClick(numHSClick).misc = GloButton(button).handle
  25738.                  HSClick(numHSClick).misc2 = GloButton(button).buttonType
  25739.  
  25740.                  numHSRel = numHSRel + 1
  25741.                  HSRel(numHSRel).row1 = GloWindow(windo).row1 + GloButton(butt
  25742.                  HSRel(numHSRel).row2 = GloWindow(windo).row1 + GloButton(butt
  25743.                  HSRel(numHSRel).col1 = GloWindow(windo).col1 + GloButton(butt
  25744.                  HSRel(numHSRel).col2 = GloWindow(windo).col1 + GloButton(butt
  25745.                  HSRel(numHSRel).action = 1
  25746.                  HSRel(numHSRel).misc = GloButton(button).handle
  25747.                  HSRel(numHSRel).misc2 = GloButton(button).buttonType
  25748.  
  25749.                  ' ===========================================================
  25750.                  ' Adjust previous info to handle special cases for
  25751.                  ' "field" buttons, and "scroll bar" buttons.
  25752.                  ' ===========================================================
  25753.  
  25754.                  SELECT CASE GloButton(button).buttonType
  25755.                      CASE 4
  25756.                          numHSRel = numHSRel - 1
  25757.                          HSClick(numHSClick).row2 = GloWindow(windo).row1 + Gl
  25758.                          HSClick(numHSClick).col2 = GloWindow(windo).col1 + Gl
  25759.                      CASE 5
  25760.                          numHSClick = numHSClick - 1
  25761.                          HSRel(numHSRel).row2 = GloWindow(windo).row1 + GloBut
  25762.                          HSRel(numHSRel).col2 = GloWindow(windo).col1 + GloBut
  25763.                      CASE 6
  25764.                          numHSRel = numHSRel - 1
  25765.                          HSClick(numHSClick).row2 = GloWindow(windo).row1 + Gl
  25766.                          HSClick(numHSClick).col2 = GloWindow(windo).col1 + Gl
  25767.                      CASE 7
  25768.                          numHSRel = numHSRel - 1
  25769.                          HSClick(numHSClick).row2 = GloWindow(windo).row1 + Gl
  25770.                          HSClick(numHSClick).col2 = GloWindow(windo).col1 + Gl
  25771.                      CASE ELSE
  25772.                  END SELECT
  25773.              END IF
  25774.          WEND
  25775.      END IF
  25776.  
  25777.      ' =======================================================================
  25778.      ' EditFields are Click events
  25779.      ' =======================================================================
  25780.  
  25781.      IF GloStorage.numEditFieldsOpen > 0 THEN
  25782.          editField = 0
  25783.          WHILE editField < GloStorage.numEditFieldsOpen
  25784.              editField = editField + 1
  25785.              IF GloEdit(editField).windowHandle = windo THEN
  25786.                  numHSClick = numHSClick + 1
  25787.                  HSClick(numHSClick).row1 = GloWindow(windo).row1 + GloEdit(ed
  25788.                  HSClick(numHSClick).row2 = GloWindow(windo).row1 + GloEdit(ed
  25789.                  HSClick(numHSClick).col1 = GloWindow(windo).col1 + GloEdit(ed
  25790.                  HSClick(numHSClick).col2 = GloWindow(windo).col1 + GloEdit(ed
  25791.                  HSClick(numHSClick).action = 2
  25792.                  HSClick(numHSClick).misc = GloEdit(editField).handle
  25793.              END IF
  25794.          WEND
  25795.      END IF
  25796.  
  25797.      ' =======================================================================
  25798.      ' Feel free to add your own hot spots!  One good idea is if the
  25799.      ' right hand side of the border is ▓, make that a scroll bar!  Adding
  25800.      ' that would be good practice -- and fun too!
  25801.      ' =======================================================================
  25802.  
  25803.  RETURN
  25804.  
  25805.  ' ===========================================================================
  25806.  ' Polls the mouse
  25807.  ' ===========================================================================
  25808.  
  25809.  WindowDoMouse:
  25810.  
  25811.      MousePoll MouseRow, MouseCol, lButton, rButton
  25812.  
  25813.      ' =======================================================================
  25814.      ' If lButton is down, then keep checking for click events until it's rele
  25815.      ' =======================================================================
  25816.  
  25817.      IF lButton THEN
  25818.          WHILE lButton AND MouseRow <> 1 AND NOT ExitFlag
  25819.              GOSUB WindowDoCheckClickEvent
  25820.              IF Found THEN
  25821.                  GOSUB WindowDoClickEvent
  25822.              END IF
  25823.  
  25824.              MousePoll MouseRow, MouseCol, lButton, rButton
  25825.          WEND
  25826.  
  25827.          ' ===================================================================
  25828.          ' If the button was released (and no click event occured) then check
  25829.          ' for a release event!
  25830.          ' ===================================================================
  25831.  
  25832.          IF NOT lButton AND MouseRow <> 1 AND NOT ExitFlag THEN
  25833.              GOSUB WindowDoCheckReleaseEvent
  25834.              IF Found THEN
  25835.                  GOSUB WindowDoReleaseEvent
  25836.              ELSE
  25837.  
  25838.                  ' ===========================================================
  25839.                  ' If no release event, then see if mouse was released in anot
  25840.                  ' window.  This is a special case release event
  25841.                  ' ===========================================================
  25842.  
  25843.                  GOSUB WindowDoCheckOtherWindow
  25844.              END IF
  25845.  
  25846.              ' ===============================================================
  25847.              ' Un highlight the button if the mouse was released for any reaso
  25848.              ' ===============================================================
  25849.  
  25850.              GOSUB WindowDoUnHighlightButton
  25851.  
  25852.          END IF
  25853.  
  25854.  
  25855.      END IF
  25856.  
  25857.      ' =======================================================================
  25858.      ' If in button mode, return cursor to original spot.
  25859.      ' =======================================================================
  25860.  
  25861.      IF WindowDoMode = 2 THEN
  25862.          currCursorRow = origCursorRow
  25863.          currCursorCol = origCursorCol
  25864.          GOSUB WindowDoShowTextCursor
  25865.      END IF
  25866.  
  25867.  RETURN
  25868.  
  25869.  ' ===========================================================================
  25870.  ' Used only in Button mode.  Checks for menu event with MenuInkey$,
  25871.  ' then checks for all the misc events.  See below
  25872.  ' If an event is found, the proper event ID is stored, and ExifFlag is set
  25873.  ' ===========================================================================
  25874.  
  25875.  WindowDoButtonKbd:
  25876.  
  25877.      ' =======================================================================
  25878.      ' Only check menu if window type > 0.
  25879.      ' =======================================================================
  25880.  
  25881.      IF GloWindow(windo).windowType < 0 THEN
  25882.          kbd$ = INKEY$
  25883.      ELSE
  25884.          kbd$ = MenuInkey$
  25885.      END IF
  25886.  
  25887.      ' =======================================================================
  25888.      ' The following is a list of key strokes that can be detected. You can
  25889.      ' add more as needed, but you will need to change any programs that use
  25890.      ' the existing configuration.  The numbers associated with each key are
  25891.      ' the numbers that are returned by Dialog(0).
  25892.      ' =======================================================================
  25893.  
  25894.      SELECT CASE kbd$
  25895.          CASE CHR$(13)
  25896.              GloStorage.oldDialogEvent = 6          'Return
  25897.              ExitFlag = TRUE
  25898.          CASE CHR$(9)
  25899.              GloStorage.oldDialogEvent = 7          'Tab
  25900.              ExitFlag = TRUE
  25901.          CASE CHR$(0) + CHR$(15)
  25902.              GloStorage.oldDialogEvent = 8          'Back Tab
  25903.              ExitFlag = TRUE
  25904.          CASE CHR$(27)
  25905.              GloStorage.oldDialogEvent = 9          'Escape
  25906.              ExitFlag = TRUE
  25907.          CASE CHR$(0) + "H"
  25908.              GloStorage.oldDialogEvent = 10         'Up
  25909.              ExitFlag = TRUE
  25910.          CASE CHR$(0) + "P"
  25911.              GloStorage.oldDialogEvent = 11         'Down
  25912.              ExitFlag = TRUE
  25913.          CASE CHR$(0) + "K"
  25914.              GloStorage.oldDialogEvent = 12         'Left
  25915.              ExitFlag = TRUE
  25916.          CASE CHR$(0) + "M"
  25917.              GloStorage.oldDialogEvent = 13         'Right
  25918.              ExitFlag = TRUE
  25919.          CASE " "
  25920.              GloStorage.oldDialogEvent = 14         'Space
  25921.              ExitFlag = TRUE
  25922.          CASE CHR$(0) + "G"
  25923.              GloStorage.oldDialogEvent = 16         'Home
  25924.              ExitFlag = TRUE
  25925.          CASE CHR$(0) + "O"
  25926.              GloStorage.oldDialogEvent = 17         'End
  25927.              ExitFlag = TRUE
  25928.          CASE CHR$(0) + "I"
  25929.              GloStorage.oldDialogEvent = 18         'PgUp
  25930.              ExitFlag = TRUE
  25931.          CASE CHR$(0) + "Q"
  25932.              GloStorage.oldDialogEvent = 19         'PgDn
  25933.              ExitFlag = TRUE
  25934.          CASE "menu"
  25935.              GloStorage.oldDialogEvent = 20         'Menu
  25936.              ExitFlag = TRUE
  25937.          CASE ELSE
  25938.      END SELECT
  25939.  RETURN
  25940.  
  25941.  ' ===========================================================================
  25942.  ' Checks mouseRow, mouseCol against all the click events stored in HSClick
  25943.  ' ===========================================================================
  25944.  
  25945.  WindowDoCheckClickEvent:
  25946.      currEvent = 1
  25947.      Found = FALSE
  25948.  
  25949.      WHILE NOT Found AND currEvent <= numHSClick
  25950.          IF MouseRow >= HSClick(currEvent).row1 AND MouseRow <= HSClick(currEv
  25951.              Found = TRUE
  25952.          ELSE
  25953.              currEvent = currEvent + 1
  25954.          END IF
  25955.      WEND
  25956.  
  25957.      IF NOT Found THEN
  25958.          GOSUB WindowDoUnHighlightButton
  25959.      END IF
  25960.  
  25961.  RETURN
  25962.  
  25963.  ' ===========================================================================
  25964.  ' Checks mouseRow,mouseCol against all the release events in HSRel
  25965.  ' ===========================================================================
  25966.  
  25967.  WindowDoCheckReleaseEvent:
  25968.      currEvent = 1
  25969.      Found = FALSE
  25970.  
  25971.      WHILE NOT Found AND currEvent <= numHSRel
  25972.          IF MouseRow >= HSRel(currEvent).row1 AND MouseRow <= HSRel(currEvent)
  25973.              Found = TRUE
  25974.          ELSE
  25975.              currEvent = currEvent + 1
  25976.          END IF
  25977.      WEND
  25978.  RETURN
  25979.  
  25980.  ' ===========================================================================
  25981.  ' Calls WhichWindow to see if mouseRow, mouseCol is in another window
  25982.  ' If it is, that's event ID #3, so set it, and set ExitFlag to TRUE
  25983.  ' ===========================================================================
  25984.  
  25985.  WindowDoCheckOtherWindow:
  25986.      IF GloWindow(windo).windowType > 0 THEN
  25987.          otherWindow = WhichWindow(MouseRow, MouseCol)
  25988.          IF otherWindow AND (otherWindow <> windo) THEN
  25989.              GloStorage.oldDialogEvent = 3
  25990.              GloStorage.oldDialogWindow = otherWindow
  25991.              ExitFlag = TRUE
  25992.          END IF
  25993.      END IF
  25994.  RETURN
  25995.  
  25996.  ' ===========================================================================
  25997.  ' If there was a release event, this routine handles it
  25998.  ' ===========================================================================
  25999.  
  26000.  WindowDoReleaseEvent:
  26001.  
  26002.      SELECT CASE HSRel(currEvent).action
  26003.          CASE 1                                      'Released on Button
  26004.              GloStorage.oldDialogEvent = 1
  26005.              GloStorage.oldDialogButton = HSRel(currEvent).misc
  26006.              ExitFlag = TRUE
  26007.          CASE 4                                      'Released on Close Box
  26008.              GloStorage.oldDialogEvent = 4
  26009.              GloStorage.oldDialogClose = HSRel(currEvent).misc
  26010.              ExitFlag = TRUE
  26011.          CASE ELSE
  26012.      END SELECT
  26013.  RETURN
  26014.  
  26015.  ' ===========================================================================
  26016.  ' If there was a click event, this routine handles it
  26017.  ' ===========================================================================
  26018.  
  26019.  WindowDoClickEvent:
  26020.  
  26021.      SELECT CASE HSClick(currEvent).action
  26022.          CASE 1                                          'ButtonClick
  26023.              SELECT CASE HSClick(currEvent).misc2
  26024.                  CASE 1
  26025.                      IF ButtonHighLight THEN
  26026.                          IF currButton <> HSClick(currEvent).misc THEN
  26027.                              ButtonSetState currButton, origState
  26028.                              currButton = HSClick(currEvent).misc
  26029.                              ButtonSetState currButton, 3
  26030.                          END IF
  26031.                      ELSE
  26032.                          currButton = HSClick(currEvent).misc
  26033.                          origState = ButtonInquire(currButton)
  26034.                          ButtonHighLight = TRUE
  26035.                          ButtonSetState currButton, 3
  26036.                      END IF
  26037.  
  26038.                      currCursorRow = HSClick(currEvent).row1
  26039.                      currCursorCol = HSClick(currEvent).col1 + 1
  26040.                      GOSUB WindowDoShowTextCursor
  26041.                  CASE 2, 3
  26042.                      currCursorRow = HSClick(currEvent).row1
  26043.                      currCursorCol = HSClick(currEvent).col1 + 1
  26044.                      GOSUB WindowDoShowTextCursor
  26045.                  CASE 4
  26046.                      IF ButtonHighLight THEN
  26047.                          ButtonSetState currButton, origState
  26048.                      END IF
  26049.  
  26050.                      GloStorage.oldDialogEvent = 1
  26051.                      GloStorage.oldDialogButton = HSClick(currEvent).misc
  26052.                      GloStorage.oldDialogRow = MouseRow - HSClick(currEvent).r
  26053.                      GloStorage.oldDialogCol = MouseCol - HSClick(currEvent).c
  26054.                      ExitFlag = TRUE
  26055.                  CASE 6
  26056.                      GloStorage.oldDialogEvent = 1
  26057.                      GloStorage.oldDialogButton = HSClick(currEvent).misc
  26058.  
  26059.                      IF MouseRow = HSClick(currEvent).row1 THEN
  26060.                          GloStorage.oldDialogScroll = -1
  26061.                      ELSEIF MouseRow = HSClick(currEvent).row2 THEN
  26062.                          GloStorage.oldDialogScroll = -2
  26063.                      ELSE
  26064.                          GloStorage.oldDialogScroll = MouseRow - HSClick(currE
  26065.                      END IF
  26066.  
  26067.                      ExitFlag = TRUE
  26068.                  CASE 7
  26069.                      GloStorage.oldDialogEvent = 1
  26070.                      GloStorage.oldDialogButton = HSClick(currEvent).misc
  26071.  
  26072.                      IF MouseCol = HSClick(currEvent).col1 THEN
  26073.                          GloStorage.oldDialogScroll = -1
  26074.                      ELSEIF MouseCol = HSClick(currEvent).col2 THEN
  26075.                          GloStorage.oldDialogScroll = -2
  26076.                      ELSE
  26077.                          GloStorage.oldDialogScroll = MouseCol - HSClick(currE
  26078.                      END IF
  26079.  
  26080.                      ExitFlag = TRUE
  26081.                  CASE ELSE
  26082.              END SELECT
  26083.          CASE 2                                      'Edit Field Click
  26084.              GloStorage.oldDialogEvent = 2           'Event ID #2
  26085.              GloStorage.oldDialogEdit = HSClick(currEvent).misc
  26086.              ExitFlag = TRUE
  26087.          CASE 5
  26088.              GOSUB WindowDoSize                      'Internally handle Re-Siz
  26089.              ExitFlag = TRUE
  26090.              GloStorage.oldDialogEvent = 5
  26091.          CASE 15
  26092.              GOSUB WindowDoHideTextCursor
  26093.              GOSUB WindowDoMove                      'Internally handle Move
  26094.              ExitFlag = TRUE
  26095.              GloStorage.oldDialogEvent = 15
  26096.          CASE ELSE
  26097.  
  26098.      END SELECT
  26099.  
  26100.      IF HSClick(currEvent).action <> 1 THEN
  26101.          GOSUB WindowDoUnHighlightButton
  26102.      END IF
  26103.  
  26104.  RETURN
  26105.  
  26106.  ' ===========================================================================
  26107.  ' Un-highlight a button
  26108.  ' ===========================================================================
  26109.  
  26110.  WindowDoUnHighlightButton:
  26111.      IF ButtonHighLight THEN
  26112.          ButtonSetState currButton, origState
  26113.          ButtonHighLight = FALSE
  26114.          GOSUB WindowDoShowTextCursor
  26115.      END IF
  26116.  RETURN
  26117.  
  26118.  ' ===========================================================================
  26119.  ' Handle the move window click -- drag the window around until button release
  26120.  ' ===========================================================================
  26121.  
  26122.  WindowDoMove:
  26123.      MouseHide
  26124.      WindowSave windo
  26125.      BackgroundRefresh windo
  26126.      IF INSTR(WindowBorder$(GloWindow(windo).windowType), "S") THEN
  26127.        WindowShadowRefresh
  26128.      END IF
  26129.  
  26130.      oldWinrow1 = GloWindow(windo).row1
  26131.      oldWincol1 = GloWindow(windo).col1
  26132.      oldWinrow2 = GloWindow(windo).row2
  26133.      oldWincol2 = GloWindow(windo).col2
  26134.  
  26135.      GOSUB DrawRubberBand
  26136.  
  26137.      WindowPrintTitle
  26138.      MouseShow
  26139.  
  26140.      MouseBorder MINROW, (MouseCol - GloWindow(windo).col1 + 1 + MINCOL), (MAX
  26141.  
  26142.      oldMouseRow = MouseRow
  26143.      oldMouseCol = MouseCol
  26144.  
  26145.      DO
  26146.          MousePoll MouseRow, MouseCol, lButton, rButton
  26147.          IF MouseRow <> oldMouseRow OR MouseCol <> oldMouseCol THEN
  26148.              MouseHide
  26149.  
  26150.              GOSUB EraseRubberBand
  26151.  
  26152.              oldWinrow1 = oldWinrow1 - oldMouseRow + MouseRow
  26153.              oldWinrow2 = oldWinrow2 - oldMouseRow + MouseRow
  26154.              oldWincol1 = oldWincol1 - oldMouseCol + MouseCol
  26155.              oldWincol2 = oldWincol2 - oldMouseCol + MouseCol
  26156.  
  26157.              oldMouseRow = MouseRow
  26158.              oldMouseCol = MouseCol
  26159.  
  26160.              GOSUB DrawRubberBand
  26161.              MouseShow
  26162.          END IF
  26163.  
  26164.      LOOP UNTIL NOT lButton
  26165.  
  26166.      MouseHide
  26167.      GOSUB EraseRubberBand
  26168.      GloWindow(windo).row1 = oldWinrow1
  26169.      GloWindow(windo).row2 = oldWinrow2
  26170.      GloWindow(windo).col1 = oldWincol1
  26171.      GloWindow(windo).col2 = oldWincol2
  26172.      BackgroundSave windo
  26173.      WindowRefresh windo
  26174.      IF INSTR(WindowBorder$(GloWindow(windo).windowType), "S") THEN
  26175.          WindowShadowSave
  26176.      END IF
  26177.      GloBuffer$(windo, 2) = ""
  26178.      MouseShow
  26179.      MouseBorder 1, 1, MAXROW, MAXCOL
  26180.      GOSUB WindowDoComputeHotSpots
  26181.  RETURN
  26182.  
  26183.  ' ===========================================================================
  26184.  ' Re-Size window -- Drag box around until button released, then exit
  26185.  ' with eventID #5  -- Window need refreshing
  26186.  ' ===========================================================================
  26187.  
  26188.  WindowDoSize:
  26189.      ButtonClose 0
  26190.      MouseHide
  26191.      WindowSave windo
  26192.  
  26193.      ' ======================================================================
  26194.      ' Comment out the next line if you want to retain the window contents
  26195.      ' while resizing the window.
  26196.      ' ======================================================================
  26197.  
  26198.      BackgroundRefresh windo
  26199.  
  26200.      IF INSTR(WindowBorder$(GloWindow(windo).windowType), "S") THEN
  26201.        WindowShadowRefresh
  26202.      END IF
  26203.  
  26204.      oldWinrow1 = GloWindow(windo).row1
  26205.      oldWincol1 = GloWindow(windo).col1
  26206.      oldWinrow2 = GloWindow(windo).row2
  26207.      oldWincol2 = GloWindow(windo).col2
  26208.  
  26209.      GOSUB DrawRubberBand
  26210.  
  26211.      MouseShow
  26212.      MouseBorder GloWindow(windo).row1 + 3, GloWindow(windo).col1 + 6, MAXROW,
  26213.  
  26214.      oldMouseRow = MouseRow
  26215.      oldMouseCol = MouseCol
  26216.  
  26217.      DO
  26218.          MousePoll MouseRow, MouseCol, lButton, rButton
  26219.          IF MouseRow <> oldMouseRow OR MouseCol <> oldMouseCol THEN
  26220.              MouseHide
  26221.  
  26222.              GOSUB EraseRubberBand
  26223.  
  26224.              oldWinrow2 = oldWinrow2 - oldMouseRow + MouseRow
  26225.              oldWincol2 = oldWincol2 - oldMouseCol + MouseCol
  26226.  
  26227.              oldMouseRow = MouseRow
  26228.              oldMouseCol = MouseCol
  26229.  
  26230.              GOSUB DrawRubberBand
  26231.              MouseShow
  26232.          END IF
  26233.      LOOP UNTIL NOT lButton
  26234.  
  26235.      MouseHide
  26236.      GOSUB EraseRubberBand
  26237.      WindowShadowRefresh
  26238.      BackgroundRefresh windo
  26239.      GloWindow(windo).row2 = oldWinrow2
  26240.      GloWindow(windo).col2 = oldWincol2
  26241.      BackgroundSave windo
  26242.      Box GloWindow(windo).row1 - 1, GloWindow(windo).col1 - 1, GloWindow(windo
  26243.      GloBuffer$(windo, 2) = ""
  26244.      WindowPrintTitle
  26245.  
  26246.      IF INSTR(WindowBorder$(GloWindow(windo).windowType), "S") THEN
  26247.          WindowShadowSave
  26248.      END IF
  26249.      MouseShow
  26250.  
  26251.      MouseBorder 1, 1, MAXROW, MAXCOL
  26252.  RETURN
  26253.  
  26254.  ' ===========================================================================
  26255.  ' Draw rubber band of current window
  26256.  ' ===========================================================================
  26257.  
  26258.  DrawRubberBand:
  26259.    GetBackground oldWinrow1 - 1, oldWincol1 - 1, oldWinrow1 - 1, oldWincol2 +
  26260.    GetBackground oldWinrow2 + 1, oldWincol1 - 1, oldWinrow2 + 1, oldWincol2 +
  26261.    GetBackground oldWinrow1 - 1, oldWincol1 - 1, oldWinrow2 + 1, oldWincol1 -
  26262.    GetBackground oldWinrow1 - 1, oldWincol2 + 1, oldWinrow2 + 1, oldWincol2 +
  26263.    Box oldWinrow1 - 1, oldWincol1 - 1, oldWinrow2 + 1, oldWincol2 + 1, GloWind
  26264.  RETURN
  26265.  
  26266.  ' ===========================================================================
  26267.  ' Erase rubber band of current window
  26268.  ' ===========================================================================
  26269.  
  26270.  EraseRubberBand:
  26271.    PutBackground oldWinrow1 - 1, oldWincol1 - 1, RB$(1)
  26272.    PutBackground oldWinrow2 + 1, oldWincol1 - 1, RB$(2)
  26273.    PutBackground oldWinrow1 - 1, oldWincol1 - 1, RB$(3)
  26274.    PutBackground oldWinrow1 - 1, oldWincol2 + 1, RB$(4)
  26275.    RETURN
  26276.  
  26277.  WindowDoHideTextCursor:
  26278.      LOCATE , , 0
  26279.  RETURN
  26280.  
  26281.  
  26282.  WindowDoShowTextCursor:
  26283.      IF currCursorRow <> 0 AND currCursorCol <> 0 THEN
  26284.          LOCATE currCursorRow, currCursorCol, 1
  26285.      ELSE
  26286.          LOCATE , , 0
  26287.      END IF
  26288.  RETURN
  26289.  
  26290.  ' ===========================================================================
  26291.  ' If in edit mode, this routine gets info from the global arrays
  26292.  ' ===========================================================================
  26293.  
  26294.  WindowDoEditInit:
  26295.      row = GloWindow(windo).row1 + GloEdit(currEditField).row - 1
  26296.      col = GloWindow(windo).col1 + GloEdit(currEditField).col - 1
  26297.      fore = GloEdit(currEditField).fore
  26298.      back = GloEdit(currEditField).back
  26299.      visLength = GloEdit(currEditField).visLength
  26300.      maxLength = GloEdit(currEditField).maxLength
  26301.      editField$ = LEFT$(GloEdit(currEditField).text$, maxLength)
  26302.      insertMode = TRUE
  26303.      InsertKey = GetShiftState(7)
  26304.  
  26305.      ' =======================================================================
  26306.      ' Make sure everything's the right length
  26307.      ' =======================================================================
  26308.  
  26309.      x = INSTR(editField$, CHR$(0)) - 1
  26310.      IF x >= 0 THEN
  26311.          editField$ = LEFT$(editField$, x)
  26312.      END IF
  26313.  
  26314.      IF LEN(editField$) >= visLength THEN
  26315.          firstchar = LEN(editField$) - visLength + 2
  26316.          cursor = visLength - 1
  26317.      ELSE
  26318.          firstchar = 1
  26319.          cursor = LEN(editField$)
  26320.      END IF
  26321.  
  26322.      GOSUB WindowDoEditPrint
  26323.  
  26324.  RETURN
  26325.  
  26326.  ' ===========================================================================
  26327.  ' Handles the edit kbd event trapping.  Some keys trigger events
  26328.  ' (e.g. TAB is event ID #7)  Others affect the current edit field string (DEL
  26329.  ' ===========================================================================
  26330.  
  26331.  WindowDoEditKbd:
  26332.      IF GetShiftState(7) = InsertKey THEN
  26333.          insertMode = TRUE
  26334.          LOCATE , , , 6, 7
  26335.      ELSE
  26336.          insertMode = FALSE
  26337.          LOCATE , , , 0, 7
  26338.      END IF
  26339.  
  26340.      LOCATE row, col + cursor, 1
  26341.  
  26342.      GOSUB WindowDoMouse
  26343.  
  26344.      ' =======================================================================
  26345.      ' Only call MenuInkey$ if menuType > 0
  26346.      ' =======================================================================
  26347.  
  26348.      IF GloWindow(windo).windowType < 0 THEN
  26349.          kbd$ = INKEY$
  26350.      ELSE
  26351.          kbd$ = MenuInkey$
  26352.      END IF
  26353.  
  26354.      ' =======================================================================
  26355.      ' Either key is an event, and the exitFlag is set, or something happens
  26356.      ' to the current edit string.
  26357.      ' =======================================================================
  26358.  
  26359.      SELECT CASE kbd$
  26360.          CASE CHR$(13)
  26361.              GloStorage.oldDialogEvent = 6          'Return
  26362.              ExitFlag = TRUE
  26363.          CASE CHR$(9)
  26364.              GloStorage.oldDialogEvent = 7          'Tab
  26365.              ExitFlag = TRUE
  26366.          CASE CHR$(0) + CHR$(15)
  26367.              GloStorage.oldDialogEvent = 8          'Back Tab
  26368.              ExitFlag = TRUE
  26369.          CASE CHR$(27)
  26370.              GloStorage.oldDialogEvent = 9          'Escape
  26371.              ExitFlag = TRUE
  26372.          CASE CHR$(0) + "H"
  26373.              GloStorage.oldDialogEvent = 10         'Up
  26374.              ExitFlag = TRUE
  26375.          CASE CHR$(0) + "P"
  26376.              GloStorage.oldDialogEvent = 11         'Down
  26377.              ExitFlag = TRUE
  26378.          CASE CHR$(0) + "M"                         'Right
  26379.              GOSUB WindowDoEditRight
  26380.          CASE CHR$(0) + "K"
  26381.              cursor = cursor - 1
  26382.              IF cursor < 0 THEN
  26383.                  cursor = cursor + 1
  26384.                  IF firstchar > 1 THEN
  26385.                      firstchar = firstchar - 1
  26386.                      GOSUB WindowDoEditPrint
  26387.                  END IF
  26388.              END IF
  26389.  
  26390.          CASE CHR$(0) + "S"
  26391.              IF cursor + firstchar <= LEN(editField$) THEN
  26392.                  editField$ = LEFT$(editField$, cursor + firstchar - 1) + RIGH
  26393.                  GOSUB WindowDoEditPrint
  26394.              END IF
  26395.          CASE CHR$(8)
  26396.              IF firstchar + cursor > 1 THEN
  26397.                  editField$ = LEFT$(editField$, cursor + firstchar - 2) + RIGH
  26398.                  GOSUB WindowDoEditPrint
  26399.                  SELECT CASE cursor
  26400.                      CASE 0
  26401.                          firstchar = firstchar - 1
  26402.                          GOSUB WindowDoEditPrint
  26403.                      CASE 1
  26404.                          IF firstchar > 1 THEN
  26405.                              firstchar = firstchar - 1
  26406.                              GOSUB WindowDoEditPrint
  26407.                          ELSE
  26408.                              cursor = cursor - 1
  26409.                          END IF
  26410.                      CASE ELSE
  26411.                          cursor = cursor - 1
  26412.                  END SELECT
  26413.              END IF
  26414.          CASE CHR$(0) + "G"                         'Home
  26415.              firstchar = 1
  26416.              cursor = 0
  26417.              GOSUB WindowDoEditPrint
  26418.          CASE CHR$(0) + "O"                         'End
  26419.              IF LEN(editField$) >= visLength THEN
  26420.                  cursor = visLength - 1
  26421.                  firstchar = LEN(editField$) - visLength + 2
  26422.                  GOSUB WindowDoEditPrint
  26423.              ELSE
  26424.                  firstchar = 1
  26425.                  cursor = LEN(editField$)
  26426.              END IF
  26427.          CASE CHR$(0) + "u"                         'Ctrl+end
  26428.              editField$ = LEFT$(editField$, firstchar + cursor - 1)
  26429.              GOSUB WindowDoEditPrint
  26430.          CASE "menu"
  26431.              GloStorage.oldDialogEvent = 20         'Menu
  26432.              ExitFlag = TRUE
  26433.  
  26434.          CASE CHR$(32) TO CHR$(255)                 'Alphanumeric
  26435.              IF insertMode THEN
  26436.                  IF LEN(editField$) < maxLength THEN
  26437.                      editField$ = LEFT$(editField$, cursor + firstchar - 1) +
  26438.                      GOSUB WindowDoEditPrint
  26439.                      GOSUB WindowDoEditRight
  26440.                  ELSE
  26441.                      BEEP
  26442.                  END IF
  26443.              ELSE
  26444.                  IF cursor + firstchar > LEN(editField$) THEN
  26445.                      IF LEN(editField$) < maxLength THEN
  26446.                          editField$ = editField$ + kbd$
  26447.                          MouseHide
  26448.                          PRINT kbd$;
  26449.                          MouseShow
  26450.                      END IF
  26451.                  ELSE
  26452.                      MID$(editField$, cursor + firstchar, 1) = kbd$
  26453.                      MouseHide
  26454.                      PRINT kbd$;
  26455.                      MouseShow
  26456.                  END IF
  26457.  
  26458.                  GOSUB WindowDoEditRight
  26459.              END IF
  26460.  
  26461.      END SELECT
  26462.  RETURN
  26463.  
  26464.  ' ===========================================================================
  26465.  ' Moves the cursor right 1 space.  This is used twice, so it is its own
  26466.  ' routine
  26467.  ' ===========================================================================
  26468.  
  26469.  WindowDoEditRight:
  26470.      cursor = cursor + 1
  26471.      IF cursor + firstchar - 1 > LEN(editField$) THEN
  26472.          cursor = cursor - 1
  26473.      ELSEIF cursor + firstchar - 1 > maxLength THEN
  26474.          cursor = cursor - 1
  26475.      ELSEIF cursor = visLength THEN
  26476.          firstchar = firstchar + 1
  26477.          cursor = cursor - 1
  26478.          GOSUB WindowDoEditPrint
  26479.      END IF
  26480.  RETURN
  26481.  
  26482.  ' ===========================================================================
  26483.  ' Upon exit, store the current edit field string back into the global array
  26484.  ' ===========================================================================
  26485.  
  26486.  WindowDoEditExit:
  26487.      GloEdit(currEditField).text$ = editField$ + CHR$(0)
  26488.      LOCATE , , 0, 6, 7
  26489.  RETURN
  26490.  
  26491.  ' ===========================================================================
  26492.  ' Prints the edit field in the proper color, at the proper location
  26493.  ' ===========================================================================
  26494.  
  26495.  WindowDoEditPrint:
  26496.      MouseHide
  26497.      COLOR fore, back
  26498.      LOCATE row, col
  26499.      PRINT MID$(editField$ + SPACE$(visLength), firstchar, visLength);
  26500.      MouseShow
  26501.  RETURN
  26502.  
  26503.  END SUB
  26504.  
  26505.  SUB WindowInit
  26506.  
  26507.      ' =======================================================================
  26508.      ' Initialize totals
  26509.      ' =======================================================================
  26510.  
  26511.      GloStorage.currWindow = -1
  26512.      GloStorage.numWindowsOpen = 0
  26513.      GloStorage.numButtonsOpen = 0
  26514.      GloStorage.numEditFieldsOpen = 0
  26515.  
  26516.      ' =======================================================================
  26517.      ' Clear all windows
  26518.      ' =======================================================================
  26519.  
  26520.      FOR A = 1 TO MAXWINDOW
  26521.          GloWindow(A).handle = -1
  26522.          GloWindow(A).row1 = 0
  26523.          GloWindow(A).col1 = 0
  26524.          GloWindow(A).row2 = 0
  26525.          GloWindow(A).col2 = 0
  26526.          GloWindow(A).fore = 0
  26527.          GloWindow(A).back = 0
  26528.          GloWindow(A).windowType = 0
  26529.          GloWindow(A).title = ""
  26530.          GloWindowStack(A) = -1
  26531.      NEXT A
  26532.  
  26533.      ' =======================================================================
  26534.      ' Clear all buttons
  26535.      ' =======================================================================
  26536.  
  26537.      FOR A = 1 TO MAXBUTTON
  26538.          GloButton(A).handle = -1
  26539.          GloButton(A).windowHandle = -1
  26540.          GloButton(A).text = ""
  26541.          GloButton(A).state = 0
  26542.          GloButton(A).buttonOn = FALSE
  26543.          GloButton(A).row1 = 0
  26544.          GloButton(A).col1 = 0
  26545.          GloButton(A).row2 = 0
  26546.          GloButton(A).col2 = 0
  26547.          GloButton(A).buttonType = 0
  26548.      NEXT A
  26549.  
  26550.      ' =======================================================================
  26551.      ' Clear all edit fields
  26552.      ' =======================================================================
  26553.  
  26554.      FOR A = 1 TO MAXEDITFIELD
  26555.          GloEdit(A).handle = 0
  26556.          GloEdit(A).windowHandle = 0
  26557.          GloEdit(A).text = ""
  26558.          GloEdit(A).row = 0
  26559.          GloEdit(A).col = 0
  26560.          GloEdit(A).visLength = 0
  26561.          GloEdit(A).maxLength = 0
  26562.          GloEdit(A).fore = 0
  26563.          GloEdit(A).back = 0
  26564.      NEXT A
  26565.  
  26566.  END SUB
  26567.  
  26568.  SUB WindowLine (row)
  26569.  
  26570.      ' =======================================================================
  26571.      ' If window is valid, draw a horizontal line at the row which is passed
  26572.      ' =======================================================================
  26573.  
  26574.      windo = WindowCurrent
  26575.  
  26576.      IF windo > 0 THEN
  26577.          IF row >= 1 OR row <= WindowRows(windo) THEN
  26578.  
  26579.              topRow = GloWindow(windo).row1
  26580.              leftCol = GloWindow(windo).col1 - 1
  26581.              rightCol = GloWindow(windo).col2 + 1
  26582.              border$ = WindowBorder$(GloWindow(windo).windowType)
  26583.  
  26584.              LOCATE topRow + row - 1, leftCol
  26585.              MouseHide
  26586.              COLOR GloWindow(windo).fore, GloWindow(windo).back
  26587.  
  26588.              IF MID$(border$, 11, 1) = " " THEN
  26589.                  PRINT STRING$(rightCol - leftCol + 1, CHR$(196))
  26590.              ELSE
  26591.                  PRINT MID$(border$, 10, 1); STRING$(rightCol - leftCol - 1, M
  26592.              END IF
  26593.  
  26594.              MouseShow
  26595.          END IF
  26596.      END IF
  26597.  
  26598.  END SUB
  26599.  
  26600.  SUB WindowLocate (row, col)
  26601.  
  26602.      ' =======================================================================
  26603.      ' If window is valid, assign the passed row and col to the global variabl
  26604.      ' =======================================================================
  26605.  
  26606.      windo = WindowCurrent
  26607.      IF windo > 0 THEN
  26608.          GloWindow(windo).cursorRow = row
  26609.          GloWindow(windo).cursorCol = col
  26610.      END IF
  26611.  
  26612.  END SUB
  26613.  
  26614.  FUNCTION WindowNext
  26615.  
  26616.      ' =======================================================================
  26617.      ' Loop through window array, and find first unused window, return handle
  26618.      ' If no window found, return 0
  26619.      ' =======================================================================
  26620.  
  26621.      Found = FALSE
  26622.      A = 1
  26623.      WHILE A <= MAXWINDOW AND NOT Found
  26624.          IF GloWindow(A).handle = -1 THEN
  26625.              Found = TRUE
  26626.          ELSE
  26627.              A = A + 1
  26628.          END IF
  26629.      WEND
  26630.  
  26631.      IF Found THEN
  26632.          WindowNext = A
  26633.      ELSE
  26634.          WindowNext = 0
  26635.      END IF
  26636.  
  26637.  END FUNCTION
  26638.  
  26639.  SUB WindowOpen (handle, row1, col1, row2, col2, textFore, textBack, fore, bac
  26640.  
  26641.      ' =======================================================================
  26642.      ' Open Window!   First make sure coordinates are valid
  26643.      ' =======================================================================
  26644.      IF row1 > row2 THEN SWAP row1, row2
  26645.      IF col1 > col2 THEN SWAP col1, col2
  26646.  
  26647.      IF col1 >= MINCOL + 1 AND row1 >= MINROW + 1 AND col2 <= MAXCOL - 1 AND r
  26648.  
  26649.          ' ===================================================================
  26650.          ' Close window by save number if it already exists
  26651.          ' ===================================================================
  26652.  
  26653.          WindowClose handle
  26654.  
  26655.          ' ===================================================================
  26656.          ' Evaluate argument list to determine windowType
  26657.          ' ===================================================================
  26658.  
  26659.          IF movewin THEN windowType = 1
  26660.          IF closewin THEN windowType = windowType + 2
  26661.          IF sizewin THEN windowType = windowType + 4
  26662.          IF borderchar = 1 THEN windowType = windowType + 8
  26663.          IF borderchar = 2 THEN windowType = windowType + 16
  26664.          IF windowType = 0 THEN windowType = 99
  26665.          IF modalwin THEN windowType = -windowType
  26666.  
  26667.          border$ = WindowBorder(windowType)
  26668.  
  26669.          ' ===================================================================
  26670.          ' hide current window's shadow if it has one
  26671.          ' ===================================================================
  26672.  
  26673.          MouseHide
  26674.          IF GloStorage.numWindowsOpen > 0 THEN
  26675.              IF INSTR(WindowBorder$(GloWindow(GloWindowStack(GloStorage.numWin
  26676.                  WindowShadowRefresh
  26677.              END IF
  26678.          END IF
  26679.  
  26680.          ' ===================================================================
  26681.          ' Assign new values to window array
  26682.          ' ===================================================================
  26683.  
  26684.          GloWindow(handle).handle = handle
  26685.          GloWindow(handle).row1 = row1
  26686.          GloWindow(handle).col1 = col1
  26687.          GloWindow(handle).row2 = row2
  26688.          GloWindow(handle).col2 = col2
  26689.          GloWindow(handle).cursorRow = 1
  26690.          GloWindow(handle).cursorCol = 1
  26691.          GloWindow(handle).fore = fore
  26692.          GloWindow(handle).back = back
  26693.          GloWindow(handle).textFore = textFore
  26694.          GloWindow(handle).textBack = textBack
  26695.          GloWindow(handle).highlight = highlight
  26696.          GloWindow(handle).windowType = windowType
  26697.          GloWindow(handle).title = title$
  26698.  
  26699.          ' ===================================================================
  26700.          ' Save background, then draw window
  26701.          ' ===================================================================
  26702.  
  26703.          BackgroundSave handle
  26704.          Box row1 - 1, col1 - 1, row2 + 1, col2 + 1, fore, back, border$, 1
  26705.          MouseShow
  26706.  
  26707.          ' ===================================================================
  26708.          ' Assign handle to currWindow, incr total windows, push handle on sta
  26709.          ' ===================================================================
  26710.  
  26711.          GloStorage.currWindow = handle
  26712.          GloStorage.numWindowsOpen = GloStorage.numWindowsOpen + 1
  26713.          GloWindowStack(GloStorage.numWindowsOpen) = handle
  26714.  
  26715.          ' ===================================================================
  26716.          ' Print window title, and shadow
  26717.          ' ===================================================================
  26718.  
  26719.          WindowPrintTitle
  26720.          IF INSTR(border$, "S") THEN
  26721.              WindowShadowSave
  26722.          END IF
  26723.      END IF
  26724.  
  26725.  END SUB
  26726.  
  26727.  SUB WindowPrint (printMode, text$)
  26728.  
  26729.      ' =======================================================================
  26730.      ' If window is valid, print text$ using mode printMode%.  See
  26731.      ' External documentation for details on printMode%
  26732.      ' =======================================================================
  26733.  
  26734.      windo = WindowCurrent
  26735.  
  26736.      IF windo > 0 THEN
  26737.          SELECT CASE printMode
  26738.  
  26739.              ' ===============================================================
  26740.              ' Truncate printing
  26741.              ' ===============================================================
  26742.  
  26743.              CASE 1, -1
  26744.                  length = WindowCols(windo) - GloWindow(windo).cursorCol + 1
  26745.                  LOCATE GloWindow(windo).row1 + GloWindow(windo).cursorRow - 1
  26746.                  COLOR GloWindow(windo).textFore, GloWindow(windo).textBack
  26747.                  MouseHide
  26748.                  PRINT LEFT$(text$, length);
  26749.                  MouseShow
  26750.                  IF printMode < 0 THEN
  26751.                      GloWindow(windo).cursorCol = GloWindow(windo).cursorCol +
  26752.                      IF GloWindow(windo).cursorCol > WindowCols(windo) THEN
  26753.                          GloWindow(windo).cursorCol = WindowCols(windo) + 1
  26754.                      END IF
  26755.                  ELSE
  26756.                      GloWindow(windo).cursorRow = GloWindow(windo).cursorRow +
  26757.                      GloWindow(windo).cursorCol = 1
  26758.                      IF GloWindow(windo).cursorRow > WindowRows(windo) THEN
  26759.                          WindowScroll 1
  26760.                          GloWindow(windo).cursorRow = WindowRows(windo)
  26761.                      END IF
  26762.                  END IF
  26763.              ' ===============================================================
  26764.              ' Character wrapping
  26765.              ' ===============================================================
  26766.  
  26767.              CASE 2, -2
  26768.                  COLOR GloWindow(windo).textFore, GloWindow(windo).textBack
  26769.                  A$ = text$
  26770.                  WHILE LEN(A$) > 0
  26771.                      length = WindowCols(windo) - GloWindow(windo).cursorCol +
  26772.                      LOCATE GloWindow(windo).row1 + GloWindow(windo).cursorRow
  26773.  
  26774.                      MouseHide
  26775.                      PRINT LEFT$(A$, length);
  26776.                      MouseShow
  26777.  
  26778.                      IF length < LEN(A$) THEN
  26779.                          A$ = RIGHT$(A$, LEN(A$) - length)
  26780.                          GloWindow(windo).cursorRow = GloWindow(windo).cursorR
  26781.                          GloWindow(windo).cursorCol = 1
  26782.                          IF GloWindow(windo).cursorRow > WindowRows(windo) THE
  26783.                              WindowScroll 1
  26784.                              GloWindow(windo).cursorRow = WindowRows(windo)
  26785.                          END IF
  26786.                      ELSE
  26787.                          IF printMode < 0 THEN
  26788.                              GloWindow(windo).cursorCol = GloWindow(windo).cur
  26789.                              IF GloWindow(windo).cursorCol > WindowCols(windo)
  26790.                                  GloWindow(windo).cursorCol = WindowCols(windo
  26791.                              END IF
  26792.                          ELSE
  26793.                              GloWindow(windo).cursorRow = GloWindow(windo).cur
  26794.                              GloWindow(windo).cursorCol = GloWindow(windo).cur
  26795.                              IF GloWindow(windo).cursorRow > WindowRows(windo)
  26796.                                  WindowScroll 1
  26797.                                  GloWindow(windo).cursorRow = WindowRows(windo
  26798.                              END IF
  26799.                          END IF
  26800.                          A$ = ""
  26801.                      END IF
  26802.                  WEND
  26803.  
  26804.              ' ===============================================================
  26805.              ' Word wrapping
  26806.              ' ===============================================================
  26807.  
  26808.              CASE 3, -3
  26809.                  COLOR GloWindow(windo).textFore, GloWindow(windo).textBack
  26810.                  A$ = text$
  26811.                  WHILE LEN(A$) > 0
  26812.                      length = WindowCols(windo) - GloWindow(windo).cursorCol +
  26813.                      LOCATE GloWindow(windo).row1 + GloWindow(windo).cursorRow
  26814.  
  26815.                      IF length < LEN(A$) THEN
  26816.                          x = length + 1
  26817.                          b$ = " " + A$
  26818.                          WHILE MID$(b$, x, 1) <> " "
  26819.                              x = x - 1
  26820.                          WEND
  26821.                          x = x - 1
  26822.  
  26823.                          MouseHide
  26824.                          IF x = 0 THEN
  26825.                              PRINT LEFT$(A$, length);
  26826.                              A$ = RIGHT$(A$, LEN(A$) - length)
  26827.                          ELSE
  26828.                              PRINT LEFT$(A$, x);
  26829.                              A$ = RIGHT$(A$, LEN(A$) - x)
  26830.                          END IF
  26831.                          MouseShow
  26832.  
  26833.                          x = 1
  26834.                          b$ = A$ + " "
  26835.                          WHILE MID$(b$, x, 1) = " "
  26836.                              x = x + 1
  26837.                          WEND
  26838.  
  26839.                          IF x = LEN(b$) THEN
  26840.                              A$ = ""
  26841.                          ELSEIF x > 1 THEN
  26842.                              A$ = RIGHT$(A$, LEN(A$) - x + 1)
  26843.                          END IF
  26844.  
  26845.                          GloWindow(windo).cursorRow = GloWindow(windo).cursorR
  26846.                          GloWindow(windo).cursorCol = 1
  26847.                          IF GloWindow(windo).cursorRow > WindowRows(windo) THE
  26848.                              WindowScroll 1
  26849.                              GloWindow(windo).cursorRow = WindowRows(windo)
  26850.                          END IF
  26851.                      ELSE
  26852.  
  26853.                      MouseHide
  26854.                      PRINT LEFT$(A$, length);
  26855.                      MouseShow
  26856.                          IF printMode < 0 THEN
  26857.                              GloWindow(windo).cursorCol = GloWindow(windo).cur
  26858.                              IF GloWindow(windo).cursorCol > WindowCols(windo)
  26859.                                  GloWindow(windo).cursorCol = WindowCols(windo
  26860.                              END IF
  26861.                          ELSE
  26862.                              GloWindow(windo).cursorRow = GloWindow(windo).cur
  26863.                              GloWindow(windo).cursorCol = GloWindow(windo).cur
  26864.                              IF GloWindow(windo).cursorRow > WindowRows(windo)
  26865.                                  WindowScroll 1
  26866.                                  GloWindow(windo).cursorRow = WindowRows(windo
  26867.                              END IF
  26868.                          END IF
  26869.                          A$ = ""
  26870.                      END IF
  26871.                  WEND
  26872.  
  26873.              ' ===============================================================
  26874.              ' Centered text printing
  26875.              ' ===============================================================
  26876.  
  26877.              CASE 4
  26878.                  COLOR GloWindow(windo).textFore, GloWindow(windo).textBack
  26879.                  IF LEN(text$) >= WindowCols(windo) THEN
  26880.                      LOCATE GloWindow(windo).row1 + GloWindow(windo).cursorRow
  26881.                      MouseHide
  26882.                      PRINT LEFT$(text$, length);
  26883.                      MouseShow
  26884.                  ELSE
  26885.                      LOCATE GloWindow(windo).row1 + GloWindow(windo).cursorRow
  26886.                      MouseHide
  26887.                      PRINT text$
  26888.                      MouseShow
  26889.                  END IF
  26890.  
  26891.                  GloWindow(windo).cursorRow = GloWindow(windo).cursorRow + 1
  26892.                  GloWindow(windo).cursorCol = 1
  26893.                  IF GloWindow(windo).cursorRow > WindowRows(windo) THEN
  26894.                      WindowScroll 1
  26895.                      GloWindow(windo).cursorRow = WindowRows(windo)
  26896.                  END IF
  26897.          END SELECT
  26898.      END IF
  26899.  
  26900.  END SUB
  26901.  
  26902.  SUB WindowPrintTitle
  26903.  
  26904.      ' =======================================================================
  26905.      ' Print title of current window if the border$ says it's valid
  26906.      ' =======================================================================
  26907.  
  26908.      windo = WindowCurrent
  26909.      IF windo > 0 THEN
  26910.  
  26911.          title$ = GloWindow(windo).title
  26912.          border$ = WindowBorder$(GloWindow(windo).windowType)
  26913.  
  26914.  
  26915.          IF INSTR(border$, "T") THEN
  26916.              tx$ = RTRIM$(title$)
  26917.              IF LEN(tx$) > 0 THEN
  26918.                  COLOR GloWindow(windo).highlight, GloWindow(windo).back
  26919.                  MouseHide
  26920.                  length = WindowCols(windo)
  26921.                  IF (LEN(tx$) + 2) < length THEN
  26922.                      LOCATE GloWindow(windo).row1 - 1, GloWindow(windo).col1 +
  26923.                      PRINT " "; tx$; " ";
  26924.                  ELSE
  26925.                      LOCATE GloWindow(windo).row1 - 1, GloWindow(windo).col1
  26926.                      PRINT LEFT$(" " + tx$ + " ", (GloWindow(windo).col2 - Glo
  26927.                  END IF
  26928.                  MouseShow
  26929.              END IF
  26930.          END IF
  26931.      END IF
  26932.  
  26933.  END SUB
  26934.  
  26935.  SUB WindowRefresh (handle)
  26936.  
  26937.      ' =======================================================================
  26938.      ' Refresh the window -- used for window move, window resize, and
  26939.      ' WindowSetCurrent
  26940.      ' =======================================================================
  26941.  
  26942.      IF GloWindow(handle).handle > 0 THEN
  26943.          MouseHide
  26944.          PutBackground GloWindow(handle).row1 - 1, GloWindow(handle).col1 - 1,
  26945.          MouseShow
  26946.      END IF
  26947.  
  26948.  END SUB
  26949.  
  26950.  FUNCTION WindowRows (handle)
  26951.  
  26952.      ' =======================================================================
  26953.      ' Returns number of rows if handle is a valid window
  26954.      ' =======================================================================
  26955.  
  26956.      IF GloWindow(handle).handle > 0 THEN
  26957.          WindowRows = GloWindow(handle).row2 - GloWindow(handle).row1 + 1
  26958.      END IF
  26959.  
  26960.  END FUNCTION
  26961.  
  26962.  SUB WindowSave (handle)
  26963.  
  26964.      ' =======================================================================
  26965.      ' Saves the window handle%
  26966.      ' =======================================================================
  26967.  
  26968.      IF GloWindow(handle).handle > 0 THEN
  26969.          MouseHide
  26970.          GetBackground GloWindow(handle).row1 - 1, GloWindow(handle).col1 - 1,
  26971.          MouseShow
  26972.      END IF
  26973.  
  26974.  END SUB
  26975.  
  26976.  SUB WindowScroll (lines)
  26977.  
  26978.      ' =======================================================================
  26979.      ' Scroll just the window area.
  26980.      ' =======================================================================
  26981.  
  26982.      windo = WindowCurrent
  26983.      IF windo > 0 THEN
  26984.          MouseHide
  26985.          CALL Scroll(GloWindow(windo).row1, GloWindow(windo).col1, GloWindow(w
  26986.          MouseShow
  26987.      END IF
  26988.  
  26989.  END SUB
  26990.  
  26991.  SUB WindowSetCurrent (handle)
  26992.  
  26993.      ' =======================================================================
  26994.      ' If window is valid, and not already the current window
  26995.      ' =======================================================================
  26996.  
  26997.      IF GloWindow(handle).handle <> -1 AND handle <> WindowCurrent THEN
  26998.  
  26999.          ' ===================================================================
  27000.          ' If current window has a shadow, hide the shadow
  27001.          ' ===================================================================
  27002.  
  27003.          MouseHide
  27004.          IF INSTR(WindowBorder$(GloWindow(GloStorage.currWindow).windowType),
  27005.              WindowShadowRefresh
  27006.          END IF
  27007.  
  27008.          ' ===================================================================
  27009.          ' Save all windows on top of the one to be current, and refresh the
  27010.          ' background of each
  27011.          ' ===================================================================
  27012.  
  27013.          x = GloStorage.numWindowsOpen
  27014.          WHILE GloWindowStack(x) <> handle
  27015.              WindowSave GloWindowStack(x)
  27016.              BackgroundRefresh GloWindowStack(x)
  27017.              x = x - 1
  27018.          WEND
  27019.  
  27020.          ' ===================================================================
  27021.          ' Save the window to be made the current window
  27022.          ' ===================================================================
  27023.  
  27024.          WindowSave handle
  27025.          BackgroundRefresh handle
  27026.  
  27027.          ' ===================================================================
  27028.          ' Replace each window that was on top of handle, and squeeze stack
  27029.          ' ===================================================================
  27030.  
  27031.          IF handle <> GloWindowStack(GloStorage.numWindowsOpen) THEN
  27032.              FOR A = x + 1 TO GloStorage.numWindowsOpen
  27033.                  BackgroundSave GloWindowStack(A)
  27034.                  WindowRefresh GloWindowStack(A)
  27035.                  GloBuffer$(GloWindowStack(A), 2) = ""
  27036.                  GloWindowStack(A - 1) = GloWindowStack(A)
  27037.              NEXT A
  27038.          END IF
  27039.  
  27040.          ' ===================================================================
  27041.          ' Save new background of new current window.
  27042.          ' ===================================================================
  27043.  
  27044.          BackgroundSave handle
  27045.          WindowRefresh handle
  27046.          GloBuffer$(handle, 2) = ""
  27047.          MouseShow
  27048.  
  27049.          GloStorage.currWindow = handle
  27050.          GloWindowStack(GloStorage.numWindowsOpen) = handle
  27051.  
  27052.          ' ===================================================================
  27053.          ' Show shadow if current window has one
  27054.          ' ===================================================================
  27055.  
  27056.          IF INSTR(WindowBorder$(GloWindow(handle).windowType), "S") THEN
  27057.              WindowShadowSave
  27058.          END IF
  27059.  
  27060.      END IF
  27061.  
  27062.  END SUB
  27063.  
  27064.  SUB WindowShadowRefresh
  27065.  
  27066.      ' =======================================================================
  27067.      ' If window is current, replace what was under the shadow
  27068.      ' =======================================================================
  27069.  
  27070.      windo = WindowCurrent
  27071.      IF windo > 0 THEN
  27072.          row1 = GloWindow(windo).row1
  27073.          row2 = GloWindow(windo).row2
  27074.          col1 = GloWindow(windo).col1
  27075.          col2 = GloWindow(windo).col2
  27076.  
  27077.          ' ===================================================================
  27078.          ' If shadow partially (or fully) off screen, adjust coordinates
  27079.          ' ===================================================================
  27080.  
  27081.          MouseHide
  27082.          IF col1 <= MAXCOL - 2 THEN
  27083.              PutBackground row1, col2 + 2, GloBuffer$(MAXWINDOW + 1, 1)
  27084.          END IF
  27085.          IF row2 <= MAXROW - 2 THEN
  27086.              PutBackground row2 + 2, col1 + 1, GloBuffer$(MAXWINDOW + 1, 2)
  27087.          END IF
  27088.          MouseShow
  27089.      END IF
  27090.  
  27091.  END SUB
  27092.  
  27093.  SUB WindowShadowSave
  27094.  
  27095.      ' =======================================================================
  27096.      ' If current window valid, draw the shadow, storing what is underneath
  27097.      ' it first.
  27098.      ' =======================================================================
  27099.  
  27100.      windo = WindowCurrent
  27101.  
  27102.      IF windo > 0 THEN
  27103.          row1 = GloWindow(windo).row1
  27104.          row2 = GloWindow(windo).row2
  27105.          col1 = GloWindow(windo).col1
  27106.          col2 = GloWindow(windo).col2
  27107.  
  27108.          ' ===================================================================
  27109.          ' If shadow is partially, or fully off screen, adjust coordinates
  27110.          ' ===================================================================
  27111.  
  27112.          IF col2 > MAXCOL - 2 THEN
  27113.              shadowWidth = -1
  27114.          ELSEIF col2 = MAXCOL - 2 THEN
  27115.              shadowWidth = 0
  27116.          ELSE
  27117.              shadowWidth = 1
  27118.          END IF
  27119.  
  27120.          MouseHide
  27121.  
  27122.          ' ===================================================================
  27123.          ' Save background, the draw shadow
  27124.          ' ===================================================================
  27125.  
  27126.          IF col2 <= MAXCOL - 2 THEN
  27127.              GetBackground row1, col2 + 2, row2 + 1, col2 + 2 + shadowWidth, G
  27128.              AttrBox row1, col2 + 2, row2 + 1, col2 + 2 + shadowWidth, 8
  27129.          END IF
  27130.  
  27131.          IF row2 <= MAXROW - 2 THEN
  27132.              GetBackground row2 + 2, col1 + 1, row2 + 2, col2 + 2 + shadowWidt
  27133.              AttrBox row2 + 2, col1 + 1, row2 + 2, col2 + 2 + shadowWidth, 8
  27134.          END IF
  27135.          MouseShow
  27136.      END IF
  27137.  
  27138.  END SUB
  27139.  
  27140.  Microsoft Quick-BASIC Sample Code
  27141.  
  27142.  
  27143.  ABSOLUTE.ASM
  27144.  CD-ROM Disc Path:   \SAMPCODE\QB\ABSOLUTE.ASM
  27145.  
  27146.          TITLE   ABSOLUTE - helper for assembly routines
  27147.  ;***
  27148.  ; ABSOLUTE - Helper for calling BASIC interpreter assembly routines
  27149.  ;
  27150.  ;       Copyright <C> 1986, Microsoft Corporation
  27151.  ;
  27152.  ;Purpose:
  27153.  ; Allow a BASIC program to call a routine which is located at an
  27154.  ; absolute memory address in the DEF SEG.
  27155.  ;
  27156.  ; The form of the call is:
  27157.  ;
  27158.  ;       CALL ABSOLUTE(<param>,...,<loc>)
  27159.  ;
  27160.  ; where
  27161.  ;       <param>,...   -  zero or more parameters for the assembly routine
  27162.  ;       <loc>         -  an Integer variable that contains the
  27163.  ;                        location in the DEF SEG of the start of
  27164.  ;                        the assembly routine
  27165.  ;
  27166.  ; The location parameter will be removed, and the routine at DEF SEG:<loc>
  27167.  ; will be called with the remaining parameters.
  27168.  ;
  27169.  ; Notes:
  27170.  ;       - The parameters are not checked or verified before being passed
  27171.  ;         to the assembly routine.
  27172.  ;       - CALL must be used.  CALLS will cause execution to jump to a
  27173.  ;         random location.
  27174.  ;       - The DOSSEG, .MODEL, .CODE, and .DATA? directives are part of
  27175.  ;         the simplified segment system of MASM 5.0. If you have an
  27176.  ;         earlier version of MASM, you must modify the source to define
  27177.  ;         the segments required by Microsoft high-level languages. These
  27178.  ;         segments are discussed in Appendix C of "Learning and Using
  27179.  ;         QuickBASIC."
  27180.  ;
  27181.  ;****************************************************************************
  27182.  ;
  27183.          DOSSEG                ;requires MASM 5.0 or higher
  27184.          .MODEL  medium
  27185.  
  27186.  ;       Define the routine as public.
  27187.  
  27188.          PUBLIC  ABSOLUTE
  27189.  
  27190.  ;       Define the seg segment
  27191.  
  27192.          .DATA?
  27193.  
  27194.          EXTRN  b$seg:WORD     ;seg segment
  27195.  
  27196.  ;***
  27197.  ; ABSOLUTE - Call absolute address
  27198.  ;
  27199.  ;Purpose:
  27200.  ; Routine which can be directly called from the basic level which in turn
  27201.  ; calls an absolute address.
  27202.  ;
  27203.  ;Entry:
  27204.  ; The actual number of parameters is variable, and depends on the routine
  27205.  ; that ABSOLUTE will in turn call. The LAST parameter pushed MUST be the DS
  27206.  ; offset of an integer variable containing the offset of the routine to be
  27207.  ; called. The current DEF SEG is used as the segment for the call.
  27208.  ;
  27209.  ;Exit:
  27210.  ; Whatever the called routine elects. We do NOT return here.
  27211.  ;
  27212.  ;Uses:
  27213.  ; This routine follows convention, but does no saving or checking of the code
  27214.  ; actually called.
  27215.  ;
  27216.  ;Notes:
  27217.  ; The called routine receives control with all parameters passed to ABSOLUTE,
  27218.  ; except the offset integer, on the stack in Pascal convention. The return
  27219.  ; address present is back to the BASIC level code which CALLed ABSOLUTE.
  27220.  ;
  27221.  ; Stack on call to ABSOLUTE:
  27222.  ;
  27223.  ;
  27224.  ;               \       Variable number of parameters           \
  27225.  ;               |          to routine to be CALLed              |
  27226.  ;               +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
  27227.  ;               |       Near pointer to I2 var containing       |
  27228.  ;               |       the offset of the routine to CALL       |
  27229.  ;               +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
  27230.  ;               |CS                                             |
  27231.  ;               +    Far return address to caller of ABSOLUTE   +
  27232.  ;       [SP] -> |IP                                             |
  27233.  ;               +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
  27234.  ;
  27235.  ; Stack on transfer to called routine:
  27236.  ;
  27237.  ;               \       Variable number of parameters           \
  27238.  ;               |          to routine to be CALLed              |
  27239.  ;               +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
  27240.  ;               |CS                                             |
  27241.  ;               +    Far return address to caller of ABSOLUTE   +
  27242.  ;       [SP] -> |IP                                             |
  27243.  ;               +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
  27244.  ;
  27245.  ;****************************************************************************
  27246.  
  27247.          .CODE
  27248.  
  27249.  ABSOLUTE PROC FAR
  27250.  
  27251.          POP     AX              ;return offset
  27252.          POP     DX              ;return segment
  27253.          POP     BX              ;get pointer to routine address
  27254.          PUSH    DX              ;restore return address
  27255.          PUSH    AX
  27256.          PUSH    [b$seg]         ;stack DEF SEG segment
  27257.          PUSH    [BX]            ;stack routine offset
  27258.  
  27259.          RET                     ;jump to ABSOLUTE routine
  27260.  
  27261.  ABSOLUTE ENDP
  27262.  
  27263.          END
  27264.  
  27265.  
  27266.  ATTRIB.BAS
  27267.  CD-ROM Disc Path:   \SAMPCODE\QB\TOOLBOX\DISK1\ATTRIB.BAS
  27268.  
  27269.    ' ************************************************
  27270.    ' **  Name:          ATTRIB                     **
  27271.    ' **  Type:          Program                    **
  27272.    ' **  Module:        ATTRIB.BAS                 **
  27273.    ' **  Language:      Microsoft QuickBASIC 4.00  **
  27274.    ' ************************************************
  27275.    '
  27276.    ' Displays all combinations of text mode character
  27277.    ' attributes on the screen for review.
  27278.    '
  27279.    ' USAGE:           No command line parameters
  27280.    ' REQUIREMENTS:    CGA
  27281.    ' .MAK FILE:       (none)
  27282.    ' FUNCTIONS:       (none)
  27283.    ' PARAMETERS:      (none)
  27284.    ' VARIABLES:       (none)
  27285.  
  27286.      DECLARE SUB Attrib ()
  27287.  
  27288.    ' Call the subprogram
  27289.      Attrib
  27290.  
  27291.    ' All done
  27292.      END
  27293.  
  27294.    ' ************************************************
  27295.    ' **  Name:          Attrib                     **
  27296.    ' **  Type:          Subprogram                 **
  27297.    ' **  Module:        ATTRIB.BAS                 **
  27298.    ' **  Language:      Microsoft QuickBASIC 4.00  **
  27299.    ' ************************************************
  27300.    '
  27301.    ' Displays table of color attributes for text mode.
  27302.    '
  27303.    ' EXAMPLE OF USE:  Attrib
  27304.    ' PARAMETERS:      (none)
  27305.    ' VARIABLES:       bgd%        Background number for COLOR statement
  27306.    '                  fgd%        Foreground number for COLOR statement
  27307.    ' MODULE LEVEL
  27308.    '   DECLARATIONS:              DECLARE SUB Attrib ()
  27309.    '
  27310.      SUB Attrib STATIC
  27311.          SCREEN 0
  27312.          CLS
  27313.          PRINT "Attributes for the COLOR statement in text mode (SCREEN 0)."
  27314.          PRINT "Add 16 to the foreground to cause the character to blink."
  27315.          FOR bgd% = 0 TO 7
  27316.              COLOR bgd% XOR 7, bgd%
  27317.              PRINT
  27318.              PRINT "Background%"; STR$(bgd%),
  27319.              PRINT "Foreground% ..."; SPACE$(41)
  27320.              FOR fgd% = 0 TO 15
  27321.                  COLOR fgd%, bgd%
  27322.                  PRINT STR$(fgd%); "  ";
  27323.              NEXT fgd%
  27324.          NEXT bgd%
  27325.          COLOR 7, 0
  27326.          PRINT
  27327.      END SUB
  27328.  
  27329.  
  27330.  
  27331.  BALLPSET.BAS
  27332.  CD-ROM Disc Path:   \SAMPCODE\QB\SRCDISK\BALLPSET.BAS
  27333.  
  27334.  DECLARE FUNCTION GetArraySize (WLeft, WRight, WTop, WBottom)
  27335.  
  27336.  SCREEN 2
  27337.  CLS
  27338.  
  27339.  ' Define a viewport and draw a border around it:
  27340.  VIEW (20, 10)-(620, 190), , 1
  27341.  
  27342.  CONST PI = 3.141592653589#
  27343.  
  27344.  ' Redefine the coordinates of the viewport with logical
  27345.  ' coordinates:
  27346.  WINDOW (-3.15, -.14)-(3.56, 1.01)
  27347.  
  27348.  ' Arrays in program are now dynamic:
  27349.  ' $DYNAMIC
  27350.  
  27351.  ' Calculate the logical coordinates for the top and bottom of a
  27352.  ' rectangle large enough to hold the image that will be drawn
  27353.  ' with CIRCLE and PAINT:
  27354.  WLeft = -.21
  27355.  WRight = .21
  27356.  WTop = .07
  27357.  WBottom = -.07
  27358.  
  27359.  ' Call the GetArraySize function, passing it the rectangle's
  27360.  ' logical coordinates:
  27361.  ArraySize% = GetArraySize(WLeft, WRight, WTop, WBottom)
  27362.  
  27363.  DIM Array(1 TO ArraySize%) AS INTEGER
  27364.  
  27365.  ' Draw and paint the circle:
  27366.  CIRCLE (0, 0), .18
  27367.  PAINT (0, 0)
  27368.  
  27369.  ' Store the rectangle in Array:
  27370.  GET (WLeft, WTop)-(WRight, WBottom), Array
  27371.  CLS
  27372.  
  27373.  ' Draw a box and fill it with a pattern:
  27374.  LINE (-3, .8)-(3.4, .2), , B
  27375.  Pattern$ = CHR$(126) + CHR$(0) + CHR$(126) + CHR$(126)
  27376.  PAINT (0, .5), Pattern$
  27377.  
  27378.  LOCATE 21, 29
  27379.  PRINT "Press any key to end"
  27380.  
  27381.  ' Initialize loop variables:
  27382.  StepSize = .02
  27383.  StartLoop = -PI
  27384.  Decay = 1
  27385.  
  27386.  DO
  27387.     EndLoop = -StartLoop
  27388.     FOR X = StartLoop TO EndLoop STEP StepSize
  27389.  
  27390.        ' Each time the ball "bounces" (hits the bottom of the
  27391.        ' viewport), the Decay variable gets smaller, making the
  27392.        ' height of the next bounce smaller:
  27393.        Y = ABS(COS(X)) * Decay - .14
  27394.        IF Y < -.13 THEN Decay = Decay * .9
  27395.  
  27396.        ' Stop if a key pressed or if Decay is less than .01:
  27397.        Esc$ = INKEY$
  27398.        IF Esc$ <> "" OR Decay < .01 THEN EXIT FOR
  27399.  
  27400.        ' Put the image on the screen.  The StepSize offset is
  27401.        ' smaller than the border around the circle, so each time
  27402.        ' the image moves, it erases any traces left from the
  27403.        ' previous PUT (it also erases anything else on the
  27404.        ' screen):
  27405.        PUT (X, Y), Array, PSET
  27406.     NEXT X
  27407.  
  27408.     ' Reverse direction:
  27409.     StepSize = -StepSize
  27410.     StartLoop = -StartLoop
  27411.  LOOP UNTIL Esc$ <> "" OR Decay < .01
  27412.  
  27413.  Pause$ = INPUT$(1)
  27414.  END
  27415.  REM $STATIC
  27416.  REM $DYNAMIC
  27417.  FUNCTION GetArraySize (WLeft, WRight, WTop, WBottom) STATIC
  27418.  
  27419.     ' Map the logical coordinates passed to this function to
  27420.     ' their physical-coordinate equivalents:
  27421.     VLeft = PMAP(WLeft, 0)
  27422.     VRight = PMAP(WRight, 0)
  27423.     VTop = PMAP(WTop, 1)
  27424.     VBottom = PMAP(WBottom, 1)
  27425.  
  27426.     ' Calculate the height and width in pixels of the
  27427.     ' enclosing rectangle:
  27428.     RectHeight = ABS(VBottom - VTop) + 1
  27429.     RectWidth = ABS(VRight - VLeft) + 1
  27430.  
  27431.     ' Calculate size in bytes of array:
  27432.     ByteSize = 4 + RectHeight * INT((RectWidth + 7) / 8)
  27433.  
  27434.     ' Array is integer, so divide bytes by two:
  27435.     GetArraySize = ByteSize \ 2 + 1
  27436.  END FUNCTION
  27437.  
  27438.  
  27439.  BALLXOR.BAS
  27440.  CD-ROM Disc Path:   \SAMPCODE\QB\SRCDISK\BALLXOR.BAS
  27441.  
  27442.  DECLARE FUNCTION GetArraySize (WLeft, WRight, WTop, WBottom)
  27443.  
  27444.  SCREEN 2
  27445.  CLS
  27446.  VIEW (20, 10)-(620, 190), , 1
  27447.  
  27448.  CONST PI = 3.141592653589#
  27449.  
  27450.  WINDOW (-3.15, -.14)-(3.56, 1.01)
  27451.  
  27452.  ' $DYNAMIC
  27453.  ' The rectangle is smaller than the one in the previous
  27454.  ' program, which means Array is also smaller:
  27455.  WLeft = -.18
  27456.  WRight = .18
  27457.  WTop = .05
  27458.  WBottom = -.05
  27459.  
  27460.  ArraySize% = GetArraySize(WLeft, WRight, WTop, WBottom)
  27461.  
  27462.  DIM Array(1 TO ArraySize%) AS INTEGER
  27463.  
  27464.  CIRCLE (0, 0), .18
  27465.  PAINT (0, 0)
  27466.  
  27467.  GET (WLeft, WTop)-(WRight, WBottom), Array
  27468.  CLS
  27469.  
  27470.  LINE (-3, .8)-(3.4, .2), , B
  27471.  Pattern$ = CHR$(126) + CHR$(0) + CHR$(126) + CHR$(126)
  27472.  PAINT (0, .5), Pattern$
  27473.  
  27474.  LOCATE 21, 29
  27475.  PRINT "Press any key to end"
  27476.  
  27477.  StepSize = .02
  27478.  StartLoop = -PI
  27479.  Decay = 1
  27480.  
  27481.  DO
  27482.     EndLoop = -StartLoop
  27483.     FOR X = StartLoop TO EndLoop STEP StepSize
  27484.        Y = ABS(COS(X)) * Decay - .14
  27485.  
  27486.        ' The first PUT statement places the image on
  27487.        ' the screen:
  27488.        PUT (X, Y), Array, XOR
  27489.  
  27490.        ' An empty FOR...NEXT loop to delay the program and
  27491.        ' reduce image flicker:
  27492.        FOR I = 1 TO 5: NEXT I
  27493.  
  27494.        IF Y < -.13 THEN Decay = Decay * .9
  27495.        Esc$ = INKEY$
  27496.        IF Esc$ <> "" OR Decay < .01 THEN EXIT FOR
  27497.  
  27498.        ' The second PUT statement erases the image and
  27499.        ' restores the background:
  27500.        PUT (X, Y), Array, XOR
  27501.     NEXT X
  27502.  
  27503.     StepSize = -StepSize
  27504.     StartLoop = -StartLoop
  27505.  LOOP UNTIL Esc$ <> "" OR Decay < .01
  27506.  
  27507.  Pause$ = INPUT$(1)
  27508.  END
  27509.  REM $STATIC
  27510.  REM $DYNAMIC
  27511.  FUNCTION GetArraySize (WLeft, WRight, WTop, WBottom) STATIC
  27512.     VLeft = PMAP(WLeft, 0)
  27513.     VRight = PMAP(WRight, 0)
  27514.     VTop = PMAP(WTop, 1)
  27515.     VBottom = PMAP(WBottom, 1)
  27516.  
  27517.     RectHeight = ABS(VBottom - VTop) + 1
  27518.     RectWidth = ABS(VRight - VLeft) + 1
  27519.  
  27520.     ByteSize = 4 + RectHeight * INT((RectWidth + 7) / 8)
  27521.     GetArraySize = ByteSize \ 2 + 1
  27522.  END FUNCTION
  27523.  
  27524.  
  27525.  BAR.BAS
  27526.  CD-ROM Disc Path:   \SAMPCODE\QB\SRCDISK\BAR.BAS
  27527.  
  27528.  ' Define type for the titles:
  27529.  TYPE TitleType
  27530.     MainTitle AS STRING * 40
  27531.     XTitle AS STRING * 40
  27532.     YTitle AS STRING * 18
  27533.  END TYPE
  27534.  
  27535.  DECLARE SUB InputTitles (T AS TitleType)
  27536.  DECLARE FUNCTION DrawGraph$ (T AS TitleType, Label$(), Value!(), N%)
  27537.  DECLARE FUNCTION InputData% (Label$(), Value!())
  27538.  
  27539.  ' Variable declarations for titles and bar data:
  27540.  DIM Titles AS TitleType, Label$(1 TO 5), Value(1 TO 5)
  27541.  
  27542.  CONST FALSE = 0, TRUE = NOT FALSE
  27543.  
  27544.  DO
  27545.     InputTitles Titles
  27546.     N% = InputData%(Label$(), Value())
  27547.     IF N% <> FALSE THEN
  27548.        NewGraph$ = DrawGraph$(Titles, Label$(), Value(), N%)
  27549.     END IF
  27550.  LOOP WHILE NewGraph$ = "Y"
  27551.  
  27552.  END
  27553.  REM $STATIC
  27554.  '
  27555.  ' ========================== DRAWGRAPH =========================
  27556.  '   Draws a bar graph from the data entered in the INPUTTITLES
  27557.  '   and INPUTDATA procedures.
  27558.  ' ==============================================================
  27559.  '
  27560.  FUNCTION DrawGraph$ (T AS TitleType, Label$(), Value(), N%) STATIC
  27561.  
  27562.     ' Set size of graph:
  27563.     CONST GRAPHTOP = 24, GRAPHBOTTOM = 171
  27564.     CONST GRAPHLEFT = 48, GRAPHRIGHT = 624
  27565.     CONST YLENGTH = GRAPHBOTTOM - GRAPHTOP
  27566.  
  27567.     ' Calculate max/min values:
  27568.     YMax = 0
  27569.     YMin = 0
  27570.     FOR I% = 1 TO N%
  27571.        IF Value(I%) < YMin THEN YMin = Value(I%)
  27572.        IF Value(I%) > YMax THEN YMax = Value(I%)
  27573.     NEXT I%
  27574.  
  27575.     ' Calculate width of bars and space between them:
  27576.     BarWidth = (GRAPHRIGHT - GRAPHLEFT) / N%
  27577.     BarSpace = .2 * BarWidth
  27578.     BarWidth = BarWidth - BarSpace
  27579.  
  27580.     SCREEN 2
  27581.     CLS
  27582.  
  27583.     ' Draw y axis:
  27584.     LINE (GRAPHLEFT, GRAPHTOP)-(GRAPHLEFT, GRAPHBOTTOM), 1
  27585.  
  27586.     ' Draw main graph title:
  27587.     Start% = 44 - (LEN(RTRIM$(T.MainTitle)) / 2)
  27588.     LOCATE 2, Start%
  27589.     PRINT RTRIM$(T.MainTitle);
  27590.  
  27591.     ' Annotate Y axis:
  27592.     Start% = CINT(13 - LEN(RTRIM$(T.YTitle)) / 2)
  27593.     FOR I% = 1 TO LEN(RTRIM$(T.YTitle))
  27594.        LOCATE Start% + I% - 1, 1
  27595.        PRINT MID$(T.YTitle, I%, 1);
  27596.     NEXT I%
  27597.  
  27598.     ' Calculate scale factor so labels aren't bigger than 4 digits:
  27599.     IF ABS(YMax) > ABS(YMin) THEN
  27600.        Power = YMax
  27601.     ELSE
  27602.        Power = YMin
  27603.     END IF
  27604.     Power = CINT(LOG(ABS(Power) / 100) / LOG(10))
  27605.     IF Power < 0 THEN Power = 0
  27606.  
  27607.     ' Scale min and max down:
  27608.     ScaleFactor = 10 ^ Power
  27609.     YMax = CINT(YMax / ScaleFactor)
  27610.     YMin = CINT(YMin / ScaleFactor)
  27611.  
  27612.     ' If power isn't zero then put scale factor on chart:
  27613.     IF Power <> 0 THEN
  27614.        LOCATE 3, 2
  27615.        PRINT "x 10^"; LTRIM$(STR$(Power))
  27616.     END IF
  27617.  
  27618.     ' Put tic mark and number for Max point on Y axis:
  27619.     LINE (GRAPHLEFT - 3, GRAPHTOP)-STEP(3, 0)
  27620.     LOCATE 4, 2
  27621.     PRINT USING "####"; YMax
  27622.  
  27623.     ' Put tic mark and number for Min point on Y axis:
  27624.     LINE (GRAPHLEFT - 3, GRAPHBOTTOM)-STEP(3, 0)
  27625.     LOCATE 22, 2
  27626.     PRINT USING "####"; YMin
  27627.  
  27628.     ' Scale min and max back up for charting calculations:
  27629.     YMax = YMax * ScaleFactor
  27630.     YMin = YMin * ScaleFactor
  27631.  
  27632.     ' Annotate X axis:
  27633.     Start% = 44 - (LEN(RTRIM$(T.XTitle)) / 2)
  27634.     LOCATE 25, Start%
  27635.     PRINT RTRIM$(T.XTitle);
  27636.  
  27637.     ' Calculate the pixel range for the Y axis:
  27638.     YRange = YMax - YMin
  27639.  
  27640.     ' Define a diagonally striped pattern:
  27641.     Tile$ = CHR$(1) + CHR$(2) + CHR$(4) + CHR$(8) + CHR$(16) + CHR$(32) + CHR$
  27642.  
  27643.     ' Draw a zero line if appropriate:
  27644.     IF YMin < 0 THEN
  27645.        Bottom = GRAPHBOTTOM - ((-YMin) / YRange * YLENGTH)
  27646.        LOCATE INT((Bottom - 1) / 8) + 1, 5
  27647.        PRINT "0";
  27648.     ELSE
  27649.        Bottom = GRAPHBOTTOM
  27650.     END IF
  27651.  
  27652.     ' Draw x axis:
  27653.     LINE (GRAPHLEFT - 3, Bottom)-(GRAPHRIGHT, Bottom)
  27654.  
  27655.     ' Draw bars and labels:
  27656.     Start% = GRAPHLEFT + (BarSpace / 2)
  27657.     FOR I% = 1 TO N%
  27658.  
  27659.        ' Draw a bar label:
  27660.        BarMid = Start% + (BarWidth / 2)
  27661.        CharMid = INT((BarMid - 1) / 8) + 1
  27662.        LOCATE 23, CharMid - INT(LEN(RTRIM$(Label$(I%))) / 2)
  27663.        PRINT Label$(I%);
  27664.  
  27665.        ' Draw the bar and fill it with the striped pattern:
  27666.        BarHeight = (Value(I%) / YRange) * YLENGTH
  27667.        LINE (Start%, Bottom)-STEP(BarWidth, -BarHeight), , B
  27668.        PAINT (BarMid, Bottom - (BarHeight / 2)), Tile$, 1
  27669.  
  27670.        Start% = Start% + BarWidth + BarSpace
  27671.     NEXT I%
  27672.  
  27673.     LOCATE 1, 1, 1
  27674.     PRINT "New graph? ";
  27675.     DrawGraph$ = UCASE$(INPUT$(1))
  27676.  
  27677.  END FUNCTION
  27678.  '
  27679.  ' ========================= INPUTDATA ========================
  27680.  '         Gets input for the bar labels and their values
  27681.  ' ============================================================
  27682.  '
  27683.  FUNCTION InputData% (Label$(), Value()) STATIC
  27684.  
  27685.     ' Initialize the number of data values:
  27686.     NumData% = 0
  27687.  
  27688.     ' Print data-entry instructions:
  27689.     CLS
  27690.     PRINT "Enter data for up to 5 bars:"
  27691.     PRINT "   * Enter the label and value for each bar."
  27692.     PRINT "   * Values can be negative."
  27693.     PRINT "   * Enter a blank label to stop."
  27694.     PRINT
  27695.     PRINT "After viewing the graph, press any key ";
  27696.     PRINT "to end the program."
  27697.  
  27698.     ' Accept data until blank label or 5 entries:
  27699.     Done% = FALSE
  27700.     DO
  27701.        NumData% = NumData% + 1
  27702.        PRINT
  27703.        PRINT "Bar("; LTRIM$(STR$(NumData%)); "):"
  27704.        INPUT ; "        Label? ", Label$(NumData%)
  27705.  
  27706.        ' Only input value if label isn't blank:
  27707.        IF Label$(NumData%) <> "" THEN
  27708.           LOCATE , 35
  27709.           INPUT "Value? ", Value(NumData%)
  27710.  
  27711.        ' If label was blank, decrement data counter and
  27712.        ' set Done flag equal to TRUE:
  27713.        ELSE
  27714.           NumData% = NumData% - 1
  27715.           Done% = TRUE
  27716.        END IF
  27717.     LOOP UNTIL (NumData% = 5) OR Done%
  27718.  
  27719.     ' Return the number of data values input:
  27720.     InputData% = NumData%
  27721.  
  27722.  END FUNCTION
  27723.  '
  27724.  ' ======================= INPUTTITLES ========================
  27725.  '       Accepts input for the three different graph titles
  27726.  ' ============================================================
  27727.  '
  27728.  SUB InputTitles (T AS TitleType) STATIC
  27729.  
  27730.     ' Set text screen:
  27731.     SCREEN 0, 0
  27732.  
  27733.     ' Input Titles
  27734.     DO
  27735.        CLS
  27736.        INPUT "Enter main graph title: ", T.MainTitle
  27737.        INPUT "Enter X-Axis title    : ", T.XTitle
  27738.        INPUT "Enter Y-Axis title    : ", T.YTitle
  27739.  
  27740.        ' Check to see if titles are OK:
  27741.        LOCATE 7, 1
  27742.        PRINT "OK (Y to continue, N to change)? ";
  27743.        LOCATE , , 1
  27744.        OK$ = UCASE$(INPUT$(1))
  27745.     LOOP UNTIL OK$ = "Y"
  27746.  END SUB
  27747.  
  27748.  
  27749.  BIN2HEX.BAS
  27750.  CD-ROM Disc Path:   \SAMPCODE\QB\TOOLBOX\DISK1\BIN2HEX.BAS
  27751.  
  27752.    ' ************************************************
  27753.    ' **  Name:          BIN2HEX                    **
  27754.    ' **  Type:          Program                    **
  27755.    ' **  Module:        BIN2HEX.BAS                **
  27756.    ' **  Language:      Microsoft QuickBASIC 4.00  **
  27757.    ' ************************************************
  27758.    '
  27759.    ' Reads in any file and writes out a hexadecimal format file
  27760.    ' suitable for rebuilding the original file using the HEX2BIN
  27761.    ' program.
  27762.    '
  27763.    ' USAGE:          BIN2HEX inFileName.ext outFileName.ext
  27764.    ' .MAK FILE:      BIN2HEX.BAS
  27765.    '                 PARSE.BAS
  27766.    ' PARAMETERS:     inFileName    Name of file to be duplicated in hexadecima
  27767.    '                               format
  27768.    '                 outFileName   Name of hexadecimal format file to be creat
  27769.    ' VARIABLES:      cmd$          Working copy of the command line
  27770.    '                 inFile$       Name of input file
  27771.    '                 outFile$      Name of output file
  27772.    '                 byte$         Buffer for binary file access
  27773.    '                 i&            Index to each byte of input file
  27774.    '                 h$            Pair of hexadecimal characters representing
  27775.    '                               each byte
  27776.  
  27777.  
  27778.      DECLARE SUB ParseWord (a$, sep$, word$)
  27779.  
  27780.    ' Initialization
  27781.      CLS
  27782.      PRINT "BIN2HEX "; COMMAND$
  27783.      PRINT
  27784.  
  27785.    ' Get the input and output filenames from the command line
  27786.      cmd$ = COMMAND$
  27787.      ParseWord cmd$, " ,", inFile$
  27788.      ParseWord cmd$, " ,", outFile$
  27789.  
  27790.    ' Verify that both filenames were given
  27791.      IF outFile$ = "" THEN
  27792.          PRINT
  27793.          PRINT "Usage: BIN2HEX inFileName outFileName"
  27794.          SYSTEM
  27795.      END IF
  27796.  
  27797.    ' Open the input file
  27798.      OPEN inFile$ FOR BINARY AS #1 LEN = 1
  27799.      IF LOF(1) = 0 THEN
  27800.          CLOSE #1
  27801.          KILL inFile$
  27802.          PRINT
  27803.          PRINT "File not found - "; inFile$
  27804.          SYSTEM
  27805.      END IF
  27806.  
  27807.    ' Open the output file
  27808.      OPEN outFile$ FOR OUTPUT AS #2
  27809.  
  27810.    ' Process each byte of the file
  27811.      byte$ = SPACE$(1)
  27812.      FOR i& = 1 TO LOF(1)
  27813.          GET #1, , byte$
  27814.          h$ = RIGHT$("0" + HEX$(ASC(byte$)), 2)
  27815.          PRINT #2, h$; SPACE$(1);
  27816.          IF i& = LOF(1) THEN
  27817.              PRINT #2, ""
  27818.          ELSEIF i& MOD 16 = 0 THEN
  27819.              PRINT #2, ""
  27820.          ELSEIF i& MOD 8 = 0 THEN
  27821.              PRINT #2, "- ";
  27822.          END IF
  27823.      NEXT i&
  27824.  
  27825.    ' Clean up and quit
  27826.      CLOSE
  27827.      END
  27828.  
  27829.  
  27830.  
  27831.  
  27832.  BIOSCALL.BAS
  27833.  CD-ROM Disc Path:   \SAMPCODE\QB\TOOLBOX\DISK1\BIOSCALL.BAS
  27834.  
  27835.    ' ************************************************
  27836.    ' **  Name:          BIOSCALL                   **
  27837.    ' **  Type:          Toolbox                    **
  27838.    ' **  Module:        BIOSCALL.BAS               **
  27839.    ' **  Language:      Microsoft QuickBASIC 4.00  **
  27840.    ' ************************************************
  27841.    '
  27842.    ' Demonstrates several interrupt calls to the ROM BIOS.
  27843.    '
  27844.    ' USAGE: No command line parameters
  27845.    ' REQUIREMENTS:    MIXED.QLB/.LIB
  27846.    ' .MAK FILE:       (none)
  27847.    ' PARAMETERS:      (none)
  27848.    ' VARIABLES:       i%         Loop index for creating lines to scroll
  27849.    '                  equip      Structure of type EquipmentType
  27850.    '                  mode%      Video mode returned by VideoState
  27851.    '                  columns%   Video columns returned by VideoState
  27852.    '                  page%      Video page returned by VideoState
  27853.    '                  shift      Structure of type ShiftType
  27854.  
  27855.  
  27856.    ' Constants
  27857.      CONST FALSE = 0
  27858.      CONST TRUE = NOT FALSE
  27859.  
  27860.    ' Declare the Type structures
  27861.      TYPE RegType
  27862.          ax    AS INTEGER
  27863.          bx    AS INTEGER
  27864.          cx    AS INTEGER
  27865.          dx    AS INTEGER
  27866.          Bp    AS INTEGER
  27867.          si    AS INTEGER
  27868.          di    AS INTEGER
  27869.          flags AS INTEGER
  27870.      END TYPE
  27871.  
  27872.      TYPE RegTypeX
  27873.          ax    AS INTEGER
  27874.          bx    AS INTEGER
  27875.          cx    AS INTEGER
  27876.          dx    AS INTEGER
  27877.          Bp    AS INTEGER
  27878.          si    AS INTEGER
  27879.          di    AS INTEGER
  27880.          flags AS INTEGER
  27881.          ds    AS INTEGER
  27882.          es    AS INTEGER
  27883.      END TYPE
  27884.  
  27885.      TYPE EquipmentType
  27886.          printers     AS INTEGER
  27887.          gameAdapter  AS INTEGER
  27888.          serial       AS INTEGER
  27889.          floppies     AS INTEGER
  27890.          initialVideo AS INTEGER
  27891.          coprocessor  AS INTEGER
  27892.      END TYPE
  27893.  
  27894.      TYPE ShiftType
  27895.          right           AS INTEGER
  27896.          left            AS INTEGER
  27897.          ctrl            AS INTEGER
  27898.          alt             AS INTEGER
  27899.          scrollLockState AS INTEGER
  27900.          numLockState    AS INTEGER
  27901.          capsLockState   AS INTEGER
  27902.          insertState     AS INTEGER
  27903.      END TYPE
  27904.  
  27905.      DECLARE SUB Interrupt (intnum%, inreg AS RegType, outreg AS RegType)
  27906.      DECLARE SUB InterruptX (intnum%, inreg AS RegTypeX, outreg AS RegTypeX)
  27907.      DECLARE SUB PrintScreen ()
  27908.      DECLARE SUB Scroll (row1%, col1%, row2%, col2%, lines%, attribute%)
  27909.      DECLARE SUB Equipment (equip AS EquipmentType)
  27910.      DECLARE SUB VideoState (mode%, columns%, page%)
  27911.      DECLARE SUB GetShiftStates (shift AS ShiftType)
  27912.      DECLARE SUB ReBoot ()
  27913.  
  27914.    ' Demonstrate the Scroll subprogram
  27915.      CLS
  27916.      FOR i% = 1 TO 15
  27917.          COLOR i%, i% - 1
  27918.          PRINT STRING$(25, i% + 64)
  27919.      NEXT i%
  27920.      COLOR 7, 0
  27921.      PRINT
  27922.      PRINT "Press <Enter> to scroll part of the screen"
  27923.      DO
  27924.      LOOP UNTIL INKEY$ = CHR$(13)
  27925.      Scroll 2, 3, 6, 16, 3, SCREEN(2, 3, 1)
  27926.  
  27927.    ' Wait for user before continuing
  27928.      PRINT
  27929.      PRINT "Press any key to continue"
  27930.      DO
  27931.      LOOP UNTIL INKEY$ <> ""
  27932.      CLS
  27933.  
  27934.    ' Determine the equipment information
  27935.      DIM equip AS EquipmentType
  27936.      Equipment equip
  27937.      PRINT "Printers:", equip.printers
  27938.      PRINT "Game adapter:", equip.gameAdapter
  27939.      PRINT "Serial IO:", equip.serial
  27940.      PRINT "Floppies:", equip.floppies
  27941.      PRINT "Video:", equip.initialVideo
  27942.      PRINT "Coprocessor:", equip.coprocessor
  27943.  
  27944.    ' Determine the current video state
  27945.      PRINT
  27946.      VideoState mode%, columns%, page%
  27947.      PRINT "Video mode:", mode%
  27948.      PRINT "Text columns:", columns%
  27949.      PRINT "Video page:", page%
  27950.  
  27951.    ' Wait for user before continuing
  27952.      PRINT
  27953.      PRINT "Press any key to continue"
  27954.      DO
  27955.      LOOP UNTIL INKEY$ <> ""
  27956.  
  27957.    ' Demonstrate the shift key states
  27958.      CLS
  27959.      PRINT "(Press shift keys, then <Enter> to continue...)"
  27960.      DIM shift AS ShiftType
  27961.      DO
  27962.          LOCATE 4, 1
  27963.          PRINT "Shift states:"
  27964.          GetShiftStates shift
  27965.          PRINT
  27966.          PRINT "Left shift:", shift.left
  27967.          PRINT "Right shift:", shift.right
  27968.          PRINT "Ctrl:", shift.ctrl
  27969.          PRINT "Alt:", shift.alt
  27970.          PRINT "Scroll Lock:", shift.scrollLockState
  27971.          PRINT "Num Lock:", shift.numLockState
  27972.          PRINT "Caps Lock:", shift.capsLockState
  27973.          PRINT "Insert:", shift.insertState
  27974.      LOOP UNTIL INKEY$ = CHR$(13)
  27975.  
  27976.    ' Uncomment the following line to cause a screen dump to printer....
  27977.    ' PrintScreen
  27978.  
  27979.    ' Uncomment the following line only if you want to reboot....
  27980.    ' ReBoot
  27981.  
  27982.      END
  27983.  
  27984.  
  27985.    ' ************************************************
  27986.    ' **  Name:          Equipment                  **
  27987.    ' **  Type:          Subprogram                 **
  27988.    ' **  Module:        BIOSCALL.BAS               **
  27989.    ' **  Language:      Microsoft QuickBASIC 4.00  **
  27990.    ' ************************************************
  27991.    '
  27992.    ' Returns equipment configuration information from BIOS.
  27993.    '
  27994.    ' EXAMPLE OF USE:  Equipment equip
  27995.    ' PARAMETERS:      equip      Structure of type EquipmentType
  27996.    ' VARIABLES:       reg        Structure of type RegType
  27997.    ' MODULE LEVEL
  27998.    '   DECLARATIONS:  TYPE RegType
  27999.    '                     ax    AS INTEGER
  28000.    '                     bx    AS INTEGER
  28001.    '                     cx    AS INTEGER
  28002.    '                     dx    AS INTEGER
  28003.    '                     Bp    AS INTEGER
  28004.    '                     si    AS INTEGER
  28005.    '                     di    AS INTEGER
  28006.    '                     flags AS INTEGER
  28007.    '                  END TYPE
  28008.    '
  28009.    '                  TYPE EquipmentType
  28010.    '                     printers     AS INTEGER
  28011.    '                     gameAdapter  AS INTEGER
  28012.    '                     serial       AS INTEGER
  28013.    '                     floppies     AS INTEGER
  28014.    '                     initialVideo AS INTEGER
  28015.    '                     coprocessor  AS INTEGER
  28016.    '                  END TYPE
  28017.    '
  28018.    '     DECLARE SUB Interrupt (intnum%, inreg AS RegType, outreg AS RegType)
  28019.    '     DECLARE SUB Equipment (equip AS EquipmentType)
  28020.    '
  28021.      SUB Equipment (equip AS EquipmentType) STATIC
  28022.          DIM reg AS RegType
  28023.          Interrupt &H11, reg, reg
  28024.          equip.printers = (reg.ax AND &HC000&) \ 16384
  28025.          equip.gameAdapter = (reg.ax AND &H1000) \ 4096
  28026.          equip.serial = (reg.ax AND &HE00) \ 512
  28027.          equip.floppies = (reg.ax AND &HC0) \ 64 + 1
  28028.          equip.initialVideo = (reg.ax AND &H30) \ 16
  28029.          equip.coprocessor = (reg.ax AND 2) \ 2
  28030.      END SUB
  28031.  
  28032.    ' ************************************************
  28033.    ' **  Name:          GetShiftStates             **
  28034.    ' **  Type:          Subprogram                 **
  28035.    ' **  Module:        BIOSCALL.BAS               **
  28036.    ' **  Language:      Microsoft QuickBASIC 4.00  **
  28037.    ' ************************************************
  28038.    '
  28039.    ' Return state of the various shift keys.
  28040.    '
  28041.    ' EXAMPLE OF USE:  GetShiftStates shift
  28042.    ' PARAMETERS:      shift      Structure of type ShiftType
  28043.    ' VARIABLES:       reg        Structure of type RegType
  28044.    ' MODULE LEVEL
  28045.    '   DECLARATIONS:  TYPE RegType
  28046.    '                     ax    AS INTEGER
  28047.    '                     bx    AS INTEGER
  28048.    '                     cx    AS INTEGER
  28049.    '                     dx    AS INTEGER
  28050.    '                     Bp    AS INTEGER
  28051.    '                     si    AS INTEGER
  28052.    '                     di    AS INTEGER
  28053.    '                     flags AS INTEGER
  28054.    '                  END TYPE
  28055.    '
  28056.    '                  TYPE ShiftType
  28057.    '                     right           AS INTEGER
  28058.    '                     left            AS INTEGER
  28059.    '                     ctrl            AS INTEGER
  28060.    '                     alt             AS INTEGER
  28061.    '                     scrollLockState AS INTEGER
  28062.    '                     numLockState    AS INTEGER
  28063.    '                     capsLockState   AS INTEGER
  28064.    '                     insertState     AS INTEGER
  28065.    '                  END TYPE
  28066.    '
  28067.    '      DECLARE SUB Interrupt (intnum%, inreg AS RegType, outreg AS RegType)
  28068.    '      DECLARE SUB GetShiftStates (shift AS ShiftType)
  28069.    '
  28070.      SUB GetShiftStates (shift AS ShiftType) STATIC
  28071.          DIM reg AS RegType
  28072.          reg.ax = &H200
  28073.          Interrupt &H16, reg, reg
  28074.          shift.right = reg.ax AND 1
  28075.          shift.left = (reg.ax AND 2) \ 2
  28076.          shift.ctrl = (reg.ax AND 4) \ 4
  28077.          shift.alt = (reg.ax AND 8) \ 8
  28078.          shift.scrollLockState = (reg.ax AND 16) \ 16
  28079.          shift.numLockState = (reg.ax AND 32) \ 32
  28080.          shift.capsLockState = (reg.ax AND 64) \ 64
  28081.          shift.insertState = (reg.ax AND 128) \ 128
  28082.      END SUB
  28083.  
  28084.    ' ************************************************
  28085.    ' **  Name:          PrintScreen                **
  28086.    ' **  Type:          Subprogram                 **
  28087.    ' **  Module:        BIOSCALL.BAS               **
  28088.    ' **  Language:      Microsoft QuickBASIC 4.00  **
  28089.    ' ************************************************
  28090.    '
  28091.    ' Activates interrupt 5 to cause a dump of the
  28092.    ' screen's contents to the printer.
  28093.    '
  28094.    ' EXAMPLE OF USE:  PrintScreen
  28095.    ' PARAMETERS:      (none)
  28096.    ' VARIABLES:       reg        Structure of type RegType
  28097.    ' MODULE LEVEL
  28098.    '   DECLARATIONS:  TYPE RegType
  28099.    '                     ax    AS INTEGER
  28100.    '                     bx    AS INTEGER
  28101.    '                     cx    AS INTEGER
  28102.    '                     dx    AS INTEGER
  28103.    '                     Bp    AS INTEGER
  28104.    '                     si    AS INTEGER
  28105.    '                     di    AS INTEGER
  28106.    '                     flags AS INTEGER
  28107.    '                  END TYPE
  28108.    '
  28109.    '      DECLARE SUB Interrupt (intnum%, inreg AS RegType, outreg AS RegType)
  28110.    '      DECLARE SUB PrintScreen ()
  28111.    '
  28112.      SUB PrintScreen STATIC
  28113.          DIM reg AS RegType
  28114.          Interrupt 5, reg, reg
  28115.      END SUB
  28116.  
  28117.    ' ************************************************
  28118.    ' **  Name:          ReBoot                     **
  28119.    ' **  Type:          Subprogram                 **
  28120.    ' **  Module:        BIOSCALL.BAS               **
  28121.    ' **  Language:      Microsoft QuickBASIC 4.00  **
  28122.    ' ************************************************
  28123.    '
  28124.    ' Causes the computer to reboot.
  28125.    '
  28126.    ' EXAMPLE OF USE:  ReBoot
  28127.    ' PARAMETERS:      (none)
  28128.    ' VARIABLES:       reg        Structure of type RegType
  28129.    ' MODULE LEVEL
  28130.    '   DECLARATIONS:  TYPE RegType
  28131.    '                     ax    AS INTEGER
  28132.    '                     bx    AS INTEGER
  28133.    '                     cx    AS INTEGER
  28134.    '                     dx    AS INTEGER
  28135.    '                     Bp    AS INTEGER
  28136.    '                     si    AS INTEGER
  28137.    '                     di    AS INTEGER
  28138.    '                     flags AS INTEGER
  28139.    '                  END TYPE
  28140.    '
  28141.    '      DECLARE SUB Interrupt (intnum%, inreg AS RegType, outreg AS RegType)
  28142.    '      DECLARE SUB ReBoot ()
  28143.    '
  28144.      SUB ReBoot STATIC
  28145.          DIM reg AS RegType
  28146.          Interrupt &H19, reg, reg
  28147.      END SUB
  28148.  
  28149.    ' ************************************************
  28150.    ' **  Name:          Scroll                     **
  28151.    ' **  Type:          Subprogram                 **
  28152.    ' **  Module:        BIOSCALL.BAS               **
  28153.    ' **  Language:      Microsoft QuickBASIC 4.00  **
  28154.    ' ************************************************
  28155.    '
  28156.    ' Scrolls the screen in the rectangular area defined
  28157.    ' by the row and col parameters.  Positive line count
  28158.    ' moves the lines up, leaving blank lines at bottom;
  28159.    ' negative line count moves the lines down.
  28160.    '
  28161.    ' EXAMPLE OF USE:  Scroll row1%, col1%, row2%, col2%, lines%, attr%
  28162.    ' PARAMETERS:      row1%    Upper left character row defining rectangular
  28163.    '                           scroll area
  28164.    '                  col1     Upper left character column defining rectangula
  28165.    '                           scroll area
  28166.    '                  row2%    Lower right character row defining rectangular
  28167.    '                           scroll area
  28168.    '                  col2%    Lower right character column defining
  28169.    '                           rectangular scroll area
  28170.    '                  lines%   Number of character lines to scroll
  28171.    '                  attr%    Color attribute byte to be used in new text
  28172.    '                           lines scrolled onto the screen
  28173.    ' VARIABLES:       reg      Structure of type RegType
  28174.    ' MODULE LEVEL
  28175.    '   DECLARATIONS:  TYPE RegType
  28176.    '                     ax    AS INTEGER
  28177.    '                     bx    AS INTEGER
  28178.    '                     cx    AS INTEGER
  28179.    '                     dx    AS INTEGER
  28180.    '                     Bp    AS INTEGER
  28181.    '                     si    AS INTEGER
  28182.    '                     di    AS INTEGER
  28183.    '                     flags AS INTEGER
  28184.    '                  END TYPE
  28185.    '      DECLARE SUB Interrupt (intnum%, inreg AS RegType, outreg AS RegType)
  28186.    '      DECLARE SUB Scroll (row1%, col1%, row2%, col2%, lines%, attribute%)
  28187.    '
  28188.      SUB Scroll (row1%, col1%, row2%, col2%, lines%, attribute%) STATIC
  28189.          DIM reg AS RegType
  28190.          IF lines% > 0 THEN
  28191.              reg.ax = &H600 + lines% MOD 256
  28192.          ELSE
  28193.              reg.ax = &H700 + ABS(lines%) MOD 256
  28194.          END IF
  28195.          reg.bx = (attribute% * 256&) AND &HFF00
  28196.          reg.cx = (row1% - 1) * 256 + col1% - 1
  28197.          reg.dx = (row2% - 1) * 256 + col2% - 1
  28198.          Interrupt &H10, reg, reg
  28199.      END SUB
  28200.  
  28201.    ' ************************************************
  28202.    ' **  Name:          VideoState                 **
  28203.    ' **  Type:          Subprogram                 **
  28204.    ' **  Module:        BIOSCALL.BAS               **
  28205.    ' **  Language:      Microsoft QuickBASIC 4.00  **
  28206.    ' ************************************************
  28207.    '
  28208.    ' Determines the current video mode parameters.
  28209.    '
  28210.    ' EXAMPLE OF USE:  VideoState mode%, columns%, page%
  28211.    ' PARAMETERS:      mode%      Current video mode
  28212.    '                  columns%   Current number of text columns
  28213.    '                  page%      Current active display page
  28214.    ' VARIABLES:       reg        Structure of type RegType
  28215.    ' MODULE LEVEL
  28216.    '   DECLARATIONS:  TYPE RegType
  28217.    '                     ax    AS INTEGER
  28218.    '                     bx    AS INTEGER
  28219.    '                     cx    AS INTEGER
  28220.    '                     dx    AS INTEGER
  28221.    '                     Bp    AS INTEGER
  28222.    '                     si    AS INTEGER
  28223.    '                     di    AS INTEGER
  28224.    '                     flags AS INTEGER
  28225.    '                  END TYPE
  28226.    '
  28227.    '      DECLARE SUB Interrupt (intnum%, inreg AS RegType, outreg AS RegType)
  28228.    '      DECLARE SUB VideoState (mode%, columns%, page%)
  28229.    '
  28230.      SUB VideoState (mode%, columns%, page%) STATIC
  28231.          DIM reg AS RegType
  28232.          reg.ax = &HF00
  28233.          Interrupt &H10, reg, reg
  28234.          mode% = reg.ax AND &HFF
  28235.          columns% = (CLNG(reg.ax) AND &HFF00) \ 256
  28236.          page% = (CLNG(reg.bx) AND &HFF00) \ 256
  28237.      END SUB
  28238.  
  28239.  
  28240.  
  28241.  BITS.BAS
  28242.  CD-ROM Disc Path:   \SAMPCODE\QB\TOOLBOX\DISK1\BITS.BAS
  28243.  
  28244.    ' ************************************************
  28245.    ' **  Name:          BITS                       **
  28246.    ' **  Type:          Toolbox                    **
  28247.    ' **  Module:        BITS.BAS                   **
  28248.    ' **  Language:      Microsoft QuickBASIC 4.00  **
  28249.    ' ************************************************
  28250.    '
  28251.    ' Demonstrates the bit manipulation functions
  28252.    ' and subprograms.
  28253.    '
  28254.    ' USAGE: No command line parameters
  28255.    ' .MAK FILE:       (none)
  28256.    ' PARAMETERS:      (none)
  28257.    ' VARIABLES:       max%       Upper limit for the prime number generator
  28258.    '                  b$         Bit string for finding prime numbers
  28259.    '                  n%         Loop index for sieve of Eratosthenes
  28260.    '                  bit%       Bit retrieved from b$
  28261.    '                  i%         Bit loop index
  28262.    '                  q$         The double quote character
  28263.  
  28264.  
  28265.      DECLARE FUNCTION BinStr2Bin% (b$)
  28266.      DECLARE FUNCTION Bin2BinStr$ (b%)
  28267.  
  28268.    ' Subprograms
  28269.      DECLARE SUB BitGet (a$, bitIndex%, bit%)
  28270.      DECLARE SUB BitPut (b$, bitIndex%, bit%)
  28271.  
  28272.    ' Prime numbers less than max%, using bit fields in B$
  28273.      CLS
  28274.      max% = 1000
  28275.      PRINT "Primes up to"; max%; "using BitGet and BitPut for sieve..."
  28276.      PRINT
  28277.      PRINT 1; 2;
  28278.      b$ = STRING$(max% \ 8 + 1, 0)
  28279.      FOR n% = 3 TO max% STEP 2
  28280.          BitGet b$, n%, bit%
  28281.          IF bit% = 0 THEN
  28282.              PRINT n%;
  28283.              FOR i% = 3 * n% TO max% STEP n% + n%
  28284.                  BitPut b$, i%, 1
  28285.              NEXT i%
  28286.          END IF
  28287.      NEXT n%
  28288.      PRINT
  28289.  
  28290.    ' Demonstration of the Bin2BinStr$ function
  28291.      PRINT
  28292.      PRINT "Bin2BinStr$(12345) = "; Bin2BinStr$(12345)
  28293.  
  28294.    ' Demonstration of the BinStr2Bin% function
  28295.      PRINT
  28296.      q$ = CHR$(34)
  28297.      PRINT "BinStr2Bin%("; q$; "1001011"; q$; ") = ";
  28298.      PRINT BinStr2Bin%("1001011")
  28299.  
  28300.    ' That's all
  28301.      END
  28302.  
  28303.  
  28304.    ' ************************************************
  28305.    ' **  Name:          Bin2BinStr$                **
  28306.    ' **  Type:          Function                   **
  28307.    ' **  Module:        BITS.BAS                   **
  28308.    ' **  Language:      Microsoft QuickBASIC 4.00  **
  28309.    ' ************************************************
  28310.    '
  28311.    ' Returns a string of sixteen "0" and "1" characters
  28312.    ' that represent the binary value of b%.
  28313.    '
  28314.    ' EXAMPLE OF USE:  PRINT Bin2BinStr$(b%)
  28315.    ' PARAMETERS:      b%         Integer number
  28316.    ' VARIABLES:       t$         Working string space for forming binary strin
  28317.    '                  b%         Integer number
  28318.    '                  mask%      Bit isolation mask
  28319.    '                  i%         Looping index
  28320.    ' MODULE LEVEL
  28321.    '   DECLARATIONS:  DECLARE FUNCTION Bin2BinStr$ (b%)
  28322.    '
  28323.      FUNCTION Bin2BinStr$ (b%) STATIC
  28324.          t$ = STRING$(16, "0")
  28325.          IF b% THEN
  28326.              IF b% < 0 THEN
  28327.                  MID$(t$, 1, 1) = "1"
  28328.              END IF
  28329.              mask% = &H4000
  28330.              FOR i% = 2 TO 16
  28331.                  IF b% AND mask% THEN
  28332.                      MID$(t$, i%, 1) = "1"
  28333.                  END IF
  28334.                  mask% = mask% \ 2
  28335.              NEXT i%
  28336.          END IF
  28337.          Bin2BinStr$ = t$
  28338.      END FUNCTION
  28339.  
  28340.    ' ************************************************
  28341.    ' **  Name:          BinStr2Bin%                **
  28342.    ' **  Type:          Function                   **
  28343.    ' **  Module:        BITS.BAS                   **
  28344.    ' **  Language:      Microsoft QuickBASIC 4.00  **
  28345.    ' ************************************************
  28346.    '
  28347.    ' Returns the integer represented by a string of up
  28348.    ' to 16 "0" and "1" characters.
  28349.    '
  28350.    ' EXAMPLE OF USE:  PRINT BinStr2Bin%(b$)
  28351.    ' PARAMETERS:      b$         Binary representation string
  28352.    ' VARIABLES:       bin%       Working variable for finding value
  28353.    '                  t$         Working copy of b$
  28354.    '                  mask%      Bit mask for forming value
  28355.    '                  i%         Looping index
  28356.    ' MODULE LEVEL
  28357.    '   DECLARATIONS:  DECLARE FUNCTION BinStr2Bin% (b$)
  28358.    '
  28359.      FUNCTION BinStr2Bin% (b$) STATIC
  28360.          bin% = 0
  28361.          t$ = RIGHT$(STRING$(16, "0") + b$, 16)
  28362.          IF LEFT$(t$, 1) = "1" THEN
  28363.              bin% = &H8000
  28364.          END IF
  28365.          mask% = &H4000
  28366.          FOR i% = 2 TO 16
  28367.              IF MID$(t$, i%, 1) = "1" THEN
  28368.                  bin% = bin% OR mask%
  28369.              END IF
  28370.              mask% = mask% \ 2
  28371.          NEXT i%
  28372.          BinStr2Bin% = bin%
  28373.      END FUNCTION
  28374.  
  28375.    ' ************************************************
  28376.    ' **  Name:          BitGet                     **
  28377.    ' **  Type:          Subprogram                 **
  28378.    ' **  Module:        BITS.BAS                   **
  28379.    ' **  Language:      Microsoft QuickBASIC 4.00  **
  28380.    ' ************************************************
  28381.    '
  28382.    ' Extracts the bit at bitIndex% into a$ and returns
  28383.    ' either 0 or 1 in bit%.  The value of bitIndex%
  28384.    ' can range from 1 to 8 * LEN(a$).
  28385.    '
  28386.    ' EXAMPLE OF USE:  BitGet a$, bitIndex%, bit%
  28387.    ' PARAMETERS:      a$         String where bit is stored
  28388.    '                  bitIndex%  Bit position in string
  28389.    '                  bit%       Extracted bit value, 0 or 1
  28390.    ' VARIABLES:       byte%      Byte location in string of the bit
  28391.    '                  mask%      Bit isolation mask for given bit
  28392.    ' MODULE LEVEL
  28393.    '   DECLARATIONS:  DECLARE SUB BitGet (a$, bitIndex%, bit%)
  28394.    '
  28395.      SUB BitGet (a$, bitIndex%, bit%) STATIC
  28396.          byte% = (bitIndex% - 1) \ 8 + 1
  28397.          SELECT CASE bitIndex% MOD 8
  28398.          CASE 1
  28399.              mask% = 128
  28400.          CASE 2
  28401.              mask% = 64
  28402.          CASE 3
  28403.              mask% = 32
  28404.          CASE 4
  28405.              mask% = 16
  28406.          CASE 5
  28407.              mask% = 8
  28408.          CASE 6
  28409.              mask% = 4
  28410.          CASE 7
  28411.              mask% = 2
  28412.          CASE 0
  28413.              mask% = 1
  28414.          END SELECT
  28415.          IF ASC(MID$(a$, byte%, 1)) AND mask% THEN
  28416.              bit% = 1
  28417.          ELSE
  28418.              bit% = 0
  28419.          END IF
  28420.      END SUB
  28421.  
  28422.    ' ************************************************
  28423.    ' **  Name:          BitPut                     **
  28424.    ' **  Type:          Subprogram                 **
  28425.    ' **  Module:        BITS.BAS                   **
  28426.    ' **  Language:      Microsoft QuickBASIC 4.00  **
  28427.    ' ************************************************
  28428.    '
  28429.    ' If bit% is non-zero, then the bit at bitIndex% into
  28430.    ' a$ is set to 1; otherwise, it's set to 0. The value
  28431.    ' of bitIndex% can range from 1 to 8 * LEN(a$).
  28432.    '
  28433.    ' EXAMPLE OF USE:  BitPut a$, bitIndex%, bit%
  28434.    ' PARAMETERS:      a$         String containing the bits
  28435.    '                  bitIndex%  Index to the bit of concern
  28436.    '                  bit%       Value of bit (1 to set, 0 to clear)
  28437.    ' VARIABLES:       bytePtr%   Pointer to the byte position in the string
  28438.    '                  mask%      Bit isolation mask
  28439.    '                  byteNow%   Current numeric value of string byte
  28440.    ' MODULE LEVEL
  28441.    '   DECLARATIONS:  DECLARE SUB BitPut (b$, bitIndex%, bit%)
  28442.    '
  28443.      SUB BitPut (a$, bitIndex%, bit%) STATIC
  28444.          bytePtr% = bitIndex% \ 8 + 1
  28445.          SELECT CASE bitIndex% MOD 8
  28446.          CASE 1
  28447.              mask% = 128
  28448.          CASE 2
  28449.              mask% = 64
  28450.          CASE 3
  28451.              mask% = 32
  28452.          CASE 4
  28453.              mask% = 16
  28454.          CASE 5
  28455.              mask% = 8
  28456.          CASE 6
  28457.              mask% = 4
  28458.          CASE 7
  28459.              mask% = 2
  28460.          CASE 0
  28461.              mask% = 1
  28462.              bytePtr% = bytePtr% - 1
  28463.          END SELECT
  28464.          byteNow% = ASC(MID$(a$, bytePtr%, 1))
  28465.          IF byteNow% AND mask% THEN
  28466.              IF bit% = 0 THEN
  28467.                  MID$(a$, bytePtr%, 1) = CHR$(byteNow% XOR mask%)
  28468.              END IF
  28469.          ELSE
  28470.              IF bit% THEN
  28471.                  MID$(a$, bytePtr%, 1) = CHR$(byteNow% XOR mask%)
  28472.              END IF
  28473.          END IF
  28474.      END SUB
  28475.  
  28476.  
  28477.  
  28478.  CAL.BAS
  28479.  CD-ROM Disc Path:   \SAMPCODE\QB\SRCDISK\CAL.BAS
  28480.  
  28481.  DEFINT A-Z               ' Default variable type is integer
  28482.  
  28483.  ' Define a data type for the names of the months and the
  28484.  ' number of days in each:
  28485.  TYPE MonthType
  28486.     Number AS INTEGER     ' Number of days in the month
  28487.     MName AS STRING * 9   ' Name of the month
  28488.  END TYPE
  28489.  
  28490.  ' Declare procedures used:
  28491.  DECLARE FUNCTION IsLeapYear% (N%)
  28492.  DECLARE FUNCTION GetInput% (Prompt$, Row%, LowVal%, HighVal%)
  28493.  
  28494.  DECLARE SUB PrintCalendar (Year%, Month%)
  28495.  DECLARE SUB ComputeMonth (Year%, Month%, StartDay%, TotalDays%)
  28496.  
  28497.  DIM MonthData(1 TO 12) AS MonthType
  28498.  
  28499.  ' Initialize month definitions from DATA statements below:
  28500.  FOR I = 1 TO 12
  28501.     READ MonthData(I).MName, MonthData(I).Number
  28502.  NEXT
  28503.  
  28504.  ' Main loop, repeat for as many months as desired:
  28505.  DO
  28506.  
  28507.     CLS
  28508.  
  28509.     ' Get year and month as input:
  28510.     Year = GetInput("Year (1899 to 2099): ", 1, 1899, 2099)
  28511.     Month = GetInput("Month (1 to 12): ", 2, 1, 12)
  28512.  
  28513.     ' Print the calendar:
  28514.     PrintCalendar Year, Month
  28515.  
  28516.     ' Another Date?
  28517.     LOCATE 13, 1         ' Locate in 13th row, 1st column
  28518.     PRINT "New Date? ";  ' Keep cursor on same line
  28519.     LOCATE , , 1, 0, 13  ' Turn cursor on and make it one
  28520.                          ' character high
  28521.     Resp$ = INPUT$(1)    ' Wait for a key press
  28522.     PRINT Resp$          ' Print the key pressed
  28523.  
  28524.  LOOP WHILE UCASE$(Resp$) = "Y"
  28525.  END
  28526.  
  28527.  ' Data for the months of a year:
  28528.  DATA January, 31, February, 28, March, 31
  28529.  DATA April, 30, May, 31, June, 30, July, 31, August, 31
  28530.  DATA September, 30, October, 31, November, 30, December, 31
  28531.  '
  28532.  ' ====================== COMPUTEMONTH ========================
  28533.  '     Computes the first day and the total days in a month.
  28534.  ' ============================================================
  28535.  '
  28536.  SUB ComputeMonth (Year, Month, StartDay, TotalDays) STATIC
  28537.     SHARED MonthData() AS MonthType
  28538.     CONST LEAP = 366 MOD 7
  28539.     CONST NORMAL = 365 MOD 7
  28540.  
  28541.     ' Calculate total number of days (NumDays) since 1/1/1899.
  28542.  
  28543.     ' Start with whole years:
  28544.     NumDays = 0
  28545.     FOR I = 1899 TO Year - 1
  28546.        IF IsLeapYear(I) THEN         ' If year is leap, add
  28547.           NumDays = NumDays + LEAP   ' 366 MOD 7.
  28548.        ELSE                          ' If normal year, add
  28549.           NumDays = NumDays + NORMAL ' 365 MOD 7.
  28550.        END IF
  28551.     NEXT
  28552.  
  28553.     ' Next, add in days from whole months:
  28554.     FOR I = 1 TO Month - 1
  28555.        NumDays = NumDays + MonthData(I).Number
  28556.     NEXT
  28557.  
  28558.     ' Set the number of days in the requested month:
  28559.     TotalDays = MonthData(Month).Number
  28560.  
  28561.     ' Compensate if requested year is a leap year:
  28562.     IF IsLeapYear(Year) THEN
  28563.  
  28564.        ' If after February, add one to total days:
  28565.        IF Month > 2 THEN
  28566.           NumDays = NumDays + 1
  28567.  
  28568.        ' If February, add one to the month's days:
  28569.        ELSEIF Month = 2 THEN
  28570.           TotalDays = TotalDays + 1
  28571.  
  28572.        END IF
  28573.     END IF
  28574.  
  28575.     ' 1/1/1899 was a Sunday, so calculating "NumDays MOD 7"
  28576.     ' gives the day of week (Sunday = 0, Monday = 1, Tuesday = 2,
  28577.     ' and so on) for the first day of the input month:
  28578.     StartDay = NumDays MOD 7
  28579.  END SUB
  28580.  '
  28581.  ' ======================== GETINPUT ==========================
  28582.  '       Prompts for input, then tests for a valid range.
  28583.  ' ============================================================
  28584.  '
  28585.  FUNCTION GetInput (Prompt$, Row, LowVal, HighVal) STATIC
  28586.  
  28587.     ' Locate prompt at specified row, turn cursor on and
  28588.     ' make it one character high:
  28589.     LOCATE Row, 1, 1, 0, 13
  28590.     PRINT Prompt$;
  28591.  
  28592.     ' Save column position:
  28593.     Column = POS(0)
  28594.  
  28595.     ' Input value until it's within range:
  28596.     DO
  28597.        LOCATE Row, Column   ' Locate cursor at end of prompt
  28598.        PRINT SPACE$(10)     ' Erase anything already there
  28599.        LOCATE Row, Column   ' Relocate cursor at end of prompt
  28600.        INPUT "", Value      ' Input value wi