home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / pascal / turbo55 / install / tcalc.arc / TCRUN.PAS < prev    next >
Pascal/Delphi Source File  |  1989-05-02  |  33KB  |  1,366 lines

  1.  
  2. { Copyright (c) 1989 by Borland International, Inc. }
  3.  
  4. unit TCRun;
  5. { Turbo Pascal 5.5 object-oriented example run module.
  6.   This unit is used by TCALC.PAS.
  7.   See TCALC.DOC for an more information about this example.
  8. }
  9.  
  10. {$N+,S-}
  11.  
  12. interface
  13.  
  14. uses Crt, Dos, TCUtil, TCLStr, TCScreen, TCHash, TCCell, TCCellSp, TCSheet,
  15.      TCInput, TCParser, TCMenu;
  16.  
  17. const
  18.   FreeListItems = 1000;
  19.   MenuHeapSpace = 1000;
  20.   MaxSpreadsheets = (MinScreenRows - EmptyRowsAtTop - EmptyRowsAtBottom) div
  21.                     4;
  22.   LegalJustification = ['L', 'C', 'R'];
  23.   HelpLine = 'F2\Save\F3\Load\F7\Formula\F8\AutoCalc\F9\Recalc\F10\Menu\Ins\Block\Alt-X\Exit';
  24.   TitleString = 'TurboCalc - Turbo Pascal Demo Program';
  25.   MainMenuString = 'Spreadsheet, Block, Column, Row, Format, Goto, Edit, Options, Quit';
  26.   SpreadsheetMenuString = 'Load, Save, Zap, Write, Open, Close, Next, Print';
  27.   OpenMenuString = 'Load, New';
  28.   BlockMenuString = 'Copy, Delete, Format, Restore default format';
  29.   ColumnMenuString = 'Insert, Delete, Width';
  30.   RowMenuString = 'Insert, Delete';
  31.   UtilityMenuString1 = 'Screen lines, Recalc, Formula display, Autocalc';
  32.   UtilityMenuString2 = 'Recalc, Formula display, Autocalc';
  33.   PromptFileLoad = 'File to load';
  34.   PromptGotoCell = 'Go to cell';
  35.   PromptCopyCell = 'Copy to cell';
  36.   PromptColLiteral = 'Copy formula columns literally';
  37.   PromptRowLiteral = 'Copy formula rows literally';
  38.   PromptCopySpreadsheet = 'Copy to spreadsheet number (0 = current)';
  39.   PromptFormatPlaces = 'Number of decimal places';
  40.   PromptFormatJustification = 'Justification - (L)eft, (C)enter, (R)ight';
  41.   PromptFormatDollar = 'Dollar format';
  42.   PromptFormatCommas = 'Put commas in numbers';
  43.   ErrFreeList = 'The free list is full';
  44.   MsgBlockCopy = 'Copying block';
  45.  
  46. type
  47.   ProgramObject = object
  48.     SSData, CurrSS : SpreadsheetPtr;
  49.     TotalSheets : Byte;
  50.     CellInput : InputField;
  51.     MainMenu : Menu;
  52.     SpreadsheetMenu : Menu;
  53.     OpenMenu : Menu;
  54.     BlockMenu : Menu;
  55.     ColumnMenu : Menu;
  56.     RowMenu : Menu;
  57.     UtilityMenu : Menu;
  58.     Stop : Boolean;
  59.     constructor Init;
  60.     destructor Done;
  61.     procedure GetCommands;
  62.     procedure SetDisplayAreas;
  63.     procedure DisplayAll;
  64.     function AddSheet(Name : PathStr) : Boolean;
  65.     procedure DeleteSheet;
  66.   end;
  67.  
  68. var
  69.   Vars : ProgramObject;
  70.  
  71. procedure Run;
  72.  
  73. implementation
  74.  
  75. const
  76.   RedrawYes = True;
  77.   RedrawNo = False;
  78.  
  79. {$F+}
  80.  
  81. function RunHeapError(Size : Word) : Integer;
  82. { Prints an error if the heap runs out of memory }
  83. begin
  84.   Scr.PrintError(ErrNoMemory);
  85.   RunHeapError := 1;
  86. end; { RunHeapError }
  87.  
  88. {$F-}
  89.  
  90. procedure InitMenus; forward;
  91.  
  92. constructor ProgramObject.Init;
  93. { Sets up the program }
  94. var
  95.   Counter : Word;
  96.   Good : Boolean;
  97. begin { ProgramObject.Init }
  98.   if MaxAvail < MenuHeapSpace then
  99.     Abort(ErrNoMemory);
  100.   InitMenus;
  101.   TotalSheets := 0;
  102.   SSData := nil;
  103.   CurrSS := nil;
  104.   Stop := False;
  105.   if ParamCount = 0 then         { Load spreadsheets named on command line }
  106.   begin
  107.     if not AddSheet('') then
  108.       Abort(ErrNoMemory);
  109.   end
  110.   else begin
  111.     Counter := 1;
  112.     repeat
  113.       Good := AddSheet(ParamStr(Counter));
  114.       Inc(Counter);
  115.     until (not Good) or (Counter > Min(ParamCount, MaxSpreadsheets));
  116.   end;
  117.   SetDisplayAreas;
  118.   DisplayAll;
  119.   with CurrSS^ do
  120.   begin
  121.     MakeCurrent;
  122.     DisplayCell(CurrPos);
  123.   end; { with }
  124. end; { ProgramObject.Init }
  125.  
  126. destructor ProgramObject.Done;
  127. { Releases all memory used by the program }
  128. begin
  129.   CurrSS^.MakeNotCurrent;
  130.   while SSData <> nil do
  131.   begin
  132.     CurrSS := SSData;
  133.     SSData := SSData^.Next;
  134.     with CurrSS^ do
  135.     begin
  136.       MakeCurrent;
  137.       DisplayCell(CurrPos);
  138.       CheckForSave;
  139.       MakeNotCurrent;
  140.       DisplayCell(CurrPos);
  141.       Dispose(CurrSS, Done);
  142.     end; { with }
  143.   end;
  144.   MainMenu.Done;
  145.   SpreadsheetMenu.Done;
  146.   OpenMenu.Done;
  147.   BlockMenu.Done;
  148.   ColumnMenu.Done;
  149.   RowMenu.Done;
  150.   UtilityMenu.Done;
  151. end; { ProgramObject.Done }
  152.  
  153. function GetFormat(var Format : Byte) : Boolean;
  154. { Reads a format value from the keyboard }
  155. var
  156.   Places : Byte;
  157.   J : Justification;
  158.   ESCPressed, Good, Dollar, Commas : Boolean;
  159.   Ch : Char;
  160. begin
  161.   GetFormat := False;
  162.   Dollar := GetYesNo(PromptFormatDollar, ESCPressed);
  163.   if ESCPressed then
  164.     Exit;
  165.   if Dollar then
  166.   begin
  167.     Places := 2;
  168.     J := JRight;
  169.   end
  170.   else begin
  171.     Places := GetNumber(PromptFormatPlaces, 0,
  172.                         Vars.CurrSS^.MaxDecimalPlaces, Good);
  173.     if not Good then
  174.       Exit;
  175.     Ch := GetLegalChar(PromptFormatJustification, LegalJustification,
  176.                        ESCPressed);
  177.     if ESCPressed then
  178.       Exit;
  179.     case Ch of
  180.       'L' : J := JLeft;
  181.       'C' : J := JCenter;
  182.       'R' : J := JRight;
  183.     end; { case }
  184.   end;
  185.   Commas := GetYesNo(PromptFormatCommas, ESCPressed);
  186.   if ESCPressed then
  187.     Exit;
  188.   Format := Places + (Ord(J) shl 4) + (Ord(Dollar) shl 6) +
  189.             (Ord(Commas) shl 7);
  190.   GetFormat := True;
  191. end; { GetFormat }
  192.  
  193. procedure EditInput(Ch : Word; Editing : Boolean);
  194. { Edits the data on the input line }
  195. var
  196.   Good, FirstEdit, Deleted : Boolean;
  197.   P : CellPos;
  198. begin
  199.   with Vars, CurrSS^ do
  200.   begin
  201.     if not CellInput.Init(1, 0, -1, 0, NotUpper) then
  202.       Exit;
  203.     with CellInput.InputData^ do
  204.     begin
  205.       if Editing then
  206.       begin
  207.         Good := True;
  208.         CellHash.Search(CurrPos)^.EditString(MaxDecimalPlaces,
  209.                         CellInput.InputData)
  210.       end
  211.       else
  212.         Good := FromString(Chr(Ch));
  213.       if not Good then
  214.       begin
  215.         CellInput.Done;
  216.         Exit;
  217.       end;
  218.       FirstEdit := True;
  219.       Parser.Init(@CellHash, CellInput.InputData, MaxCols, MaxRows);
  220.       repeat
  221.         if FirstEdit then
  222.           CellInput.Edit(0)
  223.         else
  224.           CellInput.Edit(Parser.Position);
  225.         if Length > 0 then
  226.         begin
  227.           Parser.Parse;
  228.           if Parser.TokenError = 0 then
  229.           begin
  230.             DeleteCell(CurrPos, Deleted);
  231.             Good := AddCell(Parser.CType, CurrPos, Parser.ParseError,
  232.                             Parser.ParseValue, CellInput.InputData);
  233.           end;
  234.         end;
  235.         FirstEdit := False;
  236.       until (Length = 0) or (Parser.TokenError = 0);
  237.       if Length > 0 then
  238.       begin
  239.         SetChanged(WasChanged);
  240.         if AutoCalc then
  241.           Update(DisplayYes);
  242.         P := CurrPos;
  243.         for P.Col := CurrPos.Col to ScreenBlock.Stop.Col do
  244.           DisplayCell(P);
  245.       end;
  246.       CellInput.InputArea.Clear;
  247.     end; { with }
  248.     CellInput.Done;
  249.     DisplayMemory;
  250.   end; { with }
  251. end; { EditInput }
  252.  
  253. procedure OpenSpreadsheet(Name : String);
  254. { Opens a new spreadsheet }
  255. begin
  256.   with Vars do
  257.   begin
  258.     if not AddSheet(Name) then
  259.       Exit;
  260.     SetDisplayAreas;
  261.     DisplayAll;
  262.     with CurrSS^ do
  263.     begin
  264.       MakeCurrent;
  265.       DisplayCell(CurrPos);
  266.     end; { with }
  267.   end; { with }
  268. end; { OpenSpreadsheet }
  269.  
  270. procedure ClearCurrBlock;
  271. { Turns off the block and redisplays the cells in it }
  272. begin
  273.   with Vars.CurrSS^ do
  274.   begin
  275.     if BlockOn then
  276.     begin
  277.       BlockOn := False;
  278.       DisplayBlock(CurrBlock);
  279.     end;
  280.   end;
  281. end; { ClearCurrBlock }
  282.  
  283. {$F+}
  284.  
  285. procedure ReplaceSpreadsheet;
  286. { Load a spreadsheet over the current one }
  287. var
  288.   S : PathStr;
  289.   ESCPressed : Boolean;
  290. begin
  291.   with Vars.CurrSS^ do
  292.   begin
  293.     S := ReadString(PromptFileLoad, Pred(SizeOf(PathStr)), ESCPressed);
  294.     if S = '' then
  295.       Exit;
  296.     CheckForSave;
  297.     Done;
  298.     if FromFile(S) then
  299.     begin
  300.       SetChanged(NotChanged);
  301.       SetScreenColStart(1);
  302.       SetScreenRowStart(1);
  303.       Display;
  304.       MakeCurrent;
  305.       DisplayCell(CurrPos);
  306.     end;
  307.   end; { with }
  308. end; { ReplaceSpreadsheet }
  309.  
  310. procedure NameSaveSpreadsheet;
  311. { Save a spreadsheet to a file other that its default }
  312. var
  313.   St : PathStr;
  314.   ESCPressed : Boolean;
  315. begin
  316.   with Vars.CurrSS^ do
  317.   begin
  318.     St := ReadString(PromptFileSave, Pred(SizeOf(PathStr)), ESCPressed);
  319.     if St = '' then
  320.       Exit;
  321.     if FileExists(St) then
  322.     begin
  323.       if not GetYesNo(PromptOverwriteFile, ESCPressed) then
  324.         Exit;
  325.     end;
  326.     ToFile(St);
  327.     DisplayFileName;
  328.   end; { with }
  329. end; { NameSaveSpreadsheet }
  330.  
  331. procedure SaveCurrSpreadsheet;
  332. { Save a spreadsheet to its default file }
  333. begin
  334.   with Vars.CurrSS^ do
  335.   begin
  336.     if FileName = '' then
  337.       NameSaveSpreadsheet
  338.     else
  339.       ToFile(FileName);
  340.   end; { with }
  341. end; { SaveCurrSpreadsheet }
  342.  
  343. procedure ZapSpreadsheet;
  344. { Clear the current spreadsheet from memory }
  345. var
  346.   S : PathStr;
  347. begin
  348.   with Vars.CurrSS^ do
  349.   begin
  350.     CheckForSave;
  351.     S := FileName;
  352.     Done;
  353.     Init(0, DefaultMaxCols, DefaultMaxRows, DefaultMaxDecimalPlaces,
  354.          DefaultDefaultDecimalPlaces, DefaultDefaultColWidth);
  355.     MakeCurrent;
  356.     FileName := S;
  357.     SetScreenColStart(1);
  358.     SetScreenRowStart(1);
  359.     Display;
  360.   end; { with }
  361. end; { ZapSpreadsheet }
  362.  
  363. procedure CloseSpreadsheet;
  364. { Delete a spreadsheet, closing the window that it is in }
  365. begin
  366.   with Vars, CurrSS^ do
  367.   begin
  368.     if TotalSheets = 1 then
  369.       Exit;
  370.     DeleteSheet;
  371.   end; { with }
  372. end; { CloseSpreadsheet }
  373.  
  374. procedure NextSpreadsheet;
  375. { Move to the next spreadsheet }
  376. begin
  377.   with Vars do
  378.   begin
  379.     if TotalSheets = 1 then
  380.       Exit;
  381.     with CurrSS^ do
  382.     begin
  383.       MakeNotCurrent;
  384.       DisplayCell(CurrPos);
  385.     end; { with }
  386.     CurrSS := CurrSS^.Next;
  387.     if CurrSS = nil then
  388.       CurrSS := SSData;
  389.     with CurrSS^ do
  390.     begin
  391.       MakeCurrent;
  392.       DisplayCell(CurrPos);
  393.     end; { with }
  394.   end; { with }
  395. end; { NextSpreadsheet }
  396.  
  397. procedure NewSpreadsheet;
  398. { Create a new spreadsheet, opening a window for it and loading it }
  399. var
  400.   S : PathStr;
  401.   ESCPressed : Boolean;
  402. begin
  403.   with Vars do
  404.   begin
  405.     if TotalSheets >= MaxSpreadsheets then
  406.       Exit;
  407.     S := ReadString(PromptFileLoad, Pred(SizeOf(PathStr)), ESCPressed);
  408.     if S = '' then
  409.       Exit;
  410.     OpenSpreadsheet(S);
  411.   end; { with }
  412. end; { NewSpreadsheet }
  413.  
  414. procedure NewBlankSpreadsheet;
  415. { Create a new blank spreadsheet, opening a window for it }
  416. begin
  417.   with Vars do
  418.   begin
  419.     if TotalSheets >= MaxSpreadsheets then
  420.       Exit;
  421.     OpenSpreadsheet('');
  422.   end; { with }
  423. end; { NewBlankSpreadsheet }
  424.  
  425. procedure PrintSpreadsheet;
  426. { Print a spreadsheet to a file or a printer }
  427. begin
  428.   Vars.CurrSS^.Print;
  429. end; { PrintSpreadsheet }
  430.  
  431. procedure CopyBlock;
  432. { Copy a block of cells from one spreadsheet to the same or a different
  433.   spreadsheet }
  434. var
  435.   P, N, C : CellPos;
  436.   Good, ESCPressed, ColLit, RowLit, AnyChanged, Deleted : Boolean;
  437.   CP : CellPtr;
  438.   L : LStringPtr;
  439.   CopyTo : SpreadsheetPtr;
  440.   CopySheet : Byte;
  441.   Counter : Word;
  442. begin
  443.   with Vars, CurrSS^, CurrBlock do
  444.   begin
  445.     if not BlockOn then
  446.       Exit;
  447.     if TotalSheets > 1 then
  448.       CopySheet := GetNumber(PromptCopySpreadsheet, 0, TotalSheets, Good)
  449.     else
  450.       CopySheet := 1;
  451.     if not Good then
  452.       Exit;
  453.     if not GetCellPos(PromptCopyCell, MaxCols, MaxRows, ColSpace,
  454.                       RowNumberSpace, P) then
  455.       Exit;
  456.     ColLit := GetYesNo(PromptColLiteral, ESCPressed);
  457.     if ESCPressed then
  458.       Exit;
  459.     RowLit := GetYesNo(PromptRowLiteral, ESCPressed);
  460.     if ESCPressed then
  461.       Exit;
  462.     Scr.PrintMessage(MsgBlockCopy);
  463.     if CopySheet = 0 then
  464.       CopyTo := CurrSS
  465.     else begin
  466.       CopyTo := SSData;
  467.       for Counter := 2 to CopySheet do
  468.         CopyTo := CopyTo^.Next;
  469.     end;
  470.     AnyChanged := False;
  471.     C.Row := P.Row;
  472.     N.Row := Start.Row;
  473.     L := New(LStringPtr, Init);
  474.     Good := L <> nil;
  475.     while Good and (N.Row <= Stop.Row) do
  476.     begin
  477.       C.Col := P.Col;
  478.       N.Col := Start.Col;
  479.       while Good and (N.Col <= Stop.Col) do
  480.       begin
  481.         if (Longint(P.Col) + N.Col - Start.Col <= MaxCols) and
  482.            (Longint(P.Row) + N.Row - Start.Row <= MaxRows) then
  483.         begin
  484.           CopyTo^.DeleteCell(C, Deleted);
  485.           if Deleted then
  486.             AnyChanged := True;
  487.           CP := CellHash.Search(N);
  488.           if CP <> Empty then
  489.           begin
  490.             AnyChanged := True;
  491.             with CP^ do
  492.               Good := CopyTo^.AddCell(CellType, C, HasError, CurrValue,
  493.                                       CopyString(ColLit, RowLit,
  494.                                       Longint(C.Col) - N.Col, L));
  495.             if Good and ((not ColLit) or (not RowLit)) then
  496.             begin
  497.               CP := CopyTo^.CellHash.Search(C);
  498.               if CP^.ShouldUpdate then
  499.               begin
  500.                 if not ColLit then
  501.                   FixFormulaCol(CP, Longint(C.Col) - N.Col, MaxCols,
  502.                                 MaxRows);
  503.                 if not RowLit then
  504.                   FixFormulaRow(CP, Longint(C.Row) - N.Row, MaxCols,
  505.                                 MaxRows);
  506.               end;
  507.             end;
  508.           end;
  509.         end;
  510.         Inc(C.Col);
  511.         Inc(N.Col);
  512.       end;
  513.       Inc(C.Row);
  514.       Inc(N.Row);
  515.     end;
  516.     if AnyChanged then
  517.     begin
  518.       if CopySheet = 0 then
  519.         BlockOn := False;
  520.       with CopyTo^ do
  521.       begin
  522.         SetLastPos(LastPos);
  523.         SetChanged(WasChanged);
  524.         if AutoCalc then
  525.           Update(DisplayNo);
  526.         DisplayAllCells;
  527.         DisplayMemory;
  528.       end; { with }
  529.       if CopySheet <> 0 then
  530.         ClearCurrBlock;
  531.     end
  532.     else
  533.       ClearCurrBlock;
  534.     Scr.ClearMessage;
  535.   end; { with }
  536.   if L <> nil then
  537.     Dispose(L, Done);
  538. end; { CopyBlock }
  539.  
  540. procedure DeleteBlock;
  541. { Delete a block of cells }
  542. var
  543.   Deleted : Boolean;
  544. begin
  545.   with Vars.CurrSS^, CurrBlock do
  546.   begin
  547.     if not BlockOn then
  548.       Exit;
  549.     DeleteBlock(CurrBlock, Deleted);
  550.     if Deleted then
  551.     begin
  552.       BlockOn := False;
  553.       SetLastPos(LastPos);
  554.       SetChanged(WasChanged);
  555.       if AutoCalc then
  556.         Update(DisplayNo);
  557.       DisplayMemory;
  558.       DisplayAllCells;
  559.     end
  560.     else
  561.       ClearCurrBlock;
  562.   end; { with }
  563. end; { DeleteBlock }
  564.  
  565. procedure FormatBlock;
  566. { Format a block of cells }
  567. var
  568.   Format : Byte;
  569. begin
  570.   with Vars.CurrSS^ do
  571.   begin
  572.     if not BlockOn then
  573.       Exit;
  574.     if not GetFormat(Format) then
  575.       Exit;
  576.     with CurrBlock do
  577.     begin
  578.       if not FormatHash.Add(Start, Stop, Format) then
  579.         Exit;
  580.       SetChanged(WasChanged);
  581.       DisplayAllCells;
  582.       DisplayMemory;
  583.     end; { with }
  584.   end; { with }
  585. end; { FormatBlock }
  586.  
  587. procedure FormatDefault;
  588. { Change the format of a block of cells to the default }
  589. begin
  590.   with Vars.CurrSS^ do
  591.   begin
  592.     if not BlockOn then
  593.       Exit;
  594.     with CurrBlock do
  595.     begin
  596.       if not FormatHash.Delete(Start, Stop) then
  597.         Exit;
  598.       SetChanged(WasChanged);
  599.       DisplayAllCells;
  600.       DisplayMemory;
  601.     end; { with }
  602.   end; { with }
  603. end; { FormatDefault }
  604.  
  605. procedure ColInsert;
  606. { Insert a column into the spreadsheet }
  607. begin
  608.   Vars.CurrSS^.InsertColumn;
  609. end; { ColInsert }
  610.  
  611. procedure ColDelete;
  612. { Delete a column from the spreadsheet }
  613. begin
  614.   Vars.CurrSS^.DeleteColumn;
  615. end; { ColDelete }
  616.  
  617. procedure ChangeColWidth;
  618. { Change the width of a column }
  619. begin
  620.   Vars.CurrSS^.ChangeWidth;
  621. end; { ChangeColWidth }
  622.  
  623. procedure RowInsert;
  624. { Insert a row into the spreadsheet }
  625. begin
  626.   Vars.CurrSS^.InsertRow;
  627. end; { RowInsert }
  628.  
  629. procedure RowDelete;
  630. { Delete a row from the spreadsheet }
  631. begin
  632.   Vars.CurrSS^.DeleteRow;
  633. end; { RowDelete }
  634.  
  635. procedure ToggleMaxLines;
  636. { Toggle 43/50-line mode }
  637. begin
  638.   with Vars do
  639.   begin
  640.     Scr.ToggleMaxLinesMode;
  641.     SetCursor(NoCursor);
  642.     SetDisplayAreas;
  643.     DisplayAll;
  644.   end; { with }
  645. end; { ToggleMaxLines }
  646.  
  647. procedure Recalc;
  648. { Recalculate all of the cells }
  649. begin
  650.   Vars.CurrSS^.Update(DisplayYes);
  651. end; { Recalc }
  652.  
  653. procedure ToggleFormulas;
  654. { Toggle formula display on and off }
  655. begin
  656.   with Vars.CurrSS^ do
  657.     ToggleFormulaDisplay;
  658. end; { ToggleFormulas }
  659.  
  660. procedure ToggleAutoCalc;
  661. { Toggle AutoCalc on and off }
  662. begin
  663.   with Vars.CurrSS^ do
  664.   begin
  665.     if AutoCalc then
  666.     begin
  667.       AutoCalc := False;
  668.       DisplayInfo;
  669.     end
  670.     else begin
  671.       AutoCalc := True;
  672.       DisplayInfo;
  673.       Update(DisplayYes);
  674.     end;
  675.   end;
  676. end; { ToggleAutoCalc }
  677.  
  678. procedure FormatCell;
  679. { Format a single cell }
  680. var
  681.   Format : Byte;
  682.   P : CellPos;
  683.   CP : CellPtr;
  684.   Good : Boolean;
  685. begin
  686.   with Vars.CurrSS^ do
  687.   begin
  688.     if not GetFormat(Format) then
  689.       Exit;
  690.     if not FormatHash.Add(CurrPos, CurrPos, Format) then
  691.       Exit;
  692.     CP := CellHash.Search(CurrPos);
  693.     SetChanged(WasChanged);
  694.     OverwriteHash.Delete(CurrPos);
  695.     if CP <> Empty then
  696.       Good := OverwriteHash.Add(CP, CP^.Overwritten(CellHash, FormatHash,
  697.                                 WidthHash, LastPos, MaxCols, GetColWidth,
  698.                                 DisplayFormulas));
  699.     P := CurrPos;
  700.     for P.Col := CurrPos.Col to ScreenBlock.Stop.Col do
  701.       DisplayCell(P);
  702.     DisplayMemory;
  703.   end; { with }
  704. end; { FormatCell }
  705.  
  706. procedure GotoCell;
  707. { Go to a selected cell }
  708. var
  709.   P, OldPos : CellPos;
  710. begin
  711.   with Vars.CurrSS^ do
  712.   begin
  713.     if not GetCellPos(PromptGotoCell, MaxCols, MaxRows, ColSpace,
  714.                       RowNumberSpace, P) then
  715.       Exit;
  716.     if not ScreenBlock.CellInBlock(P) then
  717.     begin
  718.       CurrPos := P;
  719.       SetScreenColStart(CurrPos.Col);
  720.       SetScreenRowStart(CurrPos.Row);
  721.       Display;
  722.     end
  723.     else begin
  724.       OldPos := CurrPos;
  725.       CurrPos := P;
  726.       DisplayCell(OldPos);
  727.       DisplayCell(CurrPos);
  728.     end;
  729.   end; { with }
  730. end; { GotoCell }
  731.  
  732. procedure EditCell;
  733. { Edit the current cell }
  734. begin
  735.   EditInput(0, EditYes);
  736. end; { EditCell }
  737.  
  738. procedure Quit;
  739. { Exit from the program }
  740. begin
  741.   Vars.Stop := True;
  742. end; { Quit }
  743.  
  744. {$F-}
  745.  
  746. procedure ExtendCurrBlock(Redraw : Boolean);
  747. { Extend the current block and redraw any cells that are affected }
  748. var
  749.   OldBlock : Block;
  750. begin
  751.   with Vars.CurrSS^ do
  752.   begin
  753.     if BlockOn then
  754.     begin
  755.       Move(CurrBlock, OldBlock, SizeOf(CurrBlock));
  756.       if CurrBlock.ExtendTo(CurrPos) then
  757.       begin
  758.         if Redraw then
  759.           DisplayBlockDiff(OldBlock, CurrBlock);
  760.       end
  761.       else
  762.         ClearCurrBlock;
  763.     end;
  764.   end; { with }
  765. end; { ExtendCurrBlock }
  766.  
  767. procedure ToggleCurrBlock;
  768. { Turn the block on and off }
  769. begin
  770.   with Vars.CurrSS^ do
  771.   begin
  772.     if not BlockOn then
  773.     begin
  774.       BlockOn := True;
  775.       CurrBlock.Init(CurrPos);
  776.     end
  777.     else
  778.       ClearCurrBlock;
  779.   end; { with }
  780. end; { ToggleCurrBlock }
  781.  
  782. procedure RemoveCell;
  783. { Delete a cell }
  784. var
  785.   P : CellPos;
  786.   Deleted : Boolean;
  787. begin
  788.   with Vars.CurrSS^ do
  789.   begin
  790.     DeleteCell(CurrPos, Deleted);
  791.     if Deleted then
  792.     begin
  793.       SetLastPos(CurrPos);
  794.       SetChanged(WasChanged);
  795.       if AutoCalc then
  796.         Update(DisplayYes);
  797.       P.Row := CurrPos.Row;
  798.       for P.Col := CurrPos.Col to ScreenBlock.Stop.Col do
  799.         DisplayCell(P);
  800.       DisplayMemory;
  801.     end;
  802.   end; { with }
  803. end; { RemoveCell }
  804.  
  805. procedure MoveHome;
  806. { Move to the home position (1, 1) }
  807. var
  808.   OldPos : CellPos;
  809. begin
  810.   with Vars.CurrSS^ do
  811.   begin
  812.     OldPos := CurrPos;
  813.     CurrPos.Col := 1;
  814.     CurrPos.Row := 1;
  815.     if not ScreenBlock.CellInBlock(CurrPos) then
  816.     begin
  817.       ExtendCurrBlock(RedrawNo);
  818.       SetScreenColStart(1);
  819.       SetScreenRowStart(1);
  820.       SetBlankArea;
  821.       Display;
  822.     end
  823.     else begin
  824.       ExtendCurrBlock(RedrawYes);
  825.       DisplayCell(OldPos);
  826.       DisplayCell(CurrPos);
  827.     end;
  828.   end; { with }
  829. end; { MoveHome }
  830.  
  831. procedure MoveEnd;
  832. { Move to the last position used }
  833. var
  834.   OldPos : CellPos;
  835. begin
  836.   with Vars.CurrSS^ do
  837.   begin
  838.     OldPos := CurrPos;
  839.     CurrPos := LastPos;
  840.     if not ScreenBlock.CellInBlock(CurrPos) then
  841.     begin
  842.       ExtendCurrBlock(RedrawNo);
  843.       SetScreenColStop(CurrPos.Col);
  844.       SetScreenRowStop(CurrPos.Row);
  845.       SetBlankArea;
  846.       Display;
  847.     end
  848.     else begin
  849.       ExtendCurrBlock(RedrawYes);
  850.       DisplayCell(OldPos);
  851.       DisplayCell(CurrPos);
  852.     end;
  853.   end; { with }
  854. end; { MoveEnd }
  855.  
  856. procedure MoveUp;
  857. { Move up a row }
  858. var
  859.   OldPos : CellPos;
  860. begin
  861.   with Vars.CurrSS^ do
  862.   begin
  863.     if CurrPos.Row > 1 then
  864.     begin
  865.       OldPos := CurrPos;
  866.       Dec(CurrPos.Row);
  867.       ExtendCurrBlock(RedrawYes);
  868.       if CurrPos.Row < ScreenBlock.Start.Row then
  869.       begin
  870.         DisplayCell(OldPos);
  871.         SetScreenRowStart(CurrPos.Row);
  872.         DisplayRows;
  873.         DisplayArea.Scroll(Down, 1);
  874.         DisplayRow(CurrPos.Row);
  875.       end
  876.       else begin
  877.         DisplayCell(OldPos);
  878.         DisplayCell(CurrPos);
  879.       end;
  880.     end;
  881.   end; { with }
  882. end; { MoveUp }
  883.  
  884. procedure MoveDown;
  885. { Move down a row }
  886. var
  887.   OldPos : CellPos;
  888. begin
  889.   with Vars.CurrSS^ do
  890.   begin
  891.     if CurrPos.Row < MaxRows then
  892.     begin
  893.       OldPos := CurrPos;
  894.       Inc(CurrPos.Row);
  895.       if CurrPos.Row > ScreenBlock.Stop.Row then
  896.       begin
  897.         ExtendCurrBlock(RedrawNo);
  898.         DisplayCell(OldPos);
  899.         SetScreenRowStop(CurrPos.Row);
  900.         DisplayRows;
  901.         DisplayArea.Scroll(Up, 1);
  902.         DisplayRow(CurrPos.Row);
  903.       end
  904.       else begin
  905.         ExtendCurrBlock(RedrawYes);
  906.         DisplayCell(OldPos);
  907.         DisplayCell(CurrPos);
  908.       end;
  909.     end;
  910.   end; { with }
  911. end; { MoveDown }
  912.  
  913. procedure MovePgUp;
  914. { Move up a page }
  915. var
  916.   OldPos : CellPos;
  917. begin
  918.   with Vars.CurrSS^ do
  919.   begin
  920.     if CurrPos.Row > 1 then
  921.     begin
  922.       OldPos := CurrPos;
  923.       CurrPos.Row := Max(1, Longint(CurrPos.Row) - TotalRows);
  924.       ExtendCurrBlock(RedrawNo);
  925.       if CurrPos.Row < ScreenBlock.Start.Row then
  926.       begin
  927.         SetScreenRowStart(CurrPos.Row);
  928.         DisplayRows;
  929.         DisplayAllCells;
  930.       end
  931.       else begin
  932.         DisplayCell(OldPos);
  933.         DisplayCell(CurrPos);
  934.       end;
  935.     end;
  936.   end; { with }
  937. end; { MovePgUp }
  938.  
  939. procedure MovePgDn;
  940. { Move down a page }
  941. var
  942.   OldPos : CellPos;
  943. begin
  944.   with Vars.CurrSS^ do
  945.   begin
  946.     if CurrPos.Row < MaxRows then
  947.     begin
  948.       OldPos := CurrPos;
  949.       CurrPos.Row := Min(MaxRows, Longint(CurrPos.Row) + TotalRows);
  950.       ExtendCurrBlock(RedrawNo);
  951.       if CurrPos.Row > ScreenBlock.Start.Row then
  952.       begin
  953.         SetScreenRowStart(CurrPos.Row);
  954.         DisplayRows;
  955.         DisplayAllCells;
  956.       end
  957.       else begin
  958.         DisplayCell(OldPos);
  959.         DisplayCell(CurrPos);
  960.       end;
  961.     end;
  962.   end; { with }
  963. end; { MovePgDn }
  964.  
  965. procedure MoveLeft;
  966. { Move left a column }
  967. var
  968.   C : Word;
  969.   OldPos : CellPos;
  970.   OldSCol : Word;
  971. begin
  972.   with Vars.CurrSS^ do
  973.   begin
  974.     if CurrPos.Col > 1 then
  975.     begin
  976.       OldPos := CurrPos;
  977.       Dec(CurrPos.Col);
  978.       ExtendCurrBlock(RedrawYes);
  979.       if CurrPos.Col < ScreenBlock.Start.Col then
  980.       begin
  981.         OldSCol := ScreenBlock.Start.Col;
  982.         C := GetColStart(1);
  983.         DisplayCell(OldPos);
  984.         SetScreenColStart(CurrPos.Col);
  985.         SetBlankArea;
  986.         DisplayCols;
  987.         DisplayArea.Scroll(Right,
  988.           GetColStart(OldSCol - ScreenBlock.Start.Col) - GetColStart(0));
  989.         if not NoBlankArea then
  990.           BlankArea.Clear;
  991.         for C := ScreenBlock.Start.Col to CurrPos.Col do
  992.           DisplayCol(C);
  993.       end
  994.       else begin
  995.         DisplayCell(OldPos);
  996.         DisplayCell(CurrPos);
  997.       end;
  998.     end;
  999.   end; { with }
  1000. end; { MoveLeft }
  1001.  
  1002. procedure MoveRight;
  1003. { Move right a column }
  1004. var
  1005.   C : Word;
  1006.   OldPos : CellPos;
  1007.   SaveColStart : array[0..79] of Byte;
  1008.   OldSCol : Word;
  1009. begin
  1010.   with Vars.CurrSS^ do
  1011.   begin
  1012.     if CurrPos.Col < MaxCols then
  1013.     begin
  1014.       OldPos := CurrPos;
  1015.       Inc(CurrPos.Col);
  1016.       if CurrPos.Col > ScreenBlock.Stop.Col then
  1017.       begin
  1018.         ExtendCurrBlock(RedrawNo);
  1019.         for C := 0 to Pred(MaxScreenCols) do
  1020.           SaveColStart[C] := GetColStart(C);
  1021.         OldSCol := ScreenBlock.Start.Col;
  1022.         DisplayCell(OldPos);
  1023.         C := ColWidth(ScreenBlock.Start.Col);
  1024.         SetScreenColStop(CurrPos.Col);
  1025.         SetBlankArea;
  1026.         DisplayCols;
  1027.         DisplayArea.Scroll(Left,
  1028.           SaveColStart[ScreenBlock.Start.Col - OldSCol] - ColStart^[0]);
  1029.         if not NoBlankArea then
  1030.           BlankArea.Clear;
  1031.         for C := CurrPos.Col to ScreenBlock.Stop.Col do
  1032.           DisplayCol(C);
  1033.       end
  1034.       else begin
  1035.         ExtendCurrBlock(RedrawYes);
  1036.         DisplayCell(OldPos);
  1037.         DisplayCell(CurrPos);
  1038.       end;
  1039.     end;
  1040.   end; { with }
  1041. end; { MoveRight }
  1042.  
  1043. procedure MovePgLeft;
  1044. { Move left a page }
  1045. var
  1046.   OldPos : CellPos;
  1047. begin
  1048.   with Vars.CurrSS^ do
  1049.   begin
  1050.     if CurrPos.Col > 1 then
  1051.     begin
  1052.       OldPos := CurrPos;
  1053.       CurrPos.Col := Max(1, Pred(ScreenBlock.Start.Col));
  1054.       ExtendCurrBlock(RedrawNo);
  1055.       if CurrPos.Col < ScreenBlock.Start.Col then
  1056.       begin
  1057.         SetScreenColStop(CurrPos.Col);
  1058.         SetBlankArea;
  1059.         DisplayCols;
  1060.         if not NoBlankArea then
  1061.           BlankArea.Clear;
  1062.         DisplayAllCells;
  1063.       end
  1064.       else begin
  1065.         DisplayCell(OldPos);
  1066.         DisplayCell(CurrPos);
  1067.       end;
  1068.     end;
  1069.   end; { with }
  1070. end; { MovePgLeft }
  1071.  
  1072. procedure MovePgRight;
  1073. { Move right a page }
  1074. var
  1075.   OldPos : CellPos;
  1076. begin
  1077.   with Vars.CurrSS^ do
  1078.   begin
  1079.     if CurrPos.Col < MaxCols then
  1080.     begin
  1081.       OldPos := CurrPos;
  1082.       CurrPos.Col := Min(MaxCols, Succ(ScreenBlock.Stop.Col));
  1083.       ExtendCurrBlock(RedrawNo);
  1084.       if CurrPos.Col > ScreenBlock.Start.Col then
  1085.       begin
  1086.         SetScreenColStart(CurrPos.Col);
  1087.         SetBlankArea;
  1088.         DisplayCols;
  1089.         if not NoBlankArea then
  1090.           BlankArea.Clear;
  1091.         DisplayAllCells;
  1092.       end
  1093.       else begin
  1094.         DisplayCell(OldPos);
  1095.         DisplayCell(CurrPos);
  1096.       end;
  1097.     end;
  1098.   end; { with }
  1099. end; { MovePgRight }
  1100.  
  1101. procedure HandleInput(Ch : Word);
  1102. { Process the initial input from the keyboard }
  1103. begin
  1104.   EditInput(Ch, EditNo);
  1105. end; { HandleInput }
  1106.  
  1107. procedure ProgramObject.GetCommands;
  1108. { Read the keyboard and process the next command }
  1109. var
  1110.   Ch : Word;
  1111. begin 
  1112.   repeat
  1113.     CurrSS^.DisplayCellData;
  1114.     ClearInputBuffer;
  1115.     Ch := GetKey;
  1116.     case Ch of
  1117.       F2 : SaveCurrSpreadsheet;
  1118.       AltF2 : NameSaveSpreadsheet;
  1119.       F3 : ReplaceSpreadsheet;
  1120.       AltF3 : NewSpreadsheet;
  1121.       F4 : DeleteSheet;
  1122.       F6 : NextSpreadsheet;
  1123.       F7 : ToggleFormulas;
  1124.       F8 : ToggleAutoCalc;
  1125.       F9 : Recalc;
  1126.       F10 : MainMenu.RunMenu;
  1127.       AltX : Stop := True;
  1128.       InsKey : ToggleCurrBlock;
  1129.       DelKey : RemoveCell;
  1130.       HomeKey : MoveHome;
  1131.       EndKey : MoveEnd;
  1132.       UpKey : MoveUp;
  1133.       DownKey : MoveDown;
  1134.       LeftKey : MoveLeft;
  1135.       RightKey : MoveRight;
  1136.       PgUpKey : MovePgUp;
  1137.       PgDnKey : MovePgDn;
  1138.       CtrlLeftKey : MovePgLeft;
  1139.       CtrlRightKey : MovePgRight;
  1140.       Ord(' ')..Ord('~') : HandleInput(Ch);
  1141.     end;
  1142.   until Stop;
  1143. end; { ProgramObject.GetCommands }
  1144.  
  1145. procedure ProgramObject.SetDisplayAreas;
  1146. { Set the display areas of the various spreadsheets }
  1147. var
  1148.   S : SpreadsheetPtr;
  1149.   Total, StartRow, Amt : Word;
  1150. begin
  1151.   S := SSData;
  1152.   StartRow := Succ(EmptyRowsAtTop);
  1153.   Amt := (Scr.CurrRows - EmptyRowsAtTop - EmptyRowsAtBottom) div
  1154.          TotalSheets;
  1155.   Total := 1;
  1156.   repeat
  1157.     if S^.Next = nil then
  1158.       Amt := Succ(Scr.CurrRows - EmptyRowsAtBottom - StartRow);
  1159.     S^.SetAreas(Total, 1, StartRow, Scr.CurrCols, Pred(StartRow + Amt));
  1160.     Inc(StartRow, Amt);
  1161.     S := S^.Next;
  1162.     Inc(Total);
  1163.   until S = nil;
  1164. end; { ProgramObject.SetDisplayAreas }
  1165.  
  1166. procedure ProgramObject.DisplayAll;
  1167. { Display all of the spreadsheets }
  1168. var
  1169.   S : SpreadsheetPtr;
  1170. begin
  1171.   TextAttr := Colors.BlankColor;
  1172.   ClrScr;
  1173.   WriteColor(TitleString, Colors.TitleColor);
  1174.   Scr.PrintHelpLine(HelpLine);
  1175.   WriteXY(MemoryString, Scr.CurrCols - Length(MemoryString) - 5, 1,
  1176.           Colors.PromptColor);
  1177.   S := SSData;
  1178.   repeat
  1179.     S^.Display;
  1180.     S := S^.Next;
  1181.   until S = nil;
  1182. end; { ProgramObject.DisplayAll }
  1183.  
  1184. function ProgramObject.AddSheet(Name : PathStr) : Boolean;
  1185. { Add a new spreadsheet }
  1186. var
  1187.   A, S : SpreadsheetPtr;
  1188.   Good, AllocatingNext : Boolean;
  1189. begin
  1190.   AddSheet := False;
  1191.   if TotalSheets = MaxSpreadsheets then
  1192.     Exit;
  1193.   S := SSData;
  1194.   while (S <> nil) and (S^.Next <> nil) do
  1195.     S := S^.Next;
  1196.   if SSData <> nil then
  1197.   begin
  1198.     A := S;
  1199.     New(S^.Next);
  1200.     S := S^.Next;
  1201.     AllocatingNext := True;
  1202.   end
  1203.   else begin
  1204.     New(S);
  1205.     AllocatingNext := False;
  1206.   end;
  1207.   if S = nil then
  1208.     Exit;
  1209.   if Name = '' then
  1210.     Good := S^.Init(0, DefaultMaxCols, DefaultMaxRows,
  1211.                     DefaultMaxDecimalPlaces, DefaultDefaultDecimalPlaces,
  1212.                     DefaultDefaultColWidth)
  1213.   else
  1214.     Good := S^.FromFile(Name);
  1215.   if not Good then
  1216.   begin
  1217.     Dispose(S);
  1218.     if AllocatingNext then
  1219.       A^.Next := nil;
  1220.     Exit;
  1221.   end;
  1222.   if SSData = nil then
  1223.     SSData := S;
  1224.   if CurrSS <> nil then
  1225.     CurrSS^.Current := False;
  1226.   CurrSS := S;
  1227.   Inc(TotalSheets);
  1228.   S^.Next := nil;
  1229.   AddSheet := True;
  1230. end; { ProgramObject.AddSheet }
  1231.  
  1232. procedure ProgramObject.DeleteSheet;
  1233. { Delete a spreadsheet }
  1234. var
  1235.   S : SpreadsheetPtr;
  1236. begin
  1237.   if TotalSheets > 1 then
  1238.   begin
  1239.     S := SSData;
  1240.     if S = CurrSS then
  1241.       SSData := S^.Next
  1242.     else begin
  1243.       while S^.Next <> CurrSS do
  1244.         S := S^.Next;
  1245.       S^.Next := S^.Next^.Next;
  1246.     end;
  1247.   end;
  1248.   with CurrSS^ do
  1249.   begin
  1250.     CheckForSave;
  1251.     Done;
  1252.   end; { with }
  1253.   if TotalSheets > 1 then
  1254.   begin
  1255.     FreeMem(CurrSS, SizeOf(Spreadsheet));
  1256.     Dec(TotalSheets);
  1257.     CurrSS := SSData;
  1258.   end
  1259.   else
  1260.     CurrSS^.Init(0, DefaultMaxCols, DefaultMaxRows,
  1261.                  DefaultMaxDecimalPlaces, DefaultDefaultDecimalPlaces,
  1262.                  DefaultDefaultColWidth);
  1263.   SetDisplayAreas;
  1264.   DisplayAll;
  1265.   with CurrSS^ do
  1266.   begin
  1267.     MakeCurrent;
  1268.     DisplayCell(CurrPos);
  1269.   end; { with }
  1270. end; { ProgramObject.DeleteSheet }
  1271.  
  1272. procedure InitMenus;
  1273. { Initialize the menu items }
  1274. var
  1275.   Good : Boolean;
  1276.   P : Word;
  1277. begin
  1278.   with Vars do
  1279.   begin
  1280.     with MainMenu do
  1281.     begin
  1282.       Init(MainMenuString, nil);
  1283.       Good := AddItemMenu(@SpreadsheetMenu);
  1284.       Good := AddItemMenu(@BlockMenu);
  1285.       Good := AddItemMenu(@ColumnMenu);
  1286.       Good := AddItemMenu(@RowMenu);
  1287.       Good := AddItemProc(FormatCell);
  1288.       Good := AddItemProc(GotoCell);
  1289.       Good := AddItemProc(EditCell);
  1290.       Good := AddItemMenu(@UtilityMenu);
  1291.       Good := AddItemProc(Quit);
  1292.     end; { with }
  1293.     with SpreadsheetMenu do
  1294.     begin
  1295.       Init(SpreadsheetMenuString, @MainMenu);
  1296.       Good := AddItemProc(Replacespreadsheet);
  1297.       Good := AddItemProc(SaveCurrSpreadsheet);
  1298.       Good := AddItemProc(ZapSpreadsheet);
  1299.       Good := AddItemProc(NameSaveSpreadsheet);
  1300.       Good := AddItemMenu(@OpenMenu);
  1301.       Good := AddItemProc(CloseSpreadsheet);
  1302.       Good := AddItemProc(NextSpreadsheet);
  1303.       Good := AddItemProc(PrintSpreadsheet);
  1304.     end; { with }
  1305.     with OpenMenu do
  1306.     begin
  1307.       Init(OpenMenuString, @SpreadsheetMenu);
  1308.       Good := AddItemProc(NewSpreadsheet);
  1309.       Good := AddItemProc(NewBlankSpreadsheet);
  1310.     end; { with }
  1311.     with BlockMenu do
  1312.     begin
  1313.       Init(BlockMenuString, @MainMenu);
  1314.       Good := AddItemProc(CopyBlock);
  1315.       Good := AddItemProc(DeleteBlock);
  1316.       Good := AddItemProc(FormatBlock);
  1317.       Good := AddItemProc(FormatDefault);
  1318.     end; { with }
  1319.     with ColumnMenu do
  1320.     begin
  1321.       Init(ColumnMenuString, @MainMenu);
  1322.       Good := AddItemProc(ColInsert);
  1323.       Good := AddItemProc(ColDelete);
  1324.       Good := AddItemProc(ChangeColWidth);
  1325.     end; { with }
  1326.     with RowMenu do
  1327.     begin
  1328.       Init(RowMenuString, @MainMenu);
  1329.       Good := AddItemProc(RowInsert);
  1330.       Good := AddItemProc(RowDelete);
  1331.     end; { with }
  1332.     with UtilityMenu do
  1333.     begin
  1334.       if Scr.VideoType >= MCGA then
  1335.       begin
  1336.         Init(UtilityMenuString1, @MainMenu);
  1337.         Good := AddItemProc(ToggleMaxLines);
  1338.       end
  1339.       else
  1340.         Init(UtilityMenuString2, @MainMenu);
  1341.       Good := AddItemProc(Recalc);
  1342.       Good := AddItemProc(ToggleFormulas);
  1343.       Good := AddItemProc(ToggleAutoCalc);
  1344.     end; { with }
  1345.   end; { with }
  1346. end; { InitMenus }
  1347.  
  1348. procedure Run;
  1349. { The main part of the program - it sets up the spreadsheets, reads commands,
  1350.   and them releases all of the memory that it used }
  1351. begin
  1352.   SetCursor(NoCursor);
  1353.   with Vars do
  1354.   begin
  1355.     Init;
  1356.     GetCommands;
  1357.     Done;
  1358.   end;
  1359. end; { Run }
  1360.  
  1361. begin
  1362.   CheckBreak := False;
  1363.   FreeMin := FreeListItems shl 3;
  1364.   HeapError := @RunHeapError;
  1365. end.
  1366.