1722 lines
46 KiB
Plaintext
1722 lines
46 KiB
Plaintext
|
||
{ Copyright (c) 1989 by Borland International, Inc. }
|
||
|
||
unit TCSheet;
|
||
{ Turbo Pascal 5.5 object-oriented example spreadsheet routines.
|
||
This unit is used by TCALC.PAS.
|
||
See TCALC.DOC for an more information about this example.
|
||
}
|
||
|
||
{$N+,S-}
|
||
|
||
interface
|
||
|
||
uses Crt, Dos, Objects, TCUtil, TCInput, TCScreen, TCLStr, TCHash, TCCell,
|
||
TCCellSp, TCParser;
|
||
|
||
const
|
||
DefaultMaxCols = 65535;
|
||
DefaultMaxRows = 65535;
|
||
DefaultMaxDecimalPlaces = 8;
|
||
DefaultDefaultDecimalPlaces = 4;
|
||
DefaultDefaultColWidth = 10;
|
||
EmptyRowsAtTop = 1;
|
||
EmptyRowsAtBottom = 2;
|
||
MinColWidth = 3;
|
||
CurrentChar = #4;
|
||
ChangedChar = '*';
|
||
PrintNormalCols = 80;
|
||
PrintCompressedCols = 132;
|
||
PrintRows = 66;
|
||
PrintTopMargin = 1;
|
||
PrintBottomMargin = 1;
|
||
PrinterCompressChar = #15;
|
||
EditYes = True;
|
||
EditNo = False;
|
||
DisplayYes = True;
|
||
DisplayNo = False;
|
||
WasChanged = True;
|
||
NotChanged = False;
|
||
AutoCalcLetter = 'A';
|
||
FormulaDisplayLetter = 'F';
|
||
MemoryString = 'Memory: ';
|
||
FileHeader = 'TurboCalc Spreadsheet'^Z;
|
||
ErrorString = 'ERROR';
|
||
TempFileName = 'TEMP.TMP'; { Temporary file used for rehashing }
|
||
PrinterName = 'PRN';
|
||
PromptFileSave = 'File to save';
|
||
PromptFilePrint = 'File to print to (ENTER = Printer)';
|
||
PromptOverwriteFile = 'The file exists. Overwrite it';
|
||
PromptCompressPrint = 'Compress the printing';
|
||
PromptBorderPrint = 'Print the borders';
|
||
PromptColumnWidth = 'Column to change';
|
||
PromptNewWidth = 'New width';
|
||
PromptColumnDelete = 'Column to delete';
|
||
PromptColumnInsert = 'Insert new column before column';
|
||
PromptRowDelete = 'Row to delete';
|
||
PromptRowInsert = 'Insert new row before row';
|
||
PromptSaveYN = 'Save spreadsheet';
|
||
ErrNoOpen = 'Cannot open file';
|
||
ErrDiskFull = 'Disk full';
|
||
ErrPrinterError = 'Printer error';
|
||
ErrNotSpreadsheet = 'Not a TurboCalc spreadsheet file';
|
||
MsgRecalc = 'Recalculating cell values';
|
||
MsgSave = 'Saving spreadsheet';
|
||
MsgLoad = 'Loading spreadsheet';
|
||
MsgBlockDelete = 'Deleting block';
|
||
|
||
type
|
||
ColStartArray = array[0..ScreenCols] of Byte;
|
||
ColStartPtr = ^ColStartArray;
|
||
SpreadsheetPtr = ^Spreadsheet;
|
||
Spreadsheet = object
|
||
Number : Byte;
|
||
MaxRows : Word;
|
||
MaxCols : Word;
|
||
MaxDecimalPlaces : Byte;
|
||
MaxColWidth : Byte;
|
||
MaxScreenCols : Byte;
|
||
DefaultColWidth : Byte;
|
||
DefaultDecimalPlaces : Byte;
|
||
RowNumberSpace : Byte;
|
||
ColSpace : Byte;
|
||
Current : Boolean;
|
||
Changed : Boolean;
|
||
CurrPos : CellPos;
|
||
LastPos : CellPos;
|
||
ScreenBlock : Block;
|
||
CurrBlock : Block;
|
||
BlockOn : Boolean;
|
||
FileName : PathStr;
|
||
TotalRows : ScreenRowRange;
|
||
DisplayArea : ScreenArea;
|
||
ColArea : ScreenArea;
|
||
RowArea : ScreenArea;
|
||
InfoArea : ScreenArea;
|
||
DataArea : ScreenArea;
|
||
ContentsArea : ScreenArea;
|
||
BlankArea : ScreenArea;
|
||
NoBlankArea : Boolean;
|
||
ColStart : ColStartPtr;
|
||
DisplayFormulas : Boolean;
|
||
AutoCalc : Boolean;
|
||
CellHash : CellHashTable;
|
||
OverwriteHash : OverwriteHashTable;
|
||
WidthHash : WidthHashTable;
|
||
FormatHash : FormatHashTable;
|
||
Next : SpreadsheetPtr;
|
||
constructor Init(InitCells : Longint; InitMaxCols, InitMaxRows : Word;
|
||
InitMaxDecimalPlaces, InitDefaultDecimalPlaces : Byte;
|
||
InitDefaultColWidth : Byte);
|
||
destructor Done;
|
||
function GetColStart(Col : Word) : Byte;
|
||
procedure SetAreas(NewNumber : Word; X1 : ScreenColRange;
|
||
Y1 : ScreenRowRange; X2 : ScreenColRange;
|
||
Y2 : ScreenRowRange);
|
||
procedure DisplayCols;
|
||
procedure DisplayRows;
|
||
procedure DisplayInfo;
|
||
procedure DisplayAllCells;
|
||
procedure Display;
|
||
procedure DisplayCell(P : CellPos);
|
||
procedure DisplayCellData;
|
||
procedure DisplayCellBlock(C1 : Word; R1 : Word; C2 : Word;
|
||
R2 : Word);
|
||
procedure DisplayBlock(B : Block);
|
||
procedure DisplayBlockDiff(B1, B2 : Block);
|
||
procedure DisplayCol(Col : Word);
|
||
procedure DisplayRow(Row : Word);
|
||
procedure DisplayMemory;
|
||
procedure DisplayFileName;
|
||
procedure SetChanged(IsChanged : Boolean);
|
||
procedure MakeCurrent;
|
||
procedure MakeNotCurrent;
|
||
procedure Update(UDisplay : Boolean);
|
||
procedure ToggleFormulaDisplay;
|
||
procedure SetScreenColStart(NewCol : Word);
|
||
procedure SetScreenColStop(NewCol: Word);
|
||
procedure SetScreenRowStart(NewRow : Word);
|
||
procedure SetScreenRowStop(NewRow : Word);
|
||
procedure FindScreenColStart;
|
||
procedure FindScreenColStop;
|
||
procedure FindScreenRowStart;
|
||
procedure FindScreenRowStop;
|
||
procedure SetBlankArea;
|
||
function AddCell(CellType : CellTypes; P : CellPos; E : Boolean;
|
||
V : Extended; I : LStringPtr) : Boolean;
|
||
procedure DeleteCell(P : CellPos; var Deleted : Boolean);
|
||
procedure DeleteBlock(B : Block; var Deleted : Boolean);
|
||
function CellToFString(P : CellPos; var Color : Byte) : String;
|
||
procedure SetLastPos(DPos : CellPos);
|
||
function GetCurrCol : Word;
|
||
function GetCurrRow : Word;
|
||
function ColToX(Col : Word) : Byte;
|
||
function RowToY(Row : Word) : Byte;
|
||
function ColWidth(Col : Word) : Byte;
|
||
function SameCellPos(P1, P2 : CellPos) : Boolean;
|
||
procedure FixOverwrite;
|
||
function FromFile(Name : PathStr) : Boolean;
|
||
procedure ToFile(Name : PathStr);
|
||
procedure CheckForSave;
|
||
procedure ChangeWidth;
|
||
function CellHashStart(TotalCells : Longint) : BucketRange;
|
||
function WidthHashStart(TotalCells : Longint) : BucketRange;
|
||
function OverwriteHashStart(TotalCells : Longint) : BucketRange;
|
||
procedure Print;
|
||
procedure DeleteColumn;
|
||
procedure InsertColumn;
|
||
procedure DeleteRow;
|
||
procedure InsertRow;
|
||
end;
|
||
|
||
function GetColWidth(var WHash : WidthHashTable; C : Word) : Byte;
|
||
|
||
implementation
|
||
|
||
function GetColWidth(var WHash : WidthHashTable; C : Word) : Byte;
|
||
{ Returns the width of a column }
|
||
var
|
||
W : Word;
|
||
begin
|
||
W := WHash.Search(C);
|
||
if W = 0 then
|
||
GetColWidth := WHash.GetDefaultColWidth
|
||
else
|
||
GetColWidth := W;
|
||
end; { GetColWidth }
|
||
|
||
constructor Spreadsheet.Init(InitCells : Longint; InitMaxCols,
|
||
InitMaxRows : Word; InitMaxDecimalPlaces,
|
||
InitDefaultDecimalPlaces : Byte;
|
||
InitDefaultColWidth : Byte);
|
||
{ Sets up a new spreadsheet }
|
||
begin
|
||
if not CellHash.Init(CellHashStart(InitCells)) then
|
||
Fail;
|
||
if not WidthHash.Init(WidthHashStart(InitCells), InitDefaultColWidth) then
|
||
begin
|
||
CellHash.Done;
|
||
Fail;
|
||
end;
|
||
if not OverwriteHash.Init(OverwriteHashStart(InitCells)) then
|
||
begin
|
||
CellHash.Done;
|
||
WidthHash.Done;
|
||
Fail;
|
||
end;
|
||
if not FormatHash.Init then
|
||
begin
|
||
CellHash.Done;
|
||
WidthHash.Done;
|
||
OverwriteHash.Done;
|
||
Fail;
|
||
end;
|
||
MaxCols := InitMaxCols;
|
||
MaxRows := InitMaxRows;
|
||
RowNumberSpace := Ord(MaxRows >= 10000) + Ord(MaxRows >= 1000) +
|
||
Ord(MaxRows >= 100) + Ord(MaxRows >= 10) + 2;
|
||
MaxColWidth := ScreenCols - RowNumberSpace;
|
||
MaxScreenCols := MaxColWidth div MinColWidth;
|
||
GetMem(ColStart, MaxScreenCols);
|
||
if ColStart = nil then
|
||
begin
|
||
CellHash.Done;
|
||
WidthHash.Done;
|
||
OverwriteHash.Done;
|
||
FormatHash.Done;
|
||
Fail;
|
||
end;
|
||
CurrPos.Col := 1;
|
||
CurrPos.Row := 1;
|
||
LastPos := CurrPos;
|
||
BlockOn := False;
|
||
FileName := '';
|
||
DisplayFormulas := False;
|
||
AutoCalc := False;
|
||
Current := False;
|
||
Changed := False;
|
||
ScreenBlock.Start.Col := 1;
|
||
ScreenBlock.Start.Row := 1;
|
||
ColSpace := Succ(Ord(MaxCols >= 18279) + Ord(MaxCols >= 703) +
|
||
Ord(MaxCols >= 27));
|
||
MaxDecimalPlaces := InitMaxDecimalPlaces;
|
||
DefaultColWidth := InitDefaultColWidth;
|
||
DefaultDecimalPlaces := InitDefaultDecimalPlaces;
|
||
end; { Spreadsheet.Init }
|
||
|
||
destructor Spreadsheet.Done;
|
||
{ Removes a spreadsheet from memory }
|
||
begin
|
||
CellHash.Done;
|
||
WidthHash.Done;
|
||
OverwriteHash.Done;
|
||
FormatHash.Done;
|
||
FreeMem(ColStart, MaxScreenCols);
|
||
end; { Spreadsheet.Done }
|
||
|
||
function Spreadsheet.GetColStart(Col : Word) : Byte;
|
||
begin
|
||
GetColStart := ColStart^[Col];
|
||
end; { Spreadsheet.GetColStart }
|
||
|
||
procedure Spreadsheet.SetAreas(NewNumber : Word; X1 : ScreenColRange;
|
||
Y1 : ScreenRowRange; X2 : ScreenColRange;
|
||
Y2 : ScreenRowRange);
|
||
{ Sets up a spreadsheet's display areas }
|
||
begin
|
||
Number := NewNumber;
|
||
TotalRows := Y2 - Y1 - 2;
|
||
ColArea.Init(X1 + RowNumberSpace, Y1, X2, Y1, Colors.ColColor);
|
||
RowArea.Init(X1, Succ(Y1), Pred(X1 + RowNumberSpace), Y2 - 2,
|
||
Colors.RowColor);
|
||
InfoArea.Init(X1, Y1, Pred(X1 + RowNumberSpace), Y1, Colors.InfoColor);
|
||
DisplayArea.Init(X1 + RowNumberSpace, Succ(Y1), X2, Y2 - 2,
|
||
Colors.BlankColor);
|
||
DataArea.Init(X1, Pred(Y2), X2, Pred(Y2), Colors.BlankColor);
|
||
ContentsArea.Init(X1, Y2, X2, Y2, Colors.ContentsColor);
|
||
SetScreenColStart(ScreenBlock.Start.Col);
|
||
SetScreenRowStart(ScreenBlock.Start.Row);
|
||
SetBlankArea;
|
||
end; { Spreadsheet.SetAreas }
|
||
|
||
procedure Spreadsheet.DisplayCols;
|
||
{ Shows the column headings }
|
||
var
|
||
C : Word;
|
||
begin
|
||
ColArea.Clear;
|
||
with ScreenBlock do
|
||
begin
|
||
for C := Start.Col to Stop.Col do
|
||
WriteXY(CenterStr(ColToString(C), ColWidth(C)),
|
||
ColStart^[C - Start.Col], ColArea.UpperLeft.Row,
|
||
Colors.ColColor);
|
||
end; { with }
|
||
end; { Spreadsheet.DisplayCols }
|
||
|
||
procedure Spreadsheet.DisplayRows;
|
||
{ Shows the row headings }
|
||
var
|
||
R : Word;
|
||
begin
|
||
RowArea.Clear;
|
||
with ScreenBlock do
|
||
begin
|
||
for R := Start.Row to Stop.Row do
|
||
with RowArea do
|
||
WriteXY(LeftJustStr(RowToString(R), RowNumberSpace),
|
||
UpperLeft.Col, R - Start.Row + UpperLeft.Row,
|
||
Colors.RowColor);
|
||
end; { with }
|
||
end; { Spreadsheet.DisplayRows }
|
||
|
||
procedure Spreadsheet.DisplayInfo;
|
||
{ Shows the spreadsheet number, current dot, and state of AutoCalc and
|
||
formula display }
|
||
begin
|
||
InfoArea.Clear;
|
||
with InfoArea do
|
||
WriteXY(NumToString(Number), UpperLeft.Col, UpperLeft.Row,
|
||
Colors.InfoColor);
|
||
if Current then
|
||
Write(CurrentChar)
|
||
else
|
||
Write(' ');
|
||
if AutoCalc then
|
||
Write(AutoCalcLetter)
|
||
else
|
||
Write(' ');
|
||
if DisplayFormulas then
|
||
Write(FormulaDisplayLetter)
|
||
else
|
||
Write(' ');
|
||
end; { Spreadsheet.DisplayRows }
|
||
|
||
procedure Spreadsheet.DisplayAllCells;
|
||
{ Displays all of the cells on the screen }
|
||
begin
|
||
DisplayArea.Clear;
|
||
DisplayBlock(ScreenBlock);
|
||
end; { Spreadsheet.DisplayAllCells }
|
||
|
||
procedure Spreadsheet.DisplayCell(P : CellPos);
|
||
{ Displays a single cell }
|
||
var
|
||
S : String[ScreenCols];
|
||
Color : Byte;
|
||
begin
|
||
S := CellToFString(P, Color);
|
||
WriteXY(S, ColToX(P.Col), RowToY(P.Row), Color);
|
||
end; { Spreadsheet.DisplayCell }
|
||
|
||
procedure Spreadsheet.DisplayCellData;
|
||
{ Displays information about a cell - its type and its contents }
|
||
var
|
||
CP : CellPtr;
|
||
begin
|
||
CP := CellHash.Search(CurrPos);
|
||
with DataArea do
|
||
WriteXY(LeftJustStr(ColToString(CurrPos.Col) +
|
||
RowToString(CurrPos.Row) + ' ' + CP^.Name, 19),
|
||
UpperLeft.Col, UpperLeft.Row, Colors.CellDataColor);
|
||
with ContentsArea do
|
||
begin
|
||
Clear;
|
||
WriteXY(LeftJustStr(CP^.DisplayString(DisplayFormulas,
|
||
MaxDecimalPlaces), Scr.CurrCols), UpperLeft.Col,
|
||
UpperLeft.Row, Colors.ContentsColor);
|
||
end; { with }
|
||
end; { Spreadsheet.DisplayCellData }
|
||
|
||
procedure Spreadsheet.DisplayCellBlock(C1 : Word; R1 : Word;
|
||
C2 : Word; R2 : Word);
|
||
{ Displays all cells within a range of rows and columns }
|
||
var
|
||
P : CellPos;
|
||
begin
|
||
with ScreenBlock do
|
||
begin
|
||
for P.Row := Max(R1, Start.Row) to Min(R2, Stop.Row) do
|
||
begin
|
||
for P.Col := Max(C1, Start.Col) to Min(C2, Stop.Col) do
|
||
DisplayCell(P);
|
||
end;
|
||
end; { with }
|
||
end; { Spreadsheet.DisplayCellBlock }
|
||
|
||
procedure Spreadsheet.DisplayBlock(B : Block);
|
||
{ Displays all cells within a certain block }
|
||
begin
|
||
with B do
|
||
DisplayCellBlock(Start.Col, Start.Row, Stop.Col, Stop.Row);
|
||
end; { Spreadsheet.DisplayBlock }
|
||
|
||
procedure Spreadsheet.DisplayBlockDiff(B1, B2 : Block);
|
||
{ When a block is extended, this will update the screen to show the new
|
||
block }
|
||
var
|
||
B : Block;
|
||
DisplayMiddle : Boolean;
|
||
begin
|
||
if Compare(B1, B2, SizeOf(Block)) then
|
||
Exit;
|
||
with B do
|
||
begin
|
||
DisplayMiddle := False;
|
||
if B1.Stop.Col <> B2.Stop.Col then
|
||
begin
|
||
B.Start.Row := B1.Start.Row;
|
||
B.Start.Col := Min(Succ(B1.Stop.Col), Succ(B2.Stop.Col));
|
||
B.Stop.Row := Min(B1.Stop.Row, B2.Stop.Row);
|
||
B.Stop.Col := Max(B1.Stop.Col, B2.Stop.Col);
|
||
DisplayBlock(B);
|
||
DisplayMiddle := True;
|
||
end;
|
||
if B1.Stop.Row <> B2.Stop.Row then
|
||
begin
|
||
B.Start.Row := Min(Succ(B1.Stop.Row), Succ(B2.Stop.Row));
|
||
B.Start.Col := B1.Start.Col;
|
||
B.Stop.Row := Max(B1.Stop.Row, B2.Stop.Row);
|
||
B.Stop.Col := Min(B1.Stop.Col, B2.Stop.Col);
|
||
DisplayBlock(B);
|
||
DisplayMiddle := True;
|
||
end;
|
||
if DisplayMiddle then
|
||
begin
|
||
B.Start.Row := Min(Succ(B1.Stop.Row), Succ(B2.Stop.Row));
|
||
B.Start.Col := Min(Succ(B1.Stop.Col), Succ(B2.Stop.Col));
|
||
B.Stop.Row := Max(B1.Stop.Row, B2.Stop.Row);
|
||
B.Stop.Col := Max(B1.Stop.Col, B2.Stop.Col);
|
||
DisplayBlock(B);
|
||
end;
|
||
end; { with }
|
||
end; { Spreadsheet.DisplayBlockDiff }
|
||
|
||
procedure Spreadsheet.DisplayCol(Col : Word);
|
||
{ Display a column of cells }
|
||
begin
|
||
with ScreenBlock do
|
||
DisplayCellBlock(Col, Start.Row, Col, Stop.Row);
|
||
end; { Spreadsheet.DisplayCol }
|
||
|
||
procedure Spreadsheet.DisplayRow(Row : Word);
|
||
{ Display a row of cells }
|
||
begin
|
||
with ScreenBlock do
|
||
DisplayCellBlock(Start.Col, Row, Stop.Col, Row);
|
||
end; { Spreadsheet.DisplayRow }
|
||
|
||
procedure Spreadsheet.DisplayMemory;
|
||
{ Display the amount of free memory }
|
||
begin
|
||
WriteXY(RightJustStr(NumToString(MemAvail), 6), Scr.CurrCols - 5, 1,
|
||
Colors.MemoryColor);
|
||
end; { Spreadsheet.DisplayMemory }
|
||
|
||
procedure Spreadsheet.DisplayFileName;
|
||
{ Display the spreadsheet's file name, and whether or not it has been
|
||
updated }
|
||
var
|
||
S : PathStr;
|
||
begin
|
||
with DataArea do
|
||
begin
|
||
if FileName = '' then
|
||
S := 'No file'
|
||
else
|
||
S := FExpand(FileName);
|
||
WriteXY(LeftJustStr(S, LowerRight.Col - UpperLeft.Col - 20),
|
||
UpperLeft.Col + 21, UpperLeft.Row, Colors.FileNameColor);
|
||
end; { with }
|
||
end; { Spreadsheet.DisplayFileName }
|
||
|
||
procedure Spreadsheet.Display;
|
||
{ Display the entire spreadsheet }
|
||
begin
|
||
DisplayCols;
|
||
DisplayRows;
|
||
DisplayInfo;
|
||
DisplayAllCells;
|
||
DisplayMemory;
|
||
DisplayCellData;
|
||
DisplayFileName;
|
||
SetChanged(Changed);
|
||
end; { Spreadsheet.Display }
|
||
|
||
procedure Spreadsheet.SetChanged(IsChanged : Boolean);
|
||
{ Sets a spreadsheet as being changed or not changed }
|
||
var
|
||
C : Char;
|
||
begin
|
||
Changed := IsChanged;
|
||
if Changed then
|
||
C := ChangedChar
|
||
else
|
||
C := ' ';
|
||
with DataArea.UpperLeft do
|
||
WriteXY(C, Col + 19, Row, Colors.ChangedColor);
|
||
end; { Spreadsheet.SetChanged }
|
||
|
||
procedure Spreadsheet.MakeCurrent;
|
||
{ Make a spreadsheet the current one }
|
||
begin
|
||
Current := True;
|
||
DisplayInfo;
|
||
end; { Spreadsheet.MakeCurrent }
|
||
|
||
procedure Spreadsheet.MakeNotCurrent;
|
||
{ Make a spreadsheet not the current one }
|
||
begin
|
||
Current := False;
|
||
DisplayInfo;
|
||
end; { Spreadsheet.MakeNotCurrent }
|
||
|
||
procedure Spreadsheet.Update(UDisplay : Boolean);
|
||
{ Update any cells in the spreadsheet that need updating }
|
||
var
|
||
P, U : CellPos;
|
||
CP : CellPtr;
|
||
O : Word;
|
||
begin
|
||
Scr.PrintMessage(MsgRecalc);
|
||
with CellHash do
|
||
begin
|
||
for P.Row := 1 to LastPos.Row do
|
||
begin
|
||
for P.Col := 1 to LastPos.Col do
|
||
begin
|
||
CP := Search(P);
|
||
if CP^.ShouldUpdate then
|
||
begin
|
||
with FormulaCellPtr(CP)^ do
|
||
begin
|
||
Parser.Init(@CellHash, Formula, MaxCols, MaxRows);
|
||
Parser.Parse;
|
||
Value := Parser.ParseValue;
|
||
Error := Parser.ParseError;
|
||
O := CP^.Overwritten(CellHash, FormatHash, WidthHash,
|
||
LastPos, MaxCols, GetColWidth, DisplayFormulas);
|
||
if (OverwriteHash.Change(CP, O)) and UDisplay and
|
||
(CP^.Loc.Col + O >= ScreenBlock.Start.Col) then
|
||
begin
|
||
U := CP^.Loc;
|
||
for U.Col := CP^.Loc.Col to ScreenBlock.Stop.Col do
|
||
begin
|
||
if ScreenBlock.CellInBlock(U) then
|
||
DisplayCell(U);
|
||
end;
|
||
end;
|
||
end; { with }
|
||
end;
|
||
end;
|
||
end;
|
||
end; { with }
|
||
if UDisplay then
|
||
DisplayMemory;
|
||
Scr.ClearMessage;
|
||
end; { Spreadsheet.Update }
|
||
|
||
procedure Spreadsheet.ToggleFormulaDisplay;
|
||
{ Change from showing formulas to showing values and vice versa }
|
||
var
|
||
CP : CellPtr;
|
||
OChanged : Boolean;
|
||
begin
|
||
DisplayFormulas := not DisplayFormulas;
|
||
DisplayInfo;
|
||
OChanged := True;
|
||
with CellHash do
|
||
begin
|
||
CP := FirstItem;
|
||
while (CP <> nil) and OChanged do
|
||
begin
|
||
if CP^.ShouldUpdate then
|
||
OChanged := OverwriteHash.Change(CP, CP^.Overwritten(CellHash,
|
||
FormatHash, WidthHash, LastPos,
|
||
MaxCols, GetColWidth,
|
||
DisplayFormulas));
|
||
CP := NextItem;
|
||
end;
|
||
end; { with }
|
||
DisplayAllCells;
|
||
DisplayMemory;
|
||
end; { Spreadsheet.ToggleFormulaDisplay }
|
||
|
||
procedure Spreadsheet.SetScreenColStart(NewCol : Word);
|
||
{ Find the starting screen column }
|
||
begin
|
||
ScreenBlock.Start.Col := NewCol;
|
||
FindScreenColStop;
|
||
FindScreenColStart;
|
||
end; { Spreadsheet.SetScreenColStart }
|
||
|
||
procedure Spreadsheet.SetScreenColStop(NewCol : Word);
|
||
{ Find the ending screen column }
|
||
begin
|
||
ScreenBlock.Stop.Col := NewCol;
|
||
FindScreenColStart;
|
||
FindScreenColStop;
|
||
end; { Spreadsheet.SetScreenColStop }
|
||
|
||
procedure Spreadsheet.SetScreenRowStart(NewRow : Word);
|
||
{ Find the starting screen row }
|
||
begin
|
||
ScreenBlock.Start.Row := NewRow;
|
||
FindScreenRowStop;
|
||
end; { Spreadsheet.SetScreenRowStart }
|
||
|
||
procedure Spreadsheet.SetScreenRowStop(NewRow : Word);
|
||
{ Find the ending screen row }
|
||
begin
|
||
ScreenBlock.Stop.Row := NewRow;
|
||
FindScreenRowStart;
|
||
end; { Spreadsheet.SetScreenRowStop }
|
||
|
||
procedure Spreadsheet.FindScreenColStart;
|
||
{ Find the starting screen column when the ending column is known }
|
||
var
|
||
Index, Place : Integer;
|
||
Temp, Width : Byte;
|
||
begin
|
||
with ScreenBlock do
|
||
begin
|
||
Index := 0;
|
||
Place := Succ(DisplayArea.LowerRight.Col);
|
||
Width := ColWidth(Stop.Col);
|
||
repeat
|
||
ColStart^[Index] := Place - Width;
|
||
Dec(Place, Width);
|
||
Inc(Index);
|
||
if Stop.Col - Index = 0 then
|
||
Width := 0
|
||
else
|
||
Width := ColWidth(Stop.Col - Index);
|
||
until (Width = 0) or (Place - Width < DisplayArea.UpperLeft.Col);
|
||
Start.Col := Succ(Stop.Col - Index);
|
||
Dec(Index);
|
||
if ColStart^[Index] <> DisplayArea.UpperLeft.Col then
|
||
begin
|
||
Temp := ColStart^[Index] - DisplayArea.UpperLeft.Col;
|
||
for Place := 0 to Index do
|
||
Dec(ColStart^[Place], Temp);
|
||
end;
|
||
if Index > 0 then
|
||
begin
|
||
for Place := 0 to (Pred(Index) shr 1) do
|
||
begin
|
||
Temp := ColStart^[Index - Place];
|
||
ColStart^[Index - Place] := ColStart^[Place];
|
||
ColStart^[Place] := Temp;
|
||
end;
|
||
end;
|
||
end; { with }
|
||
end; { Spreadsheet.FindScreenColStart }
|
||
|
||
procedure Spreadsheet.FindScreenColStop;
|
||
{ Find the ending screen column when the starting column is known }
|
||
var
|
||
Index, Place : Byte;
|
||
Width : Byte;
|
||
begin
|
||
with ScreenBlock do
|
||
begin
|
||
Index := 0;
|
||
Place := DisplayArea.UpperLeft.Col;
|
||
Width := ColWidth(Start.Col);
|
||
repeat
|
||
ColStart^[Index] := Place;
|
||
Inc(Place, Width);
|
||
Inc(Index);
|
||
if Longint(Index) + Start.Col > MaxCols then
|
||
Width := 0
|
||
else
|
||
Width := ColWidth(Index + Start.Col);
|
||
until (Width = 0) or (Place + Width > Succ(DisplayArea.LowerRight.Col));
|
||
Stop.Col := Pred(Start.Col + Index);
|
||
end; { with }
|
||
end; { Spreadsheet.FindScreenColStop }
|
||
|
||
procedure Spreadsheet.FindScreenRowStart;
|
||
{ Find the starting screen row when the ending row is known }
|
||
begin
|
||
with ScreenBlock do
|
||
begin
|
||
if Longint(Stop.Row) - TotalRows < 0 then
|
||
begin
|
||
Start.Row := 1;
|
||
FindScreenRowStop;
|
||
end
|
||
else
|
||
Start.Row := Succ(Stop.Row - TotalRows);
|
||
end; { with }
|
||
end; { Spreadsheet.FindScreenRowStart }
|
||
|
||
procedure Spreadsheet.FindScreenRowStop;
|
||
{ Find the ending screen row when the starting row is known }
|
||
begin
|
||
with ScreenBlock do
|
||
begin
|
||
if Longint(Start.Row) + TotalRows > Succ(LongInt(MaxRows)) then
|
||
begin
|
||
Stop.Row := MaxRows;
|
||
FindScreenRowStart;
|
||
end
|
||
else
|
||
Stop.Row := Pred(Start.Row + TotalRows);
|
||
end; { with }
|
||
end; { Spreadsheet.FindScreenRowStop }
|
||
|
||
procedure Spreadsheet.SetBlankArea;
|
||
{ Find the size of the blank area (the area at the right edge of the
|
||
spreadsheet that is not used }
|
||
var
|
||
C : Word;
|
||
begin
|
||
with BlankArea do
|
||
begin
|
||
Move(DisplayArea, BlankArea, SizeOf(DisplayArea));
|
||
with ScreenBlock do
|
||
C := ColStart^[Stop.Col - Start.Col] + ColWidth(Stop.Col);
|
||
if C > DisplayArea.LowerRight.Col then
|
||
NoBlankArea := True
|
||
else begin
|
||
NoBlankArea := False;
|
||
UpperLeft.Col := C;
|
||
end;
|
||
end; { with }
|
||
end; { Spreadsheet.SetBlankArea }
|
||
|
||
function Spreadsheet.AddCell(CellType : CellTypes; P : CellPos; E : Boolean;
|
||
V : Extended; I : LStringPtr) : Boolean;
|
||
{ Add a new cell to the spreadsheet }
|
||
var
|
||
CP, S : CellPtr;
|
||
OldLastPos : CellPos;
|
||
Good : Boolean;
|
||
begin
|
||
AddCell := False;
|
||
case CellType of
|
||
ClValue : CP := New(ValueCellPtr, Init(P, E, V));
|
||
ClFormula : CP := New(FormulaCellPtr, Init(P, E, V, I));
|
||
ClText : CP := New(TextCellPtr, Init(P, I));
|
||
ClRepeat : CP := New(RepeatCellPtr, Init(P, I^.Data^[2]));
|
||
end; { case }
|
||
if CP = nil then
|
||
Exit;
|
||
if not CellHash.Add(CP) then
|
||
begin
|
||
Dispose(CP, Done);
|
||
Exit;
|
||
end;
|
||
OldLastPos := LastPos;
|
||
LastPos.Col := Max(P.Col, LastPos.Col);
|
||
LastPos.Row := Max(P.Row, LastPos.Row);
|
||
if not OverwriteHash.Add(CP, CP^.Overwritten(CellHash, FormatHash,
|
||
WidthHash, LastPos, MaxCols, GetColWidth,
|
||
DisplayFormulas)) then
|
||
begin
|
||
LastPos := OldLastPos;
|
||
CellHash.Delete(CP^.Loc, S);
|
||
Dispose(CP, Done);
|
||
Exit;
|
||
end;
|
||
S := OverwriteHash.Search(CP^.Loc);
|
||
if S <> Empty then
|
||
Good := OverwriteHash.Change(S, S^.Overwritten(CellHash, FormatHash,
|
||
WidthHash, LastPos, MaxCols, GetColWidth,
|
||
DisplayFormulas));
|
||
AddCell := True;
|
||
end; { Spreadsheet.AddCell }
|
||
|
||
procedure Spreadsheet.DeleteCell(P : CellPos; var Deleted : Boolean);
|
||
{ Delete a cell from the spreadsheet }
|
||
var
|
||
CP : CellPtr;
|
||
Good : Boolean;
|
||
begin
|
||
CellHash.Delete(P, CP);
|
||
if CP <> nil then
|
||
begin
|
||
Dispose(CP, Done);
|
||
OverwriteHash.Delete(P);
|
||
if P.Col > 1 then
|
||
begin
|
||
Dec(P.Col);
|
||
CP := OverwriteHash.Search(P);
|
||
if CP = Empty then
|
||
CP := CellHash.Search(P);
|
||
if CP <> Empty then
|
||
Good := OverwriteHash.Change(CP, CP^.Overwritten(CellHash,
|
||
FormatHash, WidthHash, LastPos, MaxCols,
|
||
GetColWidth, DisplayFormulas));
|
||
end;
|
||
Deleted := True;
|
||
end
|
||
else
|
||
Deleted := False;
|
||
end; { Spreadsheet.DeleteCell }
|
||
|
||
procedure Spreadsheet.DeleteBlock(B : Block; var Deleted : Boolean);
|
||
{ Delete a block of cells from the spreadsheet }
|
||
var
|
||
P : CellPos;
|
||
H, D : HashItemPtr;
|
||
Counter : Word;
|
||
CP : CellPtr;
|
||
begin
|
||
Scr.PrintMessage(MsgBlockDelete);
|
||
Deleted := False;
|
||
with CellHash, B do
|
||
begin
|
||
for Counter := 1 to Buckets do
|
||
begin
|
||
H := HashData^[Counter];
|
||
while H <> nil do
|
||
begin
|
||
D := H;
|
||
H := H^.Next;
|
||
Move(D^.Data, CP, SizeOf(CP));
|
||
with CP^ do
|
||
begin
|
||
if CellInBlock(Loc) then
|
||
DeleteCell(Loc, Deleted);
|
||
end; { with }
|
||
end;
|
||
end;
|
||
end; { with }
|
||
Scr.ClearMessage;
|
||
end; { DeleteBlock }
|
||
|
||
function Spreadsheet.CellToFString(P : CellPos; var Color : Byte) : String;
|
||
{ Create a formatted string from a cell }
|
||
var
|
||
CP : CellPtr;
|
||
S : String;
|
||
S1 : DollarStr;
|
||
F : FormatType;
|
||
ColorFound : Boolean;
|
||
Colr : Byte;
|
||
begin
|
||
ColorFound := True;
|
||
if Current and (SameCellPos(P, CurrPos)) then
|
||
Color := Colors.HighlightColor
|
||
else if BlockOn and (CurrBlock.CellInBlock(P)) then
|
||
Color := Colors.BlockColor
|
||
else
|
||
ColorFound := False;
|
||
CP := CellHash.Search(P);
|
||
if (CP^.HasError) then
|
||
begin
|
||
S := ErrorString;
|
||
S1 := '';
|
||
if ColorFound then
|
||
Inc(Color, Blink)
|
||
else
|
||
Color := Colors.CellErrorColor;
|
||
F := Ord(JCenter) shl JustShift;
|
||
end
|
||
else begin
|
||
S := CP^.FormattedString(OverwriteHash, FormatHash, WidthHash,
|
||
GetColWidth, P, DisplayFormulas, 1,
|
||
ColWidth(P.Col), S1, Colr);
|
||
if not ColorFound then
|
||
Color := Colr;
|
||
F := CP^.Format(FormatHash, DisplayFormulas);
|
||
end;
|
||
case Justification((F shr JustShift) and JustPart) of
|
||
JLeft : CellToFString := S1 + LeftJustStr(S, ColWidth(P.Col) -
|
||
Length(S1));
|
||
JCenter : CellToFString := S1 + CenterStr(S, ColWidth(P.Col) -
|
||
Length(S1));
|
||
JRight : CellToFString := S1 + RightJustStr(S, ColWidth(P.Col) -
|
||
Length(S1));
|
||
end; { case }
|
||
end; { Spreadsheet.CellToFString }
|
||
|
||
procedure Spreadsheet.SetLastPos(DPos : CellPos);
|
||
{ Find the last position used in a spreadsheet }
|
||
var
|
||
CP : CellPtr;
|
||
Counter : Word;
|
||
ColFound, RowFound : Boolean;
|
||
begin
|
||
with CellHash do
|
||
begin
|
||
ColFound := DPos.Col < LastPos.Col;
|
||
RowFound := DPos.Row < LastPos.Row;
|
||
if (not ColFound) or (not RowFound) then
|
||
begin
|
||
if not ColFound then
|
||
LastPos.Col := 1;
|
||
if not RowFound then
|
||
LastPos.Row := 1;
|
||
CP := FirstItem;
|
||
while CP <> nil do
|
||
begin
|
||
if not ColFound then
|
||
begin
|
||
if CP^.Loc.Col > LastPos.Col then
|
||
begin
|
||
LastPos.Col := CP^.Loc.Col;
|
||
ColFound := LastPos.Col = DPos.Col;
|
||
if ColFound and RowFound then
|
||
Exit;
|
||
end;
|
||
end;
|
||
if not RowFound then
|
||
begin
|
||
if CP^.Loc.Row > LastPos.Row then
|
||
begin
|
||
LastPos.Row := CP^.Loc.Row;
|
||
RowFound := LastPos.Row = DPos.Row;
|
||
if ColFound and RowFound then
|
||
Exit;
|
||
end;
|
||
end;
|
||
CP := NextItem;
|
||
end;
|
||
end;
|
||
end; { with }
|
||
end; { Spreadsheet.SetLastPos }
|
||
|
||
function Spreadsheet.GetCurrCol : Word;
|
||
{ Find the current column }
|
||
begin
|
||
GetCurrCol := CurrPos.Col;
|
||
end; { Spreadsheet.GetCurrCol }
|
||
|
||
function Spreadsheet.GetCurrRow : Word;
|
||
{ Find the current row }
|
||
begin
|
||
GetCurrRow := CurrPos.Row;
|
||
end; { Spreadsheet.GetCurrRow }
|
||
|
||
function Spreadsheet.ColToX(Col : Word) : Byte;
|
||
{ Find where on the screen a column starts }
|
||
begin
|
||
ColToX := ColStart^[Col - ScreenBlock.Start.Col];
|
||
end; { Spreadsheet.ColToX }
|
||
|
||
function Spreadsheet.RowToY(Row : Word) : Byte;
|
||
{ Find where on the screen a row starts }
|
||
begin
|
||
RowToY := Row + DisplayArea.UpperLeft.Row - ScreenBlock.Start.Row;
|
||
end; { Spreadsheet.RowToY }
|
||
|
||
{$F+}
|
||
|
||
function Spreadsheet.ColWidth(Col : Word) : Byte;
|
||
{ Returns the width of a column }
|
||
var
|
||
Width : Word;
|
||
begin
|
||
Width := WidthHash.Search(Col);
|
||
if Width = 0 then
|
||
ColWidth := DefaultColWidth
|
||
else
|
||
ColWidth := Width;
|
||
end; { Spreadsheet.ColWidth }
|
||
|
||
{$F-}
|
||
|
||
function Spreadsheet.SameCellPos(P1, P2 : CellPos) : Boolean;
|
||
{ Returns True if two cells are at the same position }
|
||
begin
|
||
SameCellPos := Compare(P1, P2, SizeOf(CellPos));
|
||
end; { Spreadsheet.SameCellPos }
|
||
|
||
procedure Spreadsheet.FixOverwrite;
|
||
{ Fixes the overwrite hash table when the formats have been changed }
|
||
var
|
||
CP, D : CellPtr;
|
||
Counter : Word;
|
||
Good : Boolean;
|
||
begin
|
||
with CellHash do
|
||
begin
|
||
CP := FirstItem;
|
||
while CP <> nil do
|
||
begin
|
||
if not OverwriteHash.Add(CP, CP^.Overwritten(CellHash, FormatHash,
|
||
WidthHash, LastPos, MaxCols, GetColWidth,
|
||
DisplayFormulas)) then
|
||
begin
|
||
CellHash.Delete(CP^.Loc, D);
|
||
Dispose(CP, Done);
|
||
Exit;
|
||
end;
|
||
CP := OverwriteHash.Search(CP^.Loc);
|
||
if CP <> Empty then
|
||
Good := OverwriteHash.Change(CP, CP^.Overwritten(CellHash,
|
||
FormatHash, WidthHash, LastPos,
|
||
MaxCols, GetColWidth,
|
||
DisplayFormulas));
|
||
CP := NextItem;
|
||
end;
|
||
end; { with }
|
||
end; { Spreadsheet.FixOverwrite }
|
||
|
||
function Spreadsheet.FromFile(Name : PathStr) : Boolean;
|
||
{ Reads a spreadsheet from disk }
|
||
var
|
||
Header : String[Length(FileHeader)];
|
||
TotalC : Longint;
|
||
TotalW : Word;
|
||
TotalF : Longint;
|
||
S : SSStream;
|
||
NewLastPos : CellPos;
|
||
begin
|
||
FromFile := True;
|
||
Name := UpperCase(Name);
|
||
S.Init(Name, SOpen);
|
||
if S.Status <> 0 then
|
||
begin
|
||
Scr.PrintError(ErrNoOpen);
|
||
Init(0, DefaultMaxCols, DefaultMaxRows, DefaultMaxDecimalPlaces,
|
||
DefaultDefaultDecimalPlaces, DefaultDefaultColWidth);
|
||
Exit;
|
||
end
|
||
else begin
|
||
Header[0] := Chr(Length(FileHeader));
|
||
S.Read(Header[1], Length(FileHeader));
|
||
if (S.Status <> 0) or (Header <> FileHeader) then
|
||
begin
|
||
Scr.PrintError(ErrNotSpreadsheet);
|
||
S.Done;
|
||
Init(0, DefaultMaxCols, DefaultMaxRows, DefaultMaxDecimalPlaces,
|
||
DefaultDefaultDecimalPlaces, DefaultDefaultColWidth);
|
||
Exit;
|
||
end;
|
||
FileName := Name;
|
||
S.Read(NewLastPos, SizeOf(NewLastPos));
|
||
S.Read(TotalW, SizeOf(TotalW));
|
||
S.Read(TotalF, SizeOf(TotalF));
|
||
S.Read(TotalC, SizeOf(TotalC));
|
||
if not Init(TotalC, DefaultMaxCols, DefaultMaxRows,
|
||
DefaultMaxDecimalPlaces, DefaultDefaultDecimalPlaces,
|
||
DefaultDefaultColWidth) then
|
||
begin
|
||
S.Done;
|
||
FromFile := False;
|
||
Exit;
|
||
end;
|
||
LastPos := NewLastPos;
|
||
Scr.PrintMessage(MsgLoad);
|
||
FileName := Name;
|
||
WidthHash.Load(S, TotalW);
|
||
FormatHash.Load(S, TotalF);
|
||
CellHash.Load(S, TotalC);
|
||
S.Done;
|
||
FixOverwrite;
|
||
Update(DisplayNo);
|
||
Scr.ClearMessage;
|
||
end;
|
||
FromFile := True;
|
||
end; { Spreadsheet.FromFile }
|
||
|
||
procedure Spreadsheet.ToFile(Name : PathStr);
|
||
{ Writes a spreadsheet to disk }
|
||
var
|
||
Header : String[Length(FileHeader)];
|
||
S : SSStream;
|
||
begin
|
||
S.Init(Name, SCreate);
|
||
if S.Status <> 0 then
|
||
begin
|
||
Scr.PrintError(ErrNoOpen);
|
||
Exit;
|
||
end;
|
||
Scr.PrintMessage(MsgSave);
|
||
FileName := Name;
|
||
Header := FileHeader;
|
||
S.Write(Header[1], Length(Header));
|
||
S.Write(LastPos, SizeOf(LastPos));
|
||
S.Write(WidthHash.Items, 2);
|
||
S.Write(FormatHash.Items, SizeOf(FormatHash.Items));
|
||
S.Write(CellHash.Items, SizeOf(CellHash.Items));
|
||
WidthHash.Store(S);
|
||
FormatHash.Store(S);
|
||
CellHash.Store(S);
|
||
Scr.ClearMessage;
|
||
S.Done;
|
||
if S.Status <> 0 then
|
||
Scr.PrintError(ErrDiskFull)
|
||
else
|
||
SetChanged(NotChanged);
|
||
end; { Spreadsheet.ToFile }
|
||
|
||
procedure Spreadsheet.CheckForSave;
|
||
{ Before prompting for a file name, this will check to see if you want to
|
||
save the spreadsheet }
|
||
var
|
||
S : PathStr;
|
||
GoodFile, ESCPressed : Boolean;
|
||
begin
|
||
if Changed and (GetYesNo(PromptSaveYN, ESCPressed)) then
|
||
begin
|
||
S := FileName;
|
||
repeat
|
||
GoodFile := True;
|
||
if S = '' then
|
||
begin
|
||
S := ReadString(PromptFileSave, Pred(SizeOf(PathStr)), ESCPressed);
|
||
if S = '' then
|
||
Exit;
|
||
end;
|
||
if FileExists(S) then
|
||
begin
|
||
GoodFile := GetYesNo(PromptOverwriteFile, ESCPressed);
|
||
if ESCPressed then
|
||
Exit;
|
||
if not GoodFile then
|
||
S := '';
|
||
end;
|
||
until GoodFile;
|
||
ToFile(S);
|
||
end;
|
||
end; { Spreadsheet.CheckForSave }
|
||
|
||
procedure Spreadsheet.ChangeWidth;
|
||
{ Changes the width of a column }
|
||
var
|
||
W, C : Word;
|
||
Good : Boolean;
|
||
P : CellPos;
|
||
O : Word;
|
||
CP : CellPtr;
|
||
begin
|
||
C := GetColumn(PromptColumnWidth, MaxCols, ColSpace);
|
||
if C = 0 then
|
||
Exit;
|
||
W := GetNumber(PromptNewWidth, MinColWidth, MaxColWidth, Good);
|
||
if not Good then
|
||
Exit;
|
||
with WidthHash do
|
||
begin
|
||
Delete(C);
|
||
if W <> DefaultColWidth then
|
||
Good := Add(C, W);
|
||
end; { with }
|
||
if not Good then
|
||
Exit;
|
||
SetScreenColStart(ScreenBlock.Start.Col);
|
||
SetChanged(WasChanged);
|
||
with OverwriteHash do
|
||
begin
|
||
Done;
|
||
Init(OverwriteHashStart(CellHash.Items));
|
||
end;
|
||
with CellHash do
|
||
begin
|
||
CP := FirstItem;
|
||
while CP <> nil do
|
||
begin
|
||
O := CP^.Overwritten(CellHash, FormatHash, WidthHash, LastPos,
|
||
MaxCols, GetColWidth, DisplayFormulas);
|
||
if O <> 0 then
|
||
Good := OverwriteHash.Add(CP, O);
|
||
CP := NextItem;
|
||
end;
|
||
end; { with }
|
||
if CurrPos.Col > ScreenBlock.Stop.Col then
|
||
SetScreenColStart(CurrPos.Col);
|
||
Display;
|
||
end; { Spreadsheet.ChangeWidth }
|
||
|
||
function Spreadsheet.CellHashStart(TotalCells : Longint) : BucketRange;
|
||
{ Formula that determines the number of cell hash table buckets }
|
||
begin
|
||
CellHashStart := Max(100, Min(MaxBuckets, TotalCells div 10));
|
||
end; { Spreadsheet.CellHashStart }
|
||
|
||
function Spreadsheet.WidthHashStart(TotalCells : Longint) : BucketRange;
|
||
{ Formula that determines the number of width hash table buckets }
|
||
begin
|
||
WidthHashStart := 10;
|
||
end; { Spreadsheet.WidthHashStart }
|
||
|
||
function Spreadsheet.OverwriteHashStart(TotalCells : Longint) : BucketRange;
|
||
{ Formula that determines the number of overwrite hash table buckets }
|
||
begin
|
||
OverwriteHashStart := 10;
|
||
end; { Spreadsheet.OverwriteHashStart }
|
||
|
||
procedure Spreadsheet.Print;
|
||
{ Prints a spreadsheet to a file or a printer }
|
||
var
|
||
S : PathStr;
|
||
F : Text;
|
||
PageCols : Byte;
|
||
PageV, PageH : Word;
|
||
Finished, GoodFile, Error, Compress, Border, ESCPressed : Boolean;
|
||
StartCol : Word;
|
||
StartRow : Word;
|
||
|
||
procedure WString(S : String);
|
||
begin
|
||
Writeln(F, S);
|
||
if IOResult <> 0 then
|
||
begin
|
||
if S = PrinterName then
|
||
Scr.PrintError(ErrPrinterError)
|
||
else
|
||
Scr.PrintError(ErrDiskFull);
|
||
Error := True;
|
||
Finished := True;
|
||
end;
|
||
end; { WString }
|
||
|
||
function RowStartString(Row : Word) : String;
|
||
begin
|
||
if (PageH = 1) and Border then
|
||
RowStartString := LeftJustStr(RowToString(Row), RowNumberSpace)
|
||
else
|
||
RowStartString := '';
|
||
end; { RowStartString }
|
||
|
||
procedure PrintPage;
|
||
var
|
||
Counter : Word;
|
||
S : String;
|
||
Color, Cols, Rows : Byte;
|
||
P : CellPos;
|
||
begin
|
||
for Counter := 1 to PrintTopMargin do
|
||
begin
|
||
WString('');
|
||
if Error then
|
||
Exit;
|
||
end;
|
||
Rows := Min(PrintRows - PrintTopMargin - PrintBottomMargin,
|
||
Succ(MaxRows - StartRow));
|
||
if Border then
|
||
Dec(Rows);
|
||
Cols := 0;
|
||
Counter := Length(RowStartString(StartRow));
|
||
while Counter <= PageCols do
|
||
begin
|
||
Inc(Counter, ColWidth(Cols + StartCol));
|
||
Inc(Cols);
|
||
end;
|
||
Dec(Cols);
|
||
Cols := Min(Cols, Succ(MaxCols - StartCol));
|
||
if Border and (PageV = 1) then
|
||
begin
|
||
S := FillString(Length(RowStartString(StartRow)), ' ');
|
||
for Counter := StartCol to Pred(StartCol + Cols) do
|
||
S := S + CenterStr(ColToString(Counter), ColWidth(Counter));
|
||
WString(S);
|
||
if Error then
|
||
Exit;
|
||
end;
|
||
for P.Row := StartRow to Pred(StartRow + Rows) do
|
||
begin
|
||
S := RowStartString(P.Row);
|
||
for P.Col := StartCol to Pred(StartCol + Cols) do
|
||
S := S + CellToFString(P, Color);
|
||
WString(S);
|
||
if Error then
|
||
Exit;
|
||
end;
|
||
Inc(StartCol, Cols);
|
||
if (StartCol > LastPos.Col) or (StartCol = 0) then
|
||
begin
|
||
Inc(StartRow, Rows);
|
||
if (StartRow > LastPos.Row) or (StartRow = 0) then
|
||
Finished := True
|
||
else begin
|
||
Inc(PageV);
|
||
PageH := 1;
|
||
StartCol := 1;
|
||
end;
|
||
end
|
||
else
|
||
Inc(PageH);
|
||
Write(F, Chr(FF));
|
||
end; { PrintPage }
|
||
|
||
begin { Spreadsheet.Print }
|
||
repeat
|
||
GoodFile := True;
|
||
S := ReadString(PromptFilePrint, Pred(SizeOf(PathStr)), ESCPressed);
|
||
if ESCPressed then
|
||
Exit;
|
||
if S = '' then
|
||
S := PrinterName
|
||
else begin
|
||
if FileExists(S) then
|
||
begin
|
||
GoodFile := GetYesNo(PromptOverwriteFile, ESCPressed);
|
||
if ESCPressed then
|
||
Exit;
|
||
end;
|
||
end;
|
||
until GoodFile;
|
||
Compress := GetYesNo(PromptCompressPrint, ESCPressed);
|
||
if ESCPressed then
|
||
Exit;
|
||
Border := GetYesNo(PromptBorderPrint, ESCPressed);
|
||
if ESCPressed then
|
||
Exit;
|
||
Error := False;
|
||
{$I-}
|
||
Assign(F, S);
|
||
Rewrite(F);
|
||
if IOResult <> 0 then
|
||
begin
|
||
Scr.PrintError(ErrNoOpen);
|
||
Exit;
|
||
end;
|
||
if Compress then
|
||
begin
|
||
PageCols := PrintCompressedCols;
|
||
Write(F, PrinterCompressChar);
|
||
end
|
||
else
|
||
PageCols := PrintNormalCols;
|
||
PageV := 1;
|
||
PageH := 1;
|
||
StartCol := 1;
|
||
StartRow := 1;
|
||
Finished := False;
|
||
repeat
|
||
PrintPage;
|
||
until Finished;
|
||
Close(F);
|
||
{$I+}
|
||
end; { Spreadsheet.Print }
|
||
|
||
procedure Spreadsheet.DeleteColumn;
|
||
{ Deletes a column from the spreadsheet }
|
||
var
|
||
C : Word;
|
||
Start, Stop, P, OldPos, OldSPos : CellPos;
|
||
Deleted : Boolean;
|
||
OldName : PathStr;
|
||
CP : CellPtr;
|
||
H : HashItemPtr;
|
||
B : Block;
|
||
F : File;
|
||
Good : Boolean;
|
||
begin
|
||
C := GetColumn(PromptColumnDelete, MaxCols, ColSpace);
|
||
if C = 0 then
|
||
Exit;
|
||
OldPos := CurrPos;
|
||
OldSPos := ScreenBlock.Start;
|
||
P.Col := C;
|
||
Deleted := False;
|
||
if P.Col <= LastPos.Col then
|
||
begin
|
||
with B do
|
||
begin
|
||
Start.Col := P.Col;
|
||
Start.Row := 1;
|
||
Stop.Col := P.Col;
|
||
Stop.Row := LastPos.Row;
|
||
Good := FormatHash.Delete(Start, Stop);
|
||
end; { with }
|
||
DeleteBlock(B, Deleted);
|
||
end;
|
||
Dec(LastPos.Col);
|
||
WidthHash.Delete(C);
|
||
with CellHash do
|
||
begin
|
||
CP := FirstItem;
|
||
while CP <> nil do
|
||
begin
|
||
with CP^ do
|
||
begin
|
||
if Loc.Col > C then
|
||
Dec(Loc.Col);
|
||
if (CP^.ShouldUpdate) and (Loc.Col > C) then
|
||
FixFormulaCol(CP, -1, MaxCols, MaxRows);
|
||
end; { with }
|
||
CP := NextItem;
|
||
end;
|
||
end; { with }
|
||
with WidthHash do
|
||
begin
|
||
H := FirstItem;
|
||
while H <> nil do
|
||
begin
|
||
if WordPtr(@H^.Data)^ > C then
|
||
Dec(WordPtr(@H^.Data)^);
|
||
H := NextItem;
|
||
end;
|
||
end; { with }
|
||
with FormatHash do
|
||
begin
|
||
H := FirstItem;
|
||
while H <> nil do
|
||
begin
|
||
Move(H^.Data, Start, SizeOf(Start));
|
||
Move(H^.Data[SizeOf(CellPos)], Stop, SizeOf(Stop));
|
||
if (Start.Col = C) and (Stop.Col = C) then
|
||
Good := Delete(Start, Stop)
|
||
else begin
|
||
if Start.Col > C then
|
||
begin
|
||
Dec(Start.Col);
|
||
Move(Start, H^.Data, SizeOf(Start));
|
||
end;
|
||
if Stop.Col > C then
|
||
begin
|
||
Dec(Stop.Col);
|
||
Move(Stop, H^.Data[SizeOf(CellPos)], SizeOf(Stop));
|
||
end;
|
||
end;
|
||
H := NextItem;
|
||
end;
|
||
end; { with }
|
||
OldName := FileName;
|
||
ToFile(TempFileName);
|
||
Done;
|
||
Good := FromFile(TempFileName);
|
||
Assign(F, TempFileName);
|
||
Erase(F);
|
||
FileName := OldName;
|
||
if Deleted then
|
||
P.Row := LastPos.Row
|
||
else
|
||
P.Row := 1;
|
||
Dec(P.Col);
|
||
SetLastPos(P);
|
||
MakeCurrent;
|
||
SetChanged(WasChanged);
|
||
CurrPos := OldPos;
|
||
SetScreenColStart(OldSPos.Col);
|
||
SetScreenRowStart(OldSPos.Row);
|
||
Display;
|
||
end; { Spreadsheet.DeleteColumn }
|
||
|
||
procedure Spreadsheet.InsertColumn;
|
||
{ Inserts a column into the spreadsheet }
|
||
var
|
||
C : Word;
|
||
Start, Stop, P, OldPos, OldSPos : CellPos;
|
||
Deleted : Boolean;
|
||
H : HashItemPtr;
|
||
OldName : PathStr;
|
||
CP : CellPtr;
|
||
B : Block;
|
||
F : File;
|
||
Good : Boolean;
|
||
begin
|
||
C := GetColumn(PromptColumnInsert, MaxCols, ColSpace);
|
||
if C = 0 then
|
||
Exit;
|
||
OldPos := CurrPos;
|
||
OldSPos := ScreenBlock.Start;
|
||
Deleted := False;
|
||
if LastPos.Col = MaxCols then
|
||
begin
|
||
with B do
|
||
begin
|
||
Start.Col := MaxCols;
|
||
Start.Row := 1;
|
||
Stop.Col := MaxCols;
|
||
Stop.Row := LastPos.Row;
|
||
Good := FormatHash.Delete(Start, Stop);
|
||
end; { with }
|
||
DeleteBlock(B, Deleted);
|
||
end
|
||
else
|
||
Inc(LastPos.Col);
|
||
P.Col := C;
|
||
WidthHash.Delete(MaxCols);
|
||
with CellHash do
|
||
begin
|
||
CP := FirstItem;
|
||
while CP <> nil do
|
||
begin
|
||
with CP^ do
|
||
begin
|
||
if Loc.Col >= C then
|
||
Inc(Loc.Col);
|
||
if (CP^.ShouldUpdate) and (Loc.Col >= C) then
|
||
FixFormulaCol(CP, 1, MaxCols, MaxRows);
|
||
end; { with }
|
||
CP := NextItem;
|
||
end;
|
||
end; { with }
|
||
with WidthHash do
|
||
begin
|
||
H := FirstItem;
|
||
while H <> nil do
|
||
begin
|
||
if WordPtr(@H^.Data)^ >= C then
|
||
Inc(WordPtr(@H^.Data)^);
|
||
H := NextItem;
|
||
end;
|
||
end; { with }
|
||
with FormatHash do
|
||
begin
|
||
H := FirstItem;
|
||
while H <> nil do
|
||
begin
|
||
Move(H^.Data, Start, SizeOf(Start));
|
||
Move(H^.Data[SizeOf(CellPos)], Stop, SizeOf(Stop));
|
||
if Start.Col >= C then
|
||
begin
|
||
Inc(Start.Col);
|
||
Move(Start, H^.Data, SizeOf(Start));
|
||
end;
|
||
if Stop.Col >= C then
|
||
begin
|
||
Inc(Stop.Col);
|
||
Move(Stop, H^.Data[SizeOf(CellPos)], SizeOf(Stop));
|
||
end;
|
||
H := NextItem;
|
||
end;
|
||
end; { with }
|
||
OldName := FileName;
|
||
ToFile(TempFileName);
|
||
Done;
|
||
Good := FromFile(TempFileName);
|
||
Assign(F, TempFileName);
|
||
Erase(F);
|
||
FileName := OldName;
|
||
if Deleted then
|
||
P.Row := LastPos.Row
|
||
else
|
||
P.Row := 1;
|
||
if LastPos.Col = MaxCols then
|
||
P.Col := MaxCols
|
||
else
|
||
Inc(P.Col);
|
||
SetLastPos(P);
|
||
MakeCurrent;
|
||
SetChanged(WasChanged);
|
||
CurrPos := OldPos;
|
||
SetScreenColStart(OldSPos.Col);
|
||
SetScreenRowStart(OldSPos.Row);
|
||
Display;
|
||
end; { Spreadsheet.InsertColumn }
|
||
|
||
procedure Spreadsheet.DeleteRow;
|
||
{ Deletes a row from the spreadsheet }
|
||
var
|
||
R : Word;
|
||
Start, Stop, P, OldPos, OldSPos : CellPos;
|
||
Deleted : Boolean;
|
||
OldName : PathStr;
|
||
CP : CellPtr;
|
||
B : Block;
|
||
F : File;
|
||
Good : Boolean;
|
||
H : HashItemPtr;
|
||
begin
|
||
R := GetRow(PromptRowDelete, MaxRows);
|
||
if (R = 0) or (R > LastPos.Row) then
|
||
Exit;
|
||
OldPos := CurrPos;
|
||
OldSPos := ScreenBlock.Start;
|
||
P.Row := R;
|
||
if P.Row <= LastPos.Row then
|
||
begin
|
||
with B do
|
||
begin
|
||
Start.Col := 1;
|
||
Start.Row := P.Row;
|
||
Stop.Col := LastPos.Col;
|
||
Stop.Row := P.Row;
|
||
Good := FormatHash.Delete(Start, Stop);
|
||
end; { with }
|
||
DeleteBlock(B, Deleted);
|
||
end;
|
||
Dec(LastPos.Row);
|
||
with CellHash do
|
||
begin
|
||
CP := FirstItem;
|
||
while CP <> nil do
|
||
begin
|
||
with CP^ do
|
||
begin
|
||
if Loc.Row > R then
|
||
Dec(Loc.Row);
|
||
if (CP^.ShouldUpdate) and (Loc.Row > R) then
|
||
FixFormulaRow(CP, -1, MaxCols, MaxRows);
|
||
end; { with }
|
||
CP := NextItem;
|
||
end;
|
||
end; { with }
|
||
with FormatHash do
|
||
begin
|
||
H := FirstItem;
|
||
while H <> nil do
|
||
begin
|
||
Move(H^.Data, Start, SizeOf(Start));
|
||
Move(H^.Data[SizeOf(CellPos)], Stop, SizeOf(Stop));
|
||
if (Start.Row = R) and (Stop.Row = R) then
|
||
Good := Delete(Start, Stop)
|
||
else begin
|
||
if Start.Row > R then
|
||
begin
|
||
Dec(Start.Row);
|
||
Move(Start, H^.Data, SizeOf(Start));
|
||
end;
|
||
if Stop.Row > R then
|
||
begin
|
||
Dec(Stop.Row);
|
||
Move(Stop, H^.Data[SizeOf(CellPos)], SizeOf(Stop));
|
||
end;
|
||
end;
|
||
H := NextItem;
|
||
end;
|
||
end; { with }
|
||
OldName := FileName;
|
||
ToFile(TempFileName);
|
||
Done;
|
||
Good := FromFile(TempFileName);
|
||
Assign(F, TempFileName);
|
||
Erase(F);
|
||
FileName := OldName;
|
||
if Deleted then
|
||
P.Col := LastPos.Col
|
||
else
|
||
P.Col := 1;
|
||
Dec(P.Row);
|
||
SetLastPos(P);
|
||
MakeCurrent;
|
||
SetChanged(WasChanged);
|
||
CurrPos := OldPos;
|
||
SetScreenColStart(OldSPos.Col);
|
||
SetScreenRowStart(OldSPos.Row);
|
||
Display;
|
||
end; { Spreadsheet.DeleteRow }
|
||
|
||
procedure Spreadsheet.InsertRow;
|
||
{ Inserts a row into the spreadsheet }
|
||
var
|
||
R : Word;
|
||
Start, Stop, P, OldPos, OldSPos : CellPos;
|
||
Deleted : Boolean;
|
||
OldName : PathStr;
|
||
CP : CellPtr;
|
||
B : Block;
|
||
F : File;
|
||
Good : Boolean;
|
||
H : HashItemPtr;
|
||
begin
|
||
R := GetRow(PromptRowInsert, MaxRows);
|
||
if (R = 0) or (R > LastPos.Row) then
|
||
Exit;
|
||
OldPos := CurrPos;
|
||
OldSPos := ScreenBlock.Start;
|
||
if LastPos.Row = MaxRows then
|
||
begin
|
||
with B do
|
||
begin
|
||
Start.Col := 1;
|
||
Start.Row := MaxRows;
|
||
Stop.Col := LastPos.Col;
|
||
Stop.Row := MaxRows;
|
||
Good := FormatHash.Delete(Start, Stop);
|
||
end; { with }
|
||
DeleteBlock(B, Deleted);
|
||
end
|
||
else
|
||
Inc(LastPos.Row);
|
||
P.Row := R;
|
||
with CellHash do
|
||
begin
|
||
CP := FirstItem;
|
||
while CP <> nil do
|
||
begin
|
||
with CP^ do
|
||
begin
|
||
if Loc.Row >= R then
|
||
Inc(Loc.Row);
|
||
if (CP^.ShouldUpdate) and (Loc.Row >= R) then
|
||
FixFormulaRow(CP, 1, MaxCols, MaxRows);
|
||
end; { with }
|
||
CP := NextItem;
|
||
end;
|
||
end; { with }
|
||
with FormatHash do
|
||
begin
|
||
H := FirstItem;
|
||
while H <> nil do
|
||
begin
|
||
Move(H^.Data, Start, SizeOf(Start));
|
||
Move(H^.Data[SizeOf(CellPos)], Stop, SizeOf(Stop));
|
||
if Start.Row >= R then
|
||
begin
|
||
Inc(Start.Row);
|
||
Move(Start, H^.Data, SizeOf(Start));
|
||
end;
|
||
if Stop.Row >= R then
|
||
begin
|
||
Inc(Stop.Row);
|
||
Move(Stop, H^.Data[SizeOf(CellPos)], SizeOf(Stop));
|
||
end;
|
||
H := NextItem;
|
||
end;
|
||
end; { with }
|
||
OldName := FileName;
|
||
ToFile(TempFileName);
|
||
Done;
|
||
Good := FromFile(TempFileName);
|
||
Assign(F, TempFileName);
|
||
Erase(F);
|
||
FileName := OldName;
|
||
if Deleted then
|
||
P.Col := LastPos.Col
|
||
else
|
||
P.Col := 1;
|
||
if LastPos.Row = MaxRows then
|
||
P.Row := MaxRows
|
||
else
|
||
Inc(P.Row);
|
||
SetLastPos(P);
|
||
MakeCurrent;
|
||
SetChanged(WasChanged);
|
||
CurrPos := OldPos;
|
||
SetScreenColStart(OldSPos.Col);
|
||
SetScreenRowStart(OldSPos.Row);
|
||
Display;
|
||
end; { Spreadsheet.InsertRow }
|
||
|
||
end.
|
||
|