home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 9 / 09.iso / l / l044 / 4.ddi / TCALC.ZIP / TCRUN.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1990-10-23  |  32.4 KB  |  1,367 lines

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