fpspreadsheet: Redo storage of formulas: formulas now are stored in a separate tree independent of the cells tree. The field FormulaValue of TCell record was removed. All unit tests passed. Demos updated (issues of speedtest due to SST in BIFF8 found - not related).

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@6446 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2018-05-30 22:15:07 +00:00
parent 9da45f0c2c
commit 40fabfc201
35 changed files with 1476 additions and 856 deletions

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<CONFIG>
<ProjectOptions>
<Version Value="10"/>
<Version Value="11"/>
<PathDelim Value="\"/>
<General>
<SessionStorage Value="InProjectDir"/>
@ -21,9 +21,10 @@
<DestinationDirectory Value="c:\temp\fpsspeedtest"/>
</PublishOptions>
<RunParams>
<local>
<FormatVersion Value="1"/>
</local>
<FormatVersion Value="2"/>
<Modes Count="1">
<Mode0 Name="default"/>
</Modes>
</RunParams>
<RequiredPackages Count="2">
<Item1>

View File

@ -51,7 +51,7 @@ begin
parser := TsSpreadsheetParser.Create(worksheet);
try
try
parser.Expression := cell^.FormulaValue;
parser.Expression := worksheet.ReadFormula(cell);
res := parser.Evaluate;
WriteLn('A2: ', parser.Expression);

View File

@ -7,7 +7,7 @@ uses
cthreads,
{$ENDIF}{$ENDIF}
SysUtils, Classes, TypInfo,
fpsTypes, fpSpreadsheet, fpsSearch, fpsUtils, laz_fpspreadsheet;
fpsTypes, fpSpreadsheet, fpsSearch, fpsUtils;
var
workbook: TsWorkbook;

View File

@ -55,7 +55,7 @@
<SearchPaths>
<IncludeFiles Value="..\..\source"/>
<OtherUnitFiles Value="..\..\source\common"/>
<UnitOutputDirectory Value="..\..\lib\$(TargetCPU)-$(TargetOS)"/>
<UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
</SearchPaths>
<Parsing>
<SyntaxOptions>

View File

@ -50,7 +50,7 @@ begin
UTF8ToConsole(MyWorkSheet.ReadAsText(CurCell^.Row, CurCell^.Col))
);
if HasFormula(CurCell) then
Write(' (Formula ', CurCell^.FormulaValue, ')');
Write(' (Formula ', MyWorksheet.ReadFormula(CurCell), ')');
WriteLn;
end;

View File

@ -48,7 +48,7 @@ begin
' Col: ', CurCell^.Col, ' Value: ',
UTF8ToConsole(MyWorkSheet.ReadAsText(CurCell^.Row, CurCell^.Col)));
if HasFormula(CurCell) then
Write(' - Formula: ', CurCell^.FormulaValue);
Write(' - Formula: ', MyWorksheet.ReadFormula(CurCell));
WriteLn;
end;

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<CONFIG>
<ProjectOptions>
<Version Value="10"/>
<Version Value="11"/>
<PathDelim Value="\"/>
<General>
<Flags>
@ -23,9 +23,16 @@
</PublishOptions>
<RunParams>
<local>
<FormatVersion Value="1"/>
<LaunchingApplication PathPlusParams="\usr\X11R6\bin\xterm -T 'Lazarus Run Output' -e $(LazarusDir)\tools\runwait.sh $(TargetCmdLine)"/>
</local>
<FormatVersion Value="2"/>
<Modes Count="1">
<Mode0 Name="default">
<local>
<LaunchingApplication PathPlusParams="\usr\X11R6\bin\xterm -T 'Lazarus Run Output' -e $(LazarusDir)\tools\runwait.sh $(TargetCmdLine)"/>
</local>
</Mode0>
</Modes>
</RunParams>
<RequiredPackages Count="1">
<Item1>

View File

@ -20,7 +20,7 @@ var
const
// url = 'http://unicode.e-workers.de/entities.php';
url = 'http://www.freepascal.org/docs.var';
url = 'https://www.freepascal.org/docs.var';
begin
stream := TMemoryStream.Create;

View File

@ -53,7 +53,7 @@ begin
UTF8ToConsole(MyWorkSheet.ReadAsText(CurCell^.Row, CurCell^.Col))
);
if HasFormula(CurCell) then
WriteLn(' Formula: ', CurCell^.FormulaValue)
WriteLn(' Formula: ', MyWorksheet.ReadFormula(Curcell))
else
WriteLn;
end;

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<CONFIG>
<ProjectOptions>
<Version Value="10"/>
<Version Value="11"/>
<PathDelim Value="\"/>
<General>
<SessionStorage Value="InProjectDir"/>
@ -44,9 +44,10 @@
<Version Value="2"/>
</PublishOptions>
<RunParams>
<local>
<FormatVersion Value="1"/>
</local>
<FormatVersion Value="2"/>
<Modes Count="1">
<Mode0 Name="default"/>
</Modes>
</RunParams>
<RequiredPackages Count="3">
<Item1>

View File

@ -7,8 +7,7 @@ uses
cthreads,
{$ENDIF}{$ENDIF}
Interfaces, // this includes the LCL widgetset
Forms, mainform, tachartlazaruspkg
{ you can add units after this };
Forms, mainform;
{$R *.res}

View File

@ -7,7 +7,7 @@ object Form1: TForm1
ClientHeight = 634
ClientWidth = 877
OnCreate = FormCreate
LCLVersion = '1.7'
LCLVersion = '1.9.0.0'
object Panel1: TPanel
Left = 0
Height = 40
@ -77,7 +77,6 @@ object Form1: TForm1
WorkbookSource = sWorkbookSource1
Align = alClient
AutoAdvance = aaDown
ColCount = 27
DefaultColWidth = 64
DefaultRowHeight = 22
Font.Color = clBlack
@ -85,7 +84,6 @@ object Form1: TForm1
Font.Name = 'Calibri'
Options = [goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine, goRangeSelect, goEditing, goThumbTracking]
ParentFont = False
RowCount = 101
TabOrder = 1
TitleFont.Color = clBlack
TitleFont.Height = -15
@ -110,12 +108,16 @@ object Form1: TForm1
Width = 470
AxisList = <
item
Marks.LabelBrush.Style = bsClear
Minors = <>
Title.LabelFont.Orientation = 900
Title.LabelBrush.Style = bsClear
end
item
Alignment = calBottom
Marks.LabelBrush.Style = bsClear
Minors = <>
Title.LabelBrush.Style = bsClear
end>
BackColor = clWhite
Foot.Brush.Color = clBtnFace
@ -141,12 +143,16 @@ object Form1: TForm1
Width = 470
AxisList = <
item
Marks.LabelBrush.Style = bsClear
Minors = <>
Title.LabelFont.Orientation = 900
Title.LabelBrush.Style = bsClear
end
item
Alignment = calBottom
Marks.LabelBrush.Style = bsClear
Minors = <>
Title.LabelBrush.Style = bsClear
end>
BackColor = clWhite
Depth = 10
@ -179,13 +185,17 @@ object Form1: TForm1
AxisList = <
item
Visible = False
Marks.LabelBrush.Style = bsClear
Minors = <>
Title.LabelFont.Orientation = 900
Title.LabelBrush.Style = bsClear
end
item
Visible = False
Alignment = calBottom
Marks.LabelBrush.Style = bsClear
Minors = <>
Title.LabelBrush.Style = bsClear
end>
Foot.Brush.Color = clBtnFace
Foot.Font.Color = clBlue
@ -210,7 +220,7 @@ object Form1: TForm1
end
object sWorkbookSource1: TsWorkbookSource
AutoDetectFormat = False
FileFormat = sfOOXML
FileFormat = sfUser
Options = []
left = 184
top = 320

View File

@ -7,7 +7,7 @@ interface
uses
Classes, SysUtils, FileUtil, TAGraph, TASeries, Forms, Controls,
Graphics, Dialogs, ExtCtrls, StdCtrls,
fpstypes, fpspreadsheetctrls, fpspreadsheetgrid, fpspreadsheetchart;
fpstypes, fpspreadsheetctrls, fpspreadsheetgrid, fpspreadsheetchart, fpsallformats;
type

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<CONFIG>
<ProjectOptions>
<Version Value="10"/>
<Version Value="11"/>
<General>
<SessionStorage Value="InProjectDir"/>
<MainUnit Value="0"/>
@ -24,9 +24,16 @@
</PublishOptions>
<RunParams>
<local>
<FormatVersion Value="1"/>
<LaunchingApplication PathPlusParams="/usr/X11R6/bin/xterm -T 'Lazarus Run Output' -e $(LazarusDir)/tools/runwait.sh $(TargetCmdLine)"/>
</local>
<FormatVersion Value="2"/>
<Modes Count="1">
<Mode0 Name="default">
<local>
<LaunchingApplication PathPlusParams="/usr/X11R6/bin/xterm -T 'Lazarus Run Output' -e $(LazarusDir)/tools/runwait.sh $(TargetCmdLine)"/>
</local>
</Mode0>
</Modes>
</RunParams>
<RequiredPackages Count="4">
<Item1>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<CONFIG>
<ProjectOptions>
<Version Value="10"/>
<Version Value="11"/>
<PathDelim Value="\"/>
<General>
<SessionStorage Value="InProjectDir"/>
@ -21,9 +21,10 @@
<Version Value="2"/>
</PublishOptions>
<RunParams>
<local>
<FormatVersion Value="1"/>
</local>
<FormatVersion Value="2"/>
<Modes Count="1">
<Mode0 Name="default"/>
</Modes>
</RunParams>
<RequiredPackages Count="1">
<Item1>
@ -51,7 +52,7 @@
<Filename Value="demo_ctrls"/>
</Target>
<SearchPaths>
<IncludeFiles Value="$(ProjOutDir)"/>
<IncludeFiles Value="$(ProjOutDir);..\..\..\source"/>
<OtherUnitFiles Value="..\..\..\source\common;..\..\..\source\visual"/>
<UnitOutputDirectory Value="..\..\lib\$(TargetCPU)-$(TargetOS)"/>
</SearchPaths>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<CONFIG>
<ProjectOptions>
<Version Value="10"/>
<Version Value="11"/>
<PathDelim Value="\"/>
<General>
<SessionStorage Value="InProjectDir"/>
@ -83,9 +83,10 @@
<ExcludeFileFilter Value="*.(bak|ppu|ppw|o|so);*~;backup"/>
</PublishOptions>
<RunParams>
<local>
<FormatVersion Value="1"/>
</local>
<FormatVersion Value="2"/>
<Modes Count="1">
<Mode0 Name="default"/>
</Modes>
</RunParams>
<RequiredPackages Count="3">
<Item1>
@ -124,7 +125,7 @@
<Filename Value="wikitablemaker"/>
</Target>
<SearchPaths>
<IncludeFiles Value="$(ProjOutDir)"/>
<IncludeFiles Value="$(ProjOutDir);..\..\..\source"/>
<OtherUnitFiles Value="..\..\..\source\common;..\..\..\source\visual"/>
<UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
</SearchPaths>

View File

@ -7,7 +7,7 @@ object MainForm: TMainForm
ClientHeight = 476
ClientWidth = 678
OnCreate = FormCreate
LCLVersion = '1.7'
LCLVersion = '1.9.0.0'
object Grid: TsWorksheetGrid
Left = 5
Height = 386
@ -20,12 +20,10 @@ object MainForm: TMainForm
WorkbookSource = Grid.internal
Anchors = [akTop, akLeft, akRight, akBottom]
AutoAdvance = aaDown
ColCount = 27
DefaultColWidth = 64
DefaultRowHeight = 22
MouseWheelOption = mwGrid
Options = [goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine, goRangeSelect, goRowSizing, goColSizing, goEditing, goThumbTracking, goSmoothScroll]
RowCount = 101
TabOrder = 0
OnMouseWheel = GridMouseWheel
end

View File

@ -7,7 +7,7 @@ interface
uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
Spin, Buttons, Types,
fpstypes, fpspreadsheet, fpspreadsheetgrid;
fpstypes, fpspreadsheet, fpspreadsheetgrid, {%H-}fpsAllFormats;
type

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<CONFIG>
<ProjectOptions>
<Version Value="10"/>
<Version Value="11"/>
<PathDelim Value="\"/>
<General>
<SessionStorage Value="InProjectDir"/>
@ -18,9 +18,10 @@
<Version Value="2"/>
</PublishOptions>
<RunParams>
<local>
<FormatVersion Value="1"/>
</local>
<FormatVersion Value="2"/>
<Modes Count="1">
<Mode0 Name="default"/>
</Modes>
</RunParams>
<RequiredPackages Count="2">
<Item1>

View File

@ -6,7 +6,7 @@ interface
uses
Classes, SysUtils, avglvltree,
fpstypes;
fpstypes, fpsExprParser;
type
{ forward declarations }
@ -61,7 +61,8 @@ type
function GetFirst: PsRowCol;
function GetLast: PsRowCol;
procedure InsertRowOrCol(AIndex: Cardinal; IsRow: Boolean);
procedure MoveAlongRow(ARow, AFromCol, AToCol: Cardinal);
procedure MoveAlongCol(ARow, ACol, AToRow: Cardinal);
procedure MoveAlongRow(ARow, ACol, AToCol: Cardinal);
procedure Remove(ARow, ACol: Cardinal); overload;
end;
@ -174,6 +175,32 @@ type
function GetEnumerator: TsCellRangeEnumerator;
end;
{ TsFormulas }
TsFormulaEnumerator = class(TsRowColEnumerator)
protected
function GetCurrent: PsFormula;
public
function GetEnumerator: TsFormulaEnumerator; inline;
property Current: PsFormula read GetCurrent;
end;
TsFormulas = class(TsRowColAVLTree)
protected
procedure DisposeData(var AData: Pointer); override;
function NewData: Pointer; override;
public
function AddFormula(ARow, ACol: Cardinal; AFormula: String = '';
AParsedFormula: TsExpressionParser = nil): PsFormula;
procedure DeleteFormula(ACell: PCell); overload;
procedure DeleteFormula(ARow, ACol: Cardinal); overload;
procedure DeleteRowOrCol(AIndex: Cardinal; IsRow: Boolean);
function FindFormula(ACell: PCell): PsFormula; overload;
function FindFormula(ARow, ACol: Cardinal): PsFormula; overload;
procedure InsertRowOrCol(AIndex: Cardinal; IsRow: Boolean);
// enumerators
function GetEnumerator: TsFormulaEnumerator;
end;
{ TsCellFormatList }
TsCellFormatList = class(TFPList)
private
@ -223,6 +250,146 @@ begin
end;
{ Call-back function for formulas when rows/cols are inserted/deleted }
function FixDeletedCol(AExprNode: TsExprNode; AData: Pointer): Boolean;
var
colIndex: Cardinal;
rng: TsCellRange;
begin
Result := false;
colIndex := PtrInt(AData);
if AExprNode is TsCellExprNode then
begin
if not TsCellExprNode(AExprNode).Has3dLink then
if TsCellExprNode(AExprNode).Col > colIndex then begin
TsCellExprNode(AExprNode).Col := TsCellexprNode(AExprNode).Col - 1;
Result := true;
end else
if TsCellExprNode(AExprNode).Col = colIndex then begin
TsCellExprNode(AExprNode).Error := errIllegalRef;
Result := true;
end;
end else
if AExprNode is TsCellRangeExprNode then
begin
if not TsCellRangeExprNode(AExprNode).Has3dLink then begin
rng := TsCellRangeExprNode(AExprNode).Range;
if (rng.Col1 = colIndex) and (rng.Col2 = colIndex) then begin
TsCellRangeExprNode(AExprNode).Error := errIllegalRef;
Result := true;
end else begin
if rng.Col1 > colIndex then begin
dec(rng.Col1);
Result := true;
end;
if rng.Col2 >= colIndex then begin
dec(rng.Col2);
Result := true;
end;
TsCellRangeExprNode(AExprNode).Range := rng;
end;
end;
end;
end;
function FixDeletedRow(AExprNode: TsExprNode; AData: Pointer): Boolean;
var
rowIndex: Cardinal;
rng: TsCellRange;
begin
Result := false;
rowIndex := PtrInt(AData);
if AExprNode is TsCellExprNode then
begin
if not TsCellExprNode(AExprNode).Has3dLink then
if TsCellExprNode(AExprNode).Row > rowIndex then begin
TsCellExprNode(AExprNode).Row := TsCellExprNode(AExprNode).Row - 1;
Result := true;
end else
if TsCellExprNode(AExprNode).Row = rowIndex then begin
TsCelLExprNode(AExprNode).Error := errIllegalRef;
Result := true;
end;
end else
if AExprNode is TsCellRangeExprNode then
begin
if not TsCellRangeExprNode(AExprNode).Has3dLink then begin
rng := TsCellRangeExprNode(AExprNode).Range;
if (rng.Row1 = rowIndex) and (rng.Row2 = rowIndex) then begin
TsCellRangeExprNode(AExprNode).Error := errIllegalRef;
Result := true;
end else
begin
if rng.Row1 > rowIndex then begin
dec(rng.Row1);
Result := true;
end;
if rng.Row2 >= rowIndex then begin
dec(rng.Row2);
Result := true;
end;
TsCellRangeExprNode(AExprNode).Range := rng;
end;
end;
end;
end;
function FixInsertedCol(AExprNode: TsExprNode; AData: Pointer): Boolean;
var
colIndex: Cardinal;
rng: TsCellRange;
begin
Result := false;
colIndex := PtrInt(AData);
if AExprNode is TsCellExprNode then
begin
if not TsCellExprNode(AExprNode).Has3dLink then
if TsCellExprNode(AExprNode).Col >= colIndex then begin
TsCellExprNode(AExprNode).Col := TsCellexprNode(AExprNode).Col + 1;
Result := true;
end;
end else
if AExprNode is TsCellRangeExprNode then
begin
if not TsCellRangeExprNode(AExprNode).Has3dLink then begin
rng := TsCellRangeExprNode(AExprNode).Range;
if rng.Col1 >= colIndex then inc(rng.Col1);
if rng.Col2 >= colIndex then inc(rng.Col2);
TsCellRangeExprNode(AExprNode).Range := rng;
Result := true;
end;
end;
end;
function FixInsertedRow(AExprNode: TsExprNode; AData: Pointer): Boolean;
var
rowIndex: Cardinal;
rng: TsCellRange;
begin
Result := false;
rowIndex := PtrInt(AData);
if AExprNode is TsCellExprNode then
begin
if not TsCellexprNode(AExprNode).Has3dLink then
if TsCellExprNode(AExprNode).Row >= rowIndex then begin
TsCellExprNode(AExprNode).Row := TsCellExprNode(AExprNode).Row + 1;
Result := true;
end;
end else
if AExprNode is TsCellRangeExprNode then
begin
if not TsCellRangeExprNode(AExprNode).Has3dLink then begin
rng := TsCellRangeExprNode(AExprNode).Range;
if rng.Row1 >= rowIndex then inc(rng.Row1);
if rng.Row2 >= rowIndex then inc(rng.Row2);
TsCellRangeExprNode(AExprNode).Range := rng;
Result := true;
end;
end;
end;
{==============================================================================}
{ TsRowColEnumerator }
{ A specialized enumerator for TsRowColAVLTree using the pointers to the data }
@ -608,20 +775,64 @@ begin
end;
end;
{@@ ----------------------------------------------------------------------------
This method moves the cell in the specified column (ACol) and at row AFromRow
along the row before the row with index AToRow.
-------------------------------------------------------------------------------}
procedure TsRowColAVLTree.MoveAlongCol(ARow, ACol, AToRow: Cardinal);
var
r: Cardinal;
node: TAvgLvlTreeNode;
item: PsRowCol;
begin
if ARow = AToRow then
exit;
if ARow < AToRow then
begin
node := FindLowest;
while Assigned(node) do
begin
item := PsRowCol(node.Data);
if item^.Col = ACol then break;
node := FindSuccessor(node);
end;
r := ARow;
while r < AToRow do begin
Exchange(r, ACol, r+1, ACol);
inc(r);
end;
end else
begin
node:= FindHighest;
while Assigned(node) do
begin
item := PsRowCol(node.Data);
if item^.Col = ACol then break;
node := FindPrecessor(node);
end;
r := ARow;
while r > AToRow do begin
Exchange(r, ACol, r-1, ACol);
dec(r);
end;
end;
end;
{@@ ----------------------------------------------------------------------------
This method moves the cell in the specified row (ARow) and at column AFromCol
along the row before the column with index AToCol.
-------------------------------------------------------------------------------}
procedure TsRowColAVLTree.MoveAlongRow(ARow, AFromCol, AToCol: Cardinal);
procedure TsRowColAVLTree.MoveAlongRow(ARow, ACol, AToCol: Cardinal);
var
c: Cardinal;
node: TAvgLvlTreeNode;
item: PsRowCol;
begin
if AFromCol = AToCol then
if ACol = AToCol then
exit;
if AFromCol < AToCol then
if ACol < AToCol then
begin
node := FindLowest;
while Assigned(node) do
@ -631,7 +842,7 @@ begin
if item^.Row = ARow then break;
node := FindSuccessor(node);
end;
c := AFromCol;
c := ACol;
while c < AToCol do begin
Exchange(ARow, c, ARow, c+1);
inc(c);
@ -646,7 +857,7 @@ begin
if item^.Row = ARow then break;
node := FindPrecessor(node);
end;
c := AFromCol;
c := ACol;
while c > AToCol do begin
Exchange(ARow, c, ARow, c-1);
dec(c);
@ -926,16 +1137,16 @@ end;
{ TsHyperlinkEnumerator: enumerator for the TsHyperlinks AVLTree }
{==============================================================================}
function TsHyperlinkEnumerator.GetEnumerator: TsHyperlinkEnumerator;
begin
Result := self;
end;
function TsHyperlinkEnumerator.GetCurrent: PsHyperlink;
begin
Result := PsHyperlink(inherited GetCurrent);
end;
function TsHyperlinkEnumerator.GetEnumerator: TsHyperlinkEnumerator;
begin
Result := self;
end;
{==============================================================================}
{ TsHyperlinks: an AVLTree to store hyperlink records for cells }
@ -1186,6 +1397,151 @@ begin
end;
{==============================================================================}
{ TsFormulaEnumerator }
{==============================================================================}
function TsFormulaEnumerator.GetCurrent: PsFormula;
begin
Result := PsFormula(inherited GetCurrent);
end;
function TsFormulaEnumerator.GetEnumerator: TsFormulaEnumerator;
begin
Result := self;
end;
{==============================================================================}
{ TsFormulas }
{==============================================================================}
function TsFormulas.AddFormula(ARow, ACol: Cardinal; AFormula: String = '';
AParsedFormula: TsExpressionParser = nil): PsFormula;
begin
Result := PsFormula(FindByRowCol(ARow, ACol));
if Result = nil then
Result := PsFormula(Add(ARow, ACol));
Result^.Text := AFormula; // unparsed formula
Result^.Parser := AParsedFormula; // if nil, will be parsed on next calculation
Result^.CalcState := csNotCalculated;
end;
procedure TsFormulas.DeleteFormula(ACell: PCell);
begin
if ACell <> nil then
Delete(ACell^.Row, ACell^.Col);
end;
procedure TsFormulas.DeleteFormula(ARow, ACol: Cardinal);
begin
Delete(ARow, ACol); // will release memory automatically
end;
procedure TsFormulas.DeleteRowOrCol(AIndex: Cardinal; IsRow: Boolean);
var
node, nextnode: TAvgLvlTreeNode;
formula: PsFormula;
changed: Boolean;
begin
node := FindLowest;
while Assigned(node) do
begin
changed := false;
nextnode := FindSuccessor(node);
formula := PsFormula(node.Data);
if IsRow then
begin
// Remove and destroy the formula record if it is in the deleted row
if formula^.Row = AIndex then
Delete(node)
else
if formula^.Row > AIndex then
dec(formula^.Row);
// Update all RowCol records at row indexes above the deleted row
changed := formula^.Parser.IterateNodes(@FixDeletedRow, Pointer(PtrInt(AIndex)));
end else
begin
// Remove and destroy the formula record if it is in the deleted column
if formula^.Col = AIndex then
Delete(node)
else begin
if formula^.Col > AIndex then
dec(formula^.Col);
// Update all RowCol records at column indexes above the deleted column
changed := formula^.Parser.IterateNodes(@FixDeletedCol, Pointer(PtrInt(AIndex)));
end;
end;
// Recreate the formula if required.
if changed then
formula^.Text := formula^.Parser.Expression;
node := nextnode;
end;
end;
procedure TsFormulas.DisposeData(var AData: Pointer);
begin
if AData <> nil then begin
PsFormula(AData)^.Text := '';
PsFormula(AData)^.Parser.Free;
Dispose(PsFormula(AData));
end;
AData := nil;
end;
function TsFormulas.FindFormula(ACell: PCell): PsFormula;
begin
if ACell <> nil then
Result := PsFormula(FindbyRowCol(ACell^.Row, ACell^.Col))
else
Result := nil;
end;
function TsFormulas.FindFormula(ARow, ACol: Cardinal): PsFormula;
begin
Result := PsFormula(FindByRowCol(ARow, ACol));
end;
// Formula enumerators (use in "for ... in" syntax)
function TsFormulas.GetEnumerator: TsFormulaEnumerator;
begin
Result := TsFormulaEnumerator.Create(self, 0, 0, $7FFFFFFF, $7FFFFFFF, false);
end;
procedure TsFormulas.InsertRowOrCol(AIndex: Cardinal; IsRow: Boolean);
var
node: TAvgLvlTreeNode;
formula: PsFormula;
changed: Boolean;
begin
node := FindLowest;
while Assigned(node) do begin
formula := PsFormula(node.Data);
if IsRow then
begin
if formula^.Row >= AIndex then inc(formula^.Row);
changed := formula^.Parser.IterateNodes(@FixInsertedRow, Pointer(PtrInt(AIndex)));
end else
begin
if formula^.Col >= AIndex then inc(formula^.Col);
changed := formula^.Parser.IterateNodes(@FixInsertedCol, Pointer(PtrInt(AIndex)));
end;
if changed then
formula^.Text := formula^.Parser.Expression;
node := FindSuccessor(node);
end;
end;
function TsFormulas.NewData: Pointer;
var
f: PsFormula;
begin
New(f);
f^.Text := '';
f^.Parser := nil;
f^.CalcState := csNotCalculated;
Result := f;
end;
{==============================================================================}
{ TsCellFormatList }
{==============================================================================}

View File

@ -45,7 +45,7 @@
// Keep spaces in formula
{$mode objfpc}
{$h+}
{$H+}
unit fpsExprParser;
interface
@ -78,8 +78,10 @@ const
];
type
// Forward declarations
TsExpressionParser = class;
TsBuiltInExpressionManager = class;
TsExprNode = class;
TsResultType = (rtEmpty, rtBoolean, rtInteger, rtFloat, rtDateTime, rtString,
rtCell, rtCellRange, rtHyperlink, rtError, rtMissingArg, rtAny);
@ -104,6 +106,10 @@ type
PsExpressionResult = ^TsExpressionResult;
TsExprParameterArray = array of TsExpressionResult;
{ Function executed when iterating through all nodes (Parser.IterateNodes).
The function returns true if the text formula has to be rebuilt. }
TsExprNodeFunc = function(ANode: TsExprNode; AData: Pointer): Boolean;
{ TsExprNode }
TsExprNode = class(TObject)
private
@ -116,6 +122,7 @@ type
function AsString: string; virtual; abstract;
procedure Check; virtual; //abstract;
function Has3DLink: Boolean; virtual;
function IterateNodes(AFunc: TsExprNodeFunc; AData: Pointer): Boolean; virtual;
function NodeType: TsResultType; virtual; abstract;
function NodeValue: TsExpressionResult;
property Parser: TsExpressionParser read FParser;
@ -134,6 +141,7 @@ type
constructor Create(AParser: TsExpressionParser; ALeft, ARight: TsExprNode);
destructor Destroy; override;
function Has3DLink: Boolean; override;
function IterateNodes(AFunc: TsExprNodeFunc; AData: Pointer): boolean; override;
property Left: TsExprNode read FLeft;
property Right: TsExprNode read FRight;
end;
@ -555,6 +563,7 @@ type
function AsString: String; override;
procedure Check; override;
function Has3DLink: Boolean; override;
function IterateNodes(AFunc: TsExprNodeFunc; AData: Pointer): Boolean; override;
property ArgumentNodes: TsExprArgumentArray read FArgumentNodes;
property ArgumentParams: TsExprParameterArray read FArgumentParams;
end;
@ -592,12 +601,11 @@ type
FCell: PCell;
FSheetName: String;
FIsRef: Boolean;
FError: TsErrorValue;
protected
function GetCol: Cardinal;
function GetRow: Cardinal;
function GetSheet: TsBasicWorksheet;
function GetSheetIndex: Integer;
function GetSheetName: String;
function GetWorkbook: TsBasicWorkbook;
procedure GetNodeValue(out AResult: TsExpressionResult); override;
public
@ -606,8 +614,14 @@ type
function AsRPNItem(ANext: PRPNItem): PRPNItem; override;
function AsString: string; override;
procedure Check; override;
function GetSheetIndex: Integer;
function GetSheetName: String;
function Has3DLink: Boolean; override;
function IterateNodes(AFunc: TsExprNodeFunc; AData: Pointer): Boolean; override;
function NodeType: TsResultType; override;
property Col: Cardinal read FCol write FCol; // Be careful when modifying Col and Row
property Row: Cardinal read FRow write FRow;
property Error: TsErrorValue read FError write FError;
property Worksheet: TsBasicWorksheet read FWorksheet;
end;
@ -622,10 +636,13 @@ type
FSheetIndex: array[TsCellRangeIndex] of Integer;
FFlags: TsRelFlags;
F3dRange: Boolean;
FError: TsErrorValue;
function GetRange: TsCellRange;
procedure SetRange(const ARange: TsCellRange);
protected
function GetCol(AIndex: TsCellRangeIndex): Cardinal;
function GetRow(AIndex: TsCellRangeIndex): Cardinal;
procedure GetNodeValue(out Result: TsExpressionResult); override;
procedure GetNodeValue(out AResult: TsExpressionResult); override;
function GetWorkbook: TsBasicWorkbook;
public
constructor Create(AParser: TsExpressionParser; AWorksheet: TsBasicWorksheet;
@ -633,8 +650,12 @@ type
function AsRPNItem(ANext: PRPNItem): PRPNItem; override;
function AsString: String; override;
procedure Check; override;
function GetSheetIndex(AIndex: TsCellRangeIndex): Integer;
function Has3DLink: Boolean; override;
function IterateNodes(AFunc: TsExprNodeFunc; AData: Pointer): Boolean; override;
function NodeType: TsResultType; override;
property Error: TsErrorValue read FError write FError;
property Range: TsCellRange read GetRange write SetRange; // Be careful!
property Workbook: TsBasicWorkbook read GetWorkbook;
property Worksheet: TsBasicWorksheet read FWorksheet;
end;
@ -744,8 +765,8 @@ type
function TokenType: TsTokenType;
procedure CreateHashList;
property Scanner: TsExpressionScanner read FScanner;
property ExprNode: TsExprNode read FExprNode;
property Dirty: Boolean read FDirty;
property ExprNode: TsExprNode read FExprNode;
public
constructor Create(AWorksheet: TsBasicWorksheet); virtual;
@ -756,6 +777,7 @@ type
function Evaluate: TsExpressionResult;
procedure EvaluateExpression(out AResult: TsExpressionResult);
function Has3DLinks: Boolean;
function IterateNodes(AFunc: TsExprNodeFunc; AData: Pointer): boolean;
procedure PrepareCopyMode(ASourceCell, ADestCell: PCell);
function ResultType: TsResultType;
@ -820,6 +842,16 @@ type
property Identifiers[AIndex: Integer]: TsBuiltInExprIdentifierDef read GetI;
end;
{ TsFormula }
TsFormula = record
Row, Col: Cardinal;
Text: String;
Parser: TsExpressionParser;
CalcState: TsCalcState;
end;
PsFormula = ^TsFormula;
{ Exception classes }
EExprParser = class(Exception);
ECalcEngine = class(Exception);
@ -1494,8 +1526,10 @@ end;
procedure TsExpressionParser.EvaluateExpression(out AResult: TsExpressionResult);
begin
{ // Not needed. May be missing after copying formulas
if (FExpression = '') then
ParserError(rsExpressionEmpty);
}
if not Assigned(FExprNode) then
ParserError(rsErrorInExpression);
FExprNode.GetNodeValue(AResult);
@ -1941,6 +1975,12 @@ begin
Result := FExprNode.Has3DLink;
end;
function TsExpressionParser.IterateNodes(AFunc: TsExprNodeFunc;
AData: Pointer): Boolean;
begin
Result := FExprNode.IterateNodes(AFunc, AData);
end;
procedure TsExpressionParser.SetDialect(const AValue: TsFormulaDialect);
begin
if FDialect = AValue then exit;
@ -2723,6 +2763,12 @@ begin
Result := false;
end;
function TsExprNode.IterateNodes(AFunc: TsExprNodeFunc; AData: Pointer): Boolean;
begin
Unused(AFunc, AData);
// to be overridden by descendant classes
end;
function TsExprNode.NodeValue: TsExpressionResult;
begin
GetNodeValue(Result);
@ -2773,6 +2819,12 @@ begin
Result := FLeft.Has3DLink or FRight.Has3DLink;
end;
function TsBinaryOperationExprNode.IterateNodes(AFunc: TsExprNodeFunc;
AData: Pointer): Boolean;
begin
Result := FLeft.IterateNodes(AFunc, AData) or FRight.IterateNodes(AFunc, AData);
end;
function TsBinaryOperationExprNode.HasError(out AResult: TsExpressionResult): Boolean;
begin
Result := Left.HasError(AResult) or Right.HasError(AResult);
@ -3724,6 +3776,16 @@ begin
Result := false;
end;
function TsFunctionExprNode.IterateNodes(AFunc: TsExprNodeFunc;
AData: Pointer): Boolean;
var
i: Integer;
begin
Result := false;
for i:=0 to High(FArgumentParams) do
Result := Result or FArgumentNodes[i].IterateNodes(AFunc, AData);
end;
{ TsFunctionCallBackExprNode }
@ -3773,12 +3835,16 @@ begin
FRow := ARow;
FCol := ACol;
FFlags := AFlags;
FError := errOK;
FCell := (GetSheet as TsWorksheet).FindCell(FRow, FCol);
if Has3DLink then FParser.FContains3DRef := true;
end;
function TsCellExprNode.AsRPNItem(ANext: PRPNItem): PRPNItem;
begin
if FError <> errOK then
Result := RPNErr(FError, ANext)
else
if FIsRef then
begin
if Has3dLink then
@ -3798,6 +3864,11 @@ function TsCellExprNode.AsString: string;
var
r, c: Cardinal;
begin
if FError <> errOK then begin
Result := GetErrorValueStr(FError);
exit;
end;
r := Getrow;
c := GetCol;
if Has3dLink then
@ -3843,6 +3914,46 @@ begin
Result := FCol - FParser.FSourceCell^.Col + FParser.FDestCell^.Col;
end;
procedure TsCellExprNode.GetNodeValue(out AResult: TsExpressionResult);
var
cell: PCell;
formula: PsFormula;
sheet: TsWorksheet;
begin
if FError <> errOK then begin
AResult.ResultType := rtError;
AResult.ResError := FError;
{
AResult.ResRow := GetRow;
AResult.ResCol := GetCol;
AResult.Worksheet := GetSheet;
}
exit;
end;
if Parser.CopyMode then
cell := (FWorksheet as TsWorksheet).FindCell(GetRow, GetCol)
else
cell := FCell;
if (cell <> nil) and HasFormula(cell) then begin
sheet := TsWorksheet(cell^.Worksheet);
formula := sheet.Formulas.FindFormula(cell^.Row, cell^.Col);
case formula^.CalcState of
csNotCalculated:
sheet.CalcFormula(formula);
csCalculating:
raise ECalcEngine.CreateFmt(rsCircularReference, [GetCellString(cell^.Row, cell^.Col)]);
end;
end;
AResult.ResultType := rtCell;
AResult.ResRow := GetRow;
AResult.ResCol := GetCol;
AResult.Worksheet := GetSheet;
end;
(*
procedure TsCellExprNode.GetNodeValue(out AResult: TsExpressionResult);
var
cell: PCell;
@ -3865,6 +3976,7 @@ begin
AResult.ResCol := GetCol;
AResult.Worksheet := GetSheet;
end;
*)
{ See: GetCol }
function TsCellExprNode.GetRow: Cardinal;
@ -3878,8 +3990,10 @@ function TsCellExprNode.GetSheet: TsBasicWorksheet;
begin
if FSheetName = '' then
Result := FWorksheet
else
else begin
Result := (GetWorkbook as TsWorkbook).GetWorksheetByName(FSheetName);
if Result = nil then FError := errIllegalREF;
end;
end;
function TsCellExprNode.GetSheetIndex: Integer;
@ -3916,6 +4030,12 @@ begin
Result := rtCell;
end;
function TsCellExprNode.IterateNodes(AFunc: TsExprNodeFunc;
AData: Pointer): Boolean;
begin
Result := AFunc(self, AData);
end;
{ TsCellRangeExprNode }
@ -3932,15 +4052,25 @@ begin
FParser := AParser;
FWorksheet := AWorksheet;
FFlags := [];
FError := errOK;
book := TsWorkbook(GetWorkbook);
F3dRange := ((ASheet1 <> '') and (ASheet2 <> '') { and (ASheet1 <> ASheet2)}) or
((ASheet1 <> '') and (ASheet2 = ''));
FSheetIndex[1] := book.GetWorksheetIndex(ASheet1);
if ASheet2 <> '' then
FSheetIndex[2] := book.GetWorksheetIndex(ASheet2)
{
if FSheetIndex[1] = -1 then
FError := errIllegalREF
else
}
if ASheet2 <> '' then begin
FSheetIndex[2] := book.GetWorksheetIndex(ASheet2);
{
if FSheetIndex[2] = -1 then
FError := errIllegalREF;
}
end else
FSheetIndex[2] := FSheetIndex[1];
EnsureOrder(FSheetIndex[1], FSheetIndex[2]);
@ -3982,6 +4112,9 @@ end;
function TsCellRangeExprNode.AsRPNItem(ANext: PRPNItem): PRPNItem;
begin
if FError <> errOK then
Result := RPNErr(FError, ANext)
else
if F3dRange then
Result := RPNCellRange3D(
FSheetIndex[1], GetRow(1), Integer(GetCol(1)),
@ -4001,6 +4134,11 @@ var
r1, c1, r2, c2: Cardinal;
s1, s2: String;
begin
if FError <> errOK then begin
Result := GetErrorValueStr(FError);
exit;
end;
if FSheetIndex[1] = -1 then
s1 := FWorksheet.Name
else
@ -4058,7 +4196,7 @@ begin
Result := FCol[AIndex] - FParser.FSourceCell^.Col + FParser.FDestCell^.Col;
end;
procedure TsCellRangeExprNode.GetNodeValue(out Result: TsExpressionResult);
procedure TsCellRangeExprNode.GetNodeValue(out AResult: TsExpressionResult);
var
r, c, s: Array[TsCellRangeIndex] of Integer;
rr, cc, ss: Integer;
@ -4066,7 +4204,14 @@ var
cell: PCell;
book: TsWorkbook;
sheet: TsWorksheet;
formula: PsFormula;
begin
if FError <> errOK then begin
AResult.ResultType := rtError;
AResult.ResError := FError;
exit;
end;
for i in TsCellRangeIndex do
begin
r[i] := GetRow(i);
@ -4081,29 +4226,35 @@ begin
for ss := s[1] to s[2] do begin
sheet := (Workbook as TsWorkbook).GetWorksheetByIndex(ss);
for rr := r[1] to r[2] do
for cc := c[1] to c[2] do
begin
cell := sheet.FindCell(rr, cc);
if HasFormula(cell) then
case sheet.GetCalcState(cell) of
csNotCalculated:
sheet.CalcFormula(cell);
csCalculating:
raise ECalcEngine.Create(rsCircularReference);
end;
end;
for formula in sheet.Formulas do
if (formula^.Row >= r[1]) and (formula^.Row <= r[2]) and
(formula^.Col >= c[1]) and (formula^.Col <= c[2])
then
case formula^.CalcState of
csNotCalculated:
sheet.CalcFormula(formula);
csCalculating:
raise ECalcEngine.Create(rsCircularReference);
end;
end;
Result.ResultType := rtCellRange;
Result.ResCellRange.Row1 := r[1];
Result.ResCellRange.Col1 := c[1];
Result.ResCellRange.Row2 := r[2];
Result.ResCellRange.Col2 := c[2];
Result.ResCellRange.Sheet1 := s[1];
Result.ResCellRange.Sheet2 := s[2];
Result.Worksheet := FWorksheet;
// Result.Worksheet2 := FWorksheet2;
AResult.ResultType := rtCellRange;
AResult.ResCellRange.Row1 := r[1];
AResult.ResCellRange.Col1 := c[1];
AResult.ResCellRange.Row2 := r[2];
AResult.ResCellRange.Col2 := c[2];
AResult.ResCellRange.Sheet1 := s[1];
AResult.ResCellRange.Sheet2 := s[2];
AResult.Worksheet := FWorksheet;
end;
// Be careful when modifying GetRange - it may break everything
function TsCellRangeExprNode.GetRange: TsCellRange;
begin
Result.Row1 := FRow[1];
Result.Col1 := FCol[1];
Result.Row2 := FRow[2];
Result.Col2 := FCol[2];
end;
function TsCellRangeExprNode.GetRow(AIndex: TsCellRangeIndex): Cardinal;
@ -4115,7 +4266,15 @@ end;
function TsCellRangeExprNode.GetWorkbook: TsBasicWorkbook;
begin
Result := (FWorksheet as TsWorksheet).Workbook;
if FWorksheet = nil then
Result := nil
else
Result := (FWorksheet as TsWorksheet).Workbook;
end;
function TsCellRangeExprNode.GetSheetIndex(AIndex: TsCellRangeIndex): Integer;
begin
Result := FSheetIndex[AIndex];
end;
function TsCellRangeExprNode.Has3DLink: Boolean;
@ -4123,11 +4282,25 @@ begin
Result := F3dRange;
end;
function TsCellRangeExprNode.IterateNodes(AFunc: TsExprNodeFunc;
AData: Pointer): Boolean;
begin
Result := AFunc(self, AData);
end;
function TsCellRangeExprNode.NodeType: TsResultType;
begin
Result := rtCellRange;
end;
procedure TsCellRangeExprNode.SetRange(const ARange: TsCellRange);
begin
FRow[1] := ARange.Row1;
FCol[1] := ARange.Col1;
FRow[2] := ARange.Row2;
FCol[2] := ARange.Col2;
end;
{------------------------------------------------------------------------------}
{ Conversion of arguments to simple data types }

View File

@ -112,7 +112,7 @@ type
function FindNumFormatByName(ANumFmtName: String): Integer;
function FindRowStyleByName(AStyleName: String): Integer;
function FindTableStyleByName(AStyleName: String): Integer;
procedure FixFormulas;
// procedure FixFormulas;
procedure ReadCell(ANode: TDOMNode; ARow, ACol: Integer;
AFormatIndex: Integer; out AColsRepeated: Integer);
procedure ReadColumns(ATableNode: TDOMNode);
@ -134,6 +134,7 @@ type
procedure ReadRowStyle(AStyleNode: TDOMNode);
procedure ReadShapes(ATableNode: TDOMNode);
procedure ReadSheetProtection(ANode: TDOMNode; ASheet: TsBasicWorksheet);
procedure ReadSheets(ANode: TDOMNode);
procedure ReadTableStyle(AStyleNode: TDOMNode);
protected
@ -1435,7 +1436,7 @@ begin
exit;
Result := -1;
end;
(*
procedure TsSpreadOpenDocReader.FixFormulas;
procedure FixCell(ACell: PCell);
@ -1480,7 +1481,7 @@ begin
for cell in sheet.Cells do
if HasFormula(cell) then FixCell(cell);
end;
end;
end; *)
procedure TsSpreadOpenDocReader.ReadAutomaticStyles(AStylesNode: TDOMNode);
var
@ -2387,7 +2388,8 @@ procedure TsSpreadOpenDocReader.ReadFormula(ARow, ACol: Cardinal;
AStyleIndex: Integer; ACellNode: TDOMNode);
var
cell: PCell;
formula: String;
formula: PsFormula;
formulaStr: String;
// stylename: String;
floatValue: Double;
boolValue: Boolean;
@ -2418,31 +2420,38 @@ begin
}
fmt := TsWorkbook(Workbook).GetPointerToCellFormat(cell^.FormatIndex);
formula := '';
formulaStr := '';
if (boReadFormulas in FWorkbook.Options) then
begin
// Read formula, trim it, ...
formula := GetAttrValue(ACellNode, 'table:formula');
if formula <> '' then
formulaStr := GetAttrValue(ACellNode, 'table:formula');
if formulaStr <> '' then
begin
// Formulas written by Spread begin with 'of:=', by Excel with 'msof:='.
// Remove that. And both use different list separators.
p := pos('=', formula);
ns := Copy(formula, 1, p-2);
p := pos('=', formulaStr);
ns := Copy(formulaStr, 1, p-2);
case ns of
'of' : FPointSeparatorSettings.ListSeparator := ';';
'msoxl': FPointSeparatorSettings.ListSeparator := ',';
end;
Delete(formula, 1, p);
Delete(formulaStr, 1, p);
end;
// ... and store in cell's FormulaValue field.
formula := TsWorksheet(FWorksheet).Formulas.AddFormula(ARow, ACol);
formula^.Parser := TsSpreadsheetParser.Create(FWorksheet);
formula^.Parser.Dialect := fdOpenDocument; // Parse in ODS dialect
formula^.Parser.Expression := formulaStr;
formula^.Parser.Dialect := fdExcelA1; // Convert formula to Excel A1 dialect
formula^.Text := formula^.Parser.Expression;
{
cell^.FormulaValue := formula;
// Note: This formula is still in OpenDocument dialect. Conversion to
// ExcelA1 dialect (used by fps) is postponed until all sheets have beeon
// read (--> FixFormulas) because of possible references to other sheets
// which might not have been loaded yet at this moment.
}
{$IFDEF FPSpreadDebug}
DebugLn(' Formula found: ' + formula);
{$ENDIF}
@ -2587,6 +2596,7 @@ begin
if not Assigned(SpreadSheetNode) then
raise EFPSpreadsheet.Create('[TsSpreadOpenDocReader.ReadFromStream] Node "office:spreadsheet" not found.');
ReadSheets(SpreadsheetNode);
ReadDocumentProtection(SpreadsheetNode);
ReadDateMode(SpreadSheetNode);
@ -2613,7 +2623,8 @@ begin
end;
sheetName := GetAttrValue(TableNode, 'table:name');
FWorkSheet := TsWorkbook(FWorkbook).AddWorksheet(sheetName, true);
FWorksheet := TsWorkbook(FWorkbook).GetWorksheetByName(sheetName);
// FWorkSheet := TsWorkbook(FWorkbook).AddWorksheet(sheetName, true);
tablestyleName := GetAttrValue(TableNode, 'table:style-name');
// Read protection
ReadSheetProtection(TableNode, FWorksheet);
@ -2660,7 +2671,7 @@ begin
end;
// Convert formulas from OpenDocument to ExcelA1 dialect
FixFormulas;
// FixFormulas;
// Active sheet
if FActiveSheet <> '' then
@ -4085,6 +4096,24 @@ begin
(ASheet as TsWorksheet).Protect(false);
end;
procedure TsSpreadOpenDocReader.ReadSheets(ANode: TDOMNode);
var
nodename: String;
sheetName: String;
begin
ANode := ANode.FirstChild;
while ANode <> nil do begin
nodeName := ANode.NodeName;
if nodeName = 'table:table' then begin
sheetName := GetAttrValue(ANode, 'table:name');
if sheetName <> '' then
// Create worksheet immediately because it may be needed for 3d formulas
(FWorkbook as TsWorkbook).AddWorksheet(sheetname, true);
end;
ANode := ANode.NextSibling;
end;
end;
procedure TsSpreadOpenDocReader.ReadStyles(AStylesNode: TDOMNode);
var
styleNode: TDOMNode;
@ -7564,8 +7593,9 @@ procedure TsSpreadOpenDocWriter.WriteFormula(AStream: TStream; const ARow,
ACol: Cardinal; ACell: PCell);
var
lStyle: String = '';
formula: PsFormula;
formulaStr: String;
parser: TsExpressionParser;
formula: String;
valuetype: String;
value: string;
valueStr: String;
@ -7577,6 +7607,7 @@ var
fmt: TsCellFormat;
ignoreFormulas: Boolean;
sheet: TsWorksheet;
oldDialect: TsFormulaDialect;
begin
Unused(ARow, ACol);
ignoreFormulas := (boIgnoreFormulas in FWorkbook.Options);
@ -7608,16 +7639,32 @@ begin
FWorkbook.AddErrorMsg(rsODSHyperlinksOfTextCellsOnly, [GetCellString(ARow, ACol)]);
// Formula string
formula := sheet.Formulas.FindFormula(ACell);
if ignoreFormulas then begin
formula := ACell^.FormulaValue;
if (formula <> '') then begin
if not ((pos('of:=', formula) = 1) or (pos('=', formula) = 1)) then
formula := 'of:=' + formula;
formulaStr := formula^.Text;
if (formulaStr <> '') then begin
if not ((pos('of:=', formulaStr) = 1) or (pos('=', formulaStr) = 1)) then
formulaStr := 'of:=' + formulaStr;
end;
end else
begin
valueStr := '';
// Convert string formula to the format needed by ods: semicolon list separators!
if formula^.Parser = nil then begin
formula^.Parser := TsSpreadsheetParser.Create(FWorksheet);
formula^.Parser.Expression := formula^.Text;
end;
// Convert string formula to the format needed by ods
oldDialect := formula^.Parser.Dialect;
try
formula^.Parser.Dialect := fdOpenDocument;
formulaStr := formula^.Parser.Expression; // Formula converted to ODS dialect
if (formulaStr <> '') and (formulastr[1] <> '=') then
formulaStr := '=' + formulaStr;
finally
formula^.Parser.Dialect := oldDialect;
end;
{
parser := TsSpreadsheetParser.Create(FWorksheet);
try
parser.Expression := ACell^.FormulaValue; // Formula still in Excel dialect
@ -7628,7 +7675,7 @@ begin
finally
parser.Free;
end;
}
case ACell^.ContentType of
cctNumber:
begin
@ -7675,23 +7722,24 @@ begin
end;
{ Fix special xml characters }
formula := UTF8TextToXMLText(formula);
formulaStr := UTF8TextToXMLText(formulaStr);
{ We are writing a very rudimentary formula here without result and result
data type. Seems to work... }
if not ignoreFormulas or (sheet.GetCalcState(ACell) = csCalculated) then
// if not ignoreFormulas or (sheet.GetCalcState(ACell) = csCalculated) then
if not ignoreFormulas or (formula^.CalcState = csCalculated) then // LOOKS STRANGE - IS THIS CORRECT?
AppendToStream(AStream, Format(
'<table:table-cell table:formula="%s" office:value-type="%s"%s%s%s>' +
comment +
valueStr +
'</table:table-cell>', [
formula, valuetype, value, lStyle, spannedStr
formulaStr, valuetype, value, lStyle, spannedStr
]))
else
begin
AppendToStream(AStream, Format(
'<table:table-cell table:formula="%s"%s%s', [
formula, lStyle, spannedStr]));
formulaStr, lStyle, spannedStr]));
if comment <> '' then
AppendToStream(AStream, '>' + comment + '</table:table-cell>')
else

File diff suppressed because it is too large Load Diff

View File

@ -205,6 +205,9 @@ const
SHEETSEPARATOR = '!';
type
TsFormulaFlag = (ffCalculating, ffCalculated);
TsFormulaFlags = set of TsFormulaFlag;
{@@ Elements of an expanded formula.
Note: If ElementKind is fekCellOffset, "Row" and "Col" have to be cast to signed integers! }
TsFormulaElement = record
@ -589,8 +592,8 @@ type
TsCalcState = (csNotCalculated, csCalculating, csCalculated);
{@@ Cell flag }
TsCellFlag = (cfCalculating, cfCalculated, cfHasComment, cfHyperlink, cfMerged,
cf3dFormula);
TsCellFlag = ({cfCalculating, cfCalculated, }cfHasComment, cfHyperlink, cfMerged,
cfHasFormula, cf3dFormula);
{@@ Set of cell flags }
TsCellFlags = set of TsCellFlag;
@ -744,7 +747,7 @@ type
{ Cell content }
UTF8StringValue: String; // Strings cannot be part of a variant record
RichTextParams: TsRichTextParams; // Formatting of individual text ranges
FormulaValue: String; // Formula for calculation of cell content
// FormulaValue: String; // Formula for calculation of cell content
case ContentType: TCellContentType of // variant part must be at the end
cctEmpty : (); // has no data at all
cctFormula : (); // FormulaValue is outside the variant record

View File

@ -202,8 +202,9 @@ procedure InitImageRecord(out AValue: TsImage; ARow, ACol: Cardinal;
AOffsetX, AOffsetY, AScaleX, AScaleY: Double);
procedure InitHeaderFooterImageRecord(out AImage: TsHeaderFooterImage);
procedure CopyCellValue(AFromCell, AToCell: PCell);
//procedure CopyCellValue(AFromCell, AToCell: PCell);
function HasFormula(ACell: PCell): Boolean;
function Has3dFormula(ACell: PCell): Boolean;
function SameCellBorders(AFormat1, AFormat2: PsCellFormat): Boolean;
function SameFont(AFont1, AFont2: TsFont): Boolean; overload;
function SameFont(AFont: TsFont; AFontName: String; AFontSize: Single;
@ -2334,7 +2335,6 @@ end;
-------------------------------------------------------------------------------}
procedure InitCell(out ACell: TCell);
begin
ACell.FormulaValue := '';
ACell.UTF8StringValue := '';
FillChar(ACell, SizeOf(ACell), 0);
end;
@ -2418,7 +2418,7 @@ begin
Index := -1;
end;
end;
(*
{@@ ----------------------------------------------------------------------------
Copies the value of a cell to another one. Does not copy the formula, erases
the formula of the destination cell if there is one!
@ -2428,18 +2428,20 @@ end;
-------------------------------------------------------------------------------}
procedure CopyCellValue(AFromCell, AToCell: PCell);
begin
Assert(AFromCell <> nil);
Assert(AToCell <> nil);
AToCell^.ContentType := AFromCell^.ContentType;
AToCell^.NumberValue := AFromCell^.NumberValue;
AToCell^.DateTimeValue := AFromCell^.DateTimeValue;
AToCell^.BoolValue := AFromCell^.BoolValue;
AToCell^.ErrorValue := AFromCell^.ErrorValue;
AToCell^.UTF8StringValue := AFromCell^.UTF8StringValue;
AToCell^.FormulaValue := ''; // This is confirmed with Excel
if AFromCell <> nil then begin
AToCell^.ContentType := AFromCell^.ContentType;
AToCell^.NumberValue := AFromCell^.NumberValue;
AToCell^.DateTimeValue := AFromCell^.DateTimeValue;
AToCell^.BoolValue := AFromCell^.BoolValue;
AToCell^.ErrorValue := AFromCell^.ErrorValue;
AToCell^.UTF8StringValue := AFromCell^.UTF8StringValue;
// Note: As confirmed with Excel, the formula is not to be copied here.
// Note: The calling routine must erase the formula if the destination cell has one.
end;
end;
*)
{@@ ----------------------------------------------------------------------------
Returns TRUE if the cell contains a formula.
@ -2447,7 +2449,15 @@ end;
-------------------------------------------------------------------------------}
function HasFormula(ACell: PCell): Boolean;
begin
Result := Assigned(ACell) and (Length(ACell^.FormulaValue) > 0);
Result := Assigned(ACell) and (cfHasFormula in ACell^.Flags);
end;
{@@ ----------------------------------------------------------------------------
Returns TRUE if the cell has a 3D formula (i.e. reference to another sheet)
-------------------------------------------------------------------------------}
function Has3dFormula(ACell: PCell): Boolean;
begin
Result := HasFormula(ACell) and (cf3dFormula in ACell^.Flags);
end;
{@@ ----------------------------------------------------------------------------

View File

@ -2554,6 +2554,29 @@ begin
AStream.WriteWord(0);
end;
function DoCollectSheetsWith3dRefs(ANode: TsExprNode; AData: Pointer): Boolean;
var
sheetlist: TsBIFF8ExternSheetList;
sheetIdx, sheetIdx1, sheetIdx2: Integer;
workbook: TsWorkbook;
begin
sheetlist := TsBIFF8ExternSheetList(AData);
if (ANode is TsCellExprNode) and TsCellExprNode(ANode).Has3DLink then
begin
sheetIdx := TsCellExprNode(ANode).GetSheetIndex;
sheetList.AddSheets('', nil, sheetIdx, sheetIdx);
end else
if (ANode is TsCellRangeExprNode) and TsCellRangeExprNode(ANode).Has3DLink then
begin
workbook := TsCellRangeExprNode(ANode).Workbook as TsWorkbook;
sheetIdx1 := TsCellRangeExprNode(ANode).GetSheetIndex(1);
sheetIdx2 := TsCellRangeExprNode(ANode).GetSheetIndex(2);
for sheetIdx := sheetIdx1 to sheetIdx2 do
sheetList.AddSheets('', nil, sheetIdx1, sheetIdx2);
end;
Result := false;
end;
{@@ ----------------------------------------------------------------------------
Collects the data for out-of-sheet links found in the specified worksheet
(or all worksheets if the parameter is omitted).
@ -2561,6 +2584,14 @@ end;
-------------------------------------------------------------------------------}
procedure TsSpreadBIFF8Writer.CollectExternData;
procedure DoCollectForSheet(ASheet: TsWorksheet);
var
formula: PsFormula;
begin
for formula in ASheet.Formulas do
formula^.Parser.IterateNodes(@DoCollectSheetsWith3dRefs, FBiff8ExternSheets);
end;
{
procedure DoCollectForSheet(ASheet: TsWorksheet);
var
cell: PCell;
@ -2593,7 +2624,7 @@ procedure TsSpreadBIFF8Writer.CollectExternData;
end;
end;
end;
}
var
book: TsWorkbook;
sheet: TsWorksheet;

View File

@ -2872,13 +2872,35 @@ var
n: Word;
rpnFormula: TsRPNformula;
strFormula: String;
formula: PsFormula;
begin
n := ReadRPNTokenArraySize(AStream);
if n = 0 then
exit(false);
Result := ReadRPNTokenArray(AStream, n, rpnFormula, ACell, ASharedFormulaBase);
if Result then begin
formula := TsWorksheet(FWorksheet).Formulas.FindFormula(ACell);
if formula = nil then begin
formula := TsWorksheet(FWorksheet).Formulas.AddFormula(ACell^.Row, ACell^.Col);
formula^.Parser := TsSpreadsheetParser.Create(FWorksheet);
end;
formula^.Parser.RPNFormula := rpnFormula;
formula^.Text := formula^.Parser.Expression;
if formula^.Parser.Has3dLinks then
ACell^.Flags := ACell^.Flags + [cfHasFormula, cf3dFormula]
else
ACell^.Flags := ACell^.Flags + [cfHasFormula];
{
strFormula := tsWorksheet(FWorksheet).ConvertRPNFormulaToStringFormula(rpnFormula);
if strFormula <> '' then
ACell^.FormulaValue := strFormula;
if strFormula <> '' then begin
formula := TsWorksheet(FWorksheet).Formulas.AddFormula(ACell^.Row, ACell^.Col, strFormula);
if formula^.Parsed.Has3dLinks then
ACell^.Flags := ACell^.Flags + [cfHasFormula, cf3dFormula]
else
ACell^.Flags := ACell^.Flags + [cfHasFormula];
end;
}
end;
end;
@ -3441,6 +3463,27 @@ begin
end;
end;
function DoCollectSheetsWith3dRefs(ANode: TsExprNode; AData: Pointer): Boolean;
var
sheetlist: TsBIFFExternSheetList;
sheetIdx, sheetIdx1, sheetIdx2: Integer;
workbook: TsWorkbook;
begin
sheetlist := TsBIFFExternSheetList(AData);
if (ANode is TsCellExprNode) and TsCellExprNode(ANode).Has3DLink then
sheetList.AddSheet(TsCellExprNode(ANode).GetSheetName, ebkInternal)
else
if (ANode is TsCellRangeExprNode) and TsCellRangeExprNode(ANode).Has3DLink then
begin
workbook := TsCellRangeExprNode(ANode).Workbook as TsWorkbook;
sheetIdx1 := TsCellRangeExprNode(ANode).GetSheetIndex(1);
sheetIdx2 := TsCellRangeExprNode(ANode).GetSheetIndex(2);
for sheetIdx := sheetIdx1 to sheetIdx2 do
sheetList.AddSheet(workbook.GetWorksheetByIndex(sheetIdx).Name, ebkInternal);
end;
Result := false; // No need to rebuild the text formula
end;
{@@ ----------------------------------------------------------------------------
Collects the data for out-of-sheet links found in the specified worksheet
(or all worksheets if the parameter is omitted).
@ -3450,6 +3493,13 @@ end;
function TsSpreadBIFFWriter.CollectExternData(AWorksheet: TsBasicWorksheet = nil): Integer;
procedure DoCollectForSheet(ASheet: TsWorksheet; ASheetList: TsBIFFExternSheetList);
var
formula: PsFormula;
begin
for formula in ASheet.Formulas do
formula^.Parser.IterateNodes(@DoCollectSheetsWith3dRefs, ASheetList);
end;
{
var
cell: PCell;
workbook: TsWorkbook;
@ -3496,6 +3546,7 @@ function TsSpreadBIFFWriter.CollectExternData(AWorksheet: TsBasicWorksheet = nil
end;
end;
end;
}
var
sheet: TsWorksheet;

View File

@ -641,6 +641,7 @@ var
datanode, tnode: TDOMNode;
dataStr: String;
formulaStr: String;
formula: PsFormula;
nodeName: String;
sstIndex: Integer;
number: Double;
@ -731,8 +732,11 @@ begin
sharedformulabase := TSharedFormulaData(FSharedFormulaBaseList[StrToInt(s)]);
// ... and copy shared formula to destination cell
InitCell(FWorksheet, sharedformulabase.Row, sharedformulabase.Col, lCell);
lCell.Formulavalue := sharedformulabase.Formula;
lCell.Worksheet := sharedformulabase.Worksheet;
formula := sharedFormulaBase.Worksheet.Formulas.AddFormula(
sharedFormulabase.Row, sharedFormulaBase.Col, sharedformulabase.Formula
);
// lCell.Formulavalue := sharedformulabase.Formula;
// lCell.Worksheet := sharedformulabase.Worksheet;
sheet.CopyFormula(@lCell, cell);
cell^.ContentType := cctFormula;
end;
@ -740,8 +744,8 @@ begin
end
else
// "Normal" formula
cell^.FormulaValue := formulaStr;
// AWorksheet.WriteFormula(cell, formulaStr);
sheet.WriteFormula(cell, formulaStr);
// cell^.FormulaValue := formulaStr;
except
on E:EExprParser do begin
FWorkbook.AddErrorMsg(E.Message);
@ -759,11 +763,8 @@ begin
// get data type
s := GetAttrValue(ANode, 't'); // "t" = data type
if (s = '') and (dataStr = '') then
begin
formulaStr := cell^.FormulaValue;
sheet.WriteBlank(cell); // this erases the formula!!!
cell^.FormulaValue := formulaStr;
end else
sheet.WriteBlank(cell, true) // true --> do not erase the formula!!!
else
if (s = '') or (s = 'n') then begin
// Number or date/time, depending on format
number := StrToFloat(dataStr, FPointSeparatorSettings);
@ -795,9 +796,9 @@ begin
end else
if (s = 'str') or (s = 'inlineStr') then begin
// literal string
formulaStr := cell^.FormulaValue;
// formulaStr := cell^.FormulaValue;
sheet.WriteText(cell, datastr);
cell^.FormulaValue := formulaStr;
// cell^.FormulaValue := formulaStr;
end else
if s = 'b' then
// boolean
@ -2068,6 +2069,8 @@ begin
sheetData.ID := GetAttrvalue(node, 'sheetID');
sheetData.Hidden := GetAttrValue(node, 'state') = 'hidden';
FSheetList.Add(sheetData);
// Create worksheet - needed because of 3d references
(FWorkbook as TsWorkbook).AddWorksheet(sheetData.Name, true);
end;
node := node.NextSibling;
end;
@ -2520,8 +2523,12 @@ begin
// read worksheets
for i:=0 to FSheetList.Count-1 do begin
{
// Create worksheet
FWorksheet := (FWorkbook as TsWorkbook).AddWorksheet(TSheetData(FSheetList[i]).Name, true);
}
// Worksheets are already created...
FWorksheet := (FWorkbook as TsWorkbook).GetWorksheetByName(TSheetData(FSheetList[i]).Name);
if TSheetData(FSheetList[i]).Hidden then
FWorksheet.Options := FWorksheet.Options + [soHidden];
@ -5219,10 +5226,15 @@ var
cellPosText: String;
lStyleIndex: Integer;
t, v: String;
formula: PsFormula;
formulaStr: String;
begin
cellPosText := TsWorksheet.CellPosToText(ARow, ACol);
lStyleIndex := GetStyleIndex(ACell);
formula := TsWorksheet(FWorksheet).Formulas.FindFormula(ARow, ACol);
formulaStr := PrepareFormula(formula^.Text);
case ACell^.ContentType of
cctFormula:
begin
@ -5265,7 +5277,7 @@ begin
'%s' +
'</c>', [
CellPosText, lStyleIndex, t,
PrepareFormula(ACell^.FormulaValue),
formulaStr,
v
]));
end;

View File

@ -162,12 +162,12 @@ type
published
{@@ Automatically detects the fileformat when loading the spreadsheet file
specified by FileName }
property AutoDetectFormat: Boolean read FAutoDetectFormat write FAutoDetectFormat;
property AutoDetectFormat: Boolean read FAutoDetectFormat write FAutoDetectFormat default true;
{@@ File format of the next spreadsheet file to be loaded by means of the
Filename property. Not used when AutoDetectFormat is TRUE.
Note that if FileFormat is sfUser then the format ID must be specified at
runtime. }
property FileFormat: TsSpreadsheetFormat read GetFileFormat write SetFileFormat;
property FileFormat: TsSpreadsheetFormat read GetFileFormat write SetFileFormat default sfOOXML;
{@@ Name of the loaded spreadsheet file which is loaded by assigning a file name
to this property. Format detection is determined by the properties
AutoDetectFormat and FileFormat. Using this property loads the file at
@ -752,7 +752,8 @@ constructor TsWorkbookSource.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FListeners := TFPList.Create;
FFileFormatID := ord(sfExcel8);
FFileFormatID := ord(sfOOXML);
FAutoDetectFormat := True;
CreateNewWorkbook;
end;

View File

@ -6587,7 +6587,8 @@ begin
// If the cell already exists and contains a formula then the formula must be
// removed. The formula would dominate over the data value.
cell := Worksheet.FindCell(r, c);
if HasFormula(cell) then cell^.FormulaValue := '';
if HasFormula(cell) then
Worksheet.UseformulaInCell(cell, nil); //cell^.FormulaValue := '';
if VarIsNull(AValue) then
Worksheet.WriteBlank(r, c)

View File

@ -161,13 +161,14 @@ var
i, row, col: Integer;
cell: PCell;
expectedFormula: String;
expectedStr, actualStr: String;
begin
TempFile := GetTempFileName;
MyWorkbook := TsWorkbook.Create;
try
// MyWorkbook.Options := MyWorkbook.Options + [boCalcBeforeSaving]; //boAutoCalc];
MyWorkbook.Options := MyWorkbook.Options + [boCalcBeforeSaving]; //boAutoCalc];
MyWorkSheet:= MyWorkBook.AddWorksheet(CopyTestSheet);
@ -212,6 +213,7 @@ begin
begin
cell := Myworksheet.FindCell(row, 0);
case ATestKind of
// 0: ; // don't copy, just write the original file for debugging
1: MyWorksheet.CopyValue(cell, row, 2);
2: MyWorksheet.CopyValue(cell, row, 1);
3: MyWorksheet.CopyFormat(cell, row, 2);
@ -233,6 +235,7 @@ begin
// Read spreadsheet file...
MyWorkbook.ReadFromFile(TempFile, AFormat);
MyWorksheet := MyWorkbook.GetFirstWorksheet;
MyWorksheet.CalcFormulas;
if odd(ATestKind) then col := 2 else col := 1;
@ -335,9 +338,12 @@ begin
)
else
begin
expectedStr := SetToString(PTypeInfo(TypeInfo(TsUsedFormattingFields)),
integer(Sourcecells[i+col-2].UsedformattingFields), true);
actualStr := SetToString(PTypeInfo(TypeInfo(TsUsedFormattingFields)),
integer(MyWorksheet.ReadUsedFormatting(cell)), true);
CheckEquals(
true,
SourceCells[i+(col-2)].UsedFormattingFields = MyWorksheet.ReadUsedFormatting(cell),
expectedStr, actualStr,
'Used formatting fields mismatch, cell ' + CellNotation(myWorksheet, row, col)
);
if (uffBackground in SourceCells[i+(col-2)].UsedFormattingFields) then
@ -390,7 +396,8 @@ begin
else
CheckEquals(
SourceCells[i+col-2].FormulaValue,
cell^.Formulavalue,
MyWorksheet.ReadFormula(cell),
// cell^.Formulavalue,
'Formula mismatch, cell ' + CellNotation(MyWorksheet, row, col)
);
5:
@ -403,7 +410,8 @@ begin
end;
CheckEquals(
expectedFormula,
cell^.FormulaValue,
MyWorksheet.ReadFormula(cell),
// cell^.FormulaValue,
'Formula mismatch, cell ' + Cellnotation(Myworksheet, row, col)
);
end;
@ -421,7 +429,8 @@ begin
end;
CheckEquals(
expectedFormula,
cell^.FormulaValue,
MyWorksheet.ReadFormula(cell),
// cell^.FormulaValue,
'Formula mismatch, cell ' + Cellnotation(Myworksheet, row, col)
);
end;

View File

@ -763,7 +763,7 @@ begin
cctEmpty : actual := EmptyResult;
else fail('ContentType not supported');
end;
actualformula := cell^.FormulaValue;
actualformula := sheet1.Formulas.FindFormula(cell)^.Text; //cell^.FormulaValue;
expected := SollValues[row];
// Cell does not store integers!

View File

@ -565,10 +565,10 @@ begin
'67890123';
DeleteCol := 2;
Formula := 'C3';
SollFormula := '#REF!'; // col index unchanged due to deletion after cell
SollFormula := '#REF!'; // cell needec by formula does not exist any more
SollLayout := '1245678|'+
'2356789|'+
'346E890|'+ // "E" = error
'346E890|'+ // "E" = error
'4578901|'+
'5689012|'+
'6790123';
@ -617,7 +617,7 @@ begin
Layout := '12345678|'+
'23456789|'+
'3456F890|'+ // "F" = Formula in row 2, col 4
'45678901|'+
'45678901|'+ // delete this row
'56789012|'+
'67890123';
DeleteRow := 3;
@ -1010,6 +1010,7 @@ begin
if InsDelTestData[ATestIndex].DeleteRow >= 0 then
MyWorksheet.DeleteRow(InsDelTestData[ATestIndex].DeleteRow);
MyWorkbook.CalcFormulas;
MyWorkBook.WriteToFile(TempFile, AFormat, true);
finally
MyWorkbook.Free;

View File

@ -140,8 +140,8 @@ begin
cell := worksheet.WriteFormula(TESTCELL_ROW, TESTCELL_COL, AFormula);
// Read formula before saving
actualFormula := cell^.Formulavalue;
CheckEquals(AFormula, actualFormula, 'Unsaved formula text mismatch');
actualFormula := worksheet.ReadFormula(cell);
CheckEquals(AExpectedFormula, actualFormula, 'Unsaved formula text mismatch');
// Read calculated value before saving
actualvalue := worksheet.ReadAsNumber(TESTCELL_ROW, TESTCELL_COL);
@ -165,7 +165,8 @@ begin
CheckEquals(AExpected, actualValue, 'Saved calculated value mismatch');
cell := worksheet.FindCell(TESTCELL_ROW, TESTCELL_COL);
actualformula := cell^.FormulaValue;
actualformula := worksheet.Formulas.FindFormula(cell)^.Text;
// actualformula := cell^.FormulaValue;
// When writing ranges are reconstructed in correct order.
CheckEquals(AExpectedFormula, actualformula, 'Saved formula text mismatch.');
finally
@ -302,15 +303,16 @@ end;
{ --- }
{ Range formulas in which the parts are not ordered. They will be put into the
correct order when then formula is written to the worksheet. --> the
expected range must be in correct order. }
procedure TSpreadSingleFormulaTests.SumMultiSheetRange_FlippedSheetsAndCells_OOXML;
begin
// In OOXML the range is written literally.
TestFloatFormula('SUM(Sheet3:Sheet2!C5:C3)', 55.0, ftkCellRangeSheetRange, sfOOXML);
TestFloatFormula('SUM(Sheet3:Sheet2!C5:C3)', 55.0, ftkCellRangeSheetRange, sfOOXML, 'SUM(Sheet2:Sheet3!C3:C5)');
end;
procedure TSpreadSingleFormulaTests.SumMultiSheetRange_FlippedSheetsAndCells_ODS;
begin
// ODS requires conversion of the formula which results in reordering of ranges.
TestFloatFormula('SUM(Sheet3:Sheet2!C5:C3)', 55.0, ftkCellRangeSheetRange, sfOpenDocument, 'SUM(Sheet2:Sheet3!C3:C5)');
end;
@ -322,14 +324,12 @@ end;
procedure TSpreadSingleFormulaTests.SumMultiSheetRange_FlippedCells_OOXML;
begin
// In OOXML the range is written literally.
TestFloatFormula('SUM(Sheet2:Sheet3!C5:C3)', 55.0, ftkCellRangeSheetRange, sfOOXML);
TestFloatFormula('SUM(Sheet2:Sheet3!C5:C3)', 55.0, ftkCellRangeSheetRange, sfOOXML, 'SUM(Sheet2:Sheet3!C3:C5)');
end;
procedure TSpreadSingleFormulaTests.SumMultiSheetRange_FlippedSheets_OOXML;
begin
// In OOXML the range is written literally.
TestFloatFormula('SUM(Sheet3:Sheet2!C3:C5)', 55.0, ftkCellRangeSheetRange, sfOOXML);
TestFloatFormula('SUM(Sheet3:Sheet2!C3:C5)', 55.0, ftkCellRangeSheetRange, sfOOXML, 'SUM(Sheet2:Sheet3!C3:C5)');
end;

View File

@ -1026,7 +1026,10 @@
else
Myworksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
sollValues[Row] := FloatResult(arccosh(number));
if AFormat = sfExcel2 then
sollValues[Row] := ErrorResult(errFormulaNotSupported)
else
sollValues[Row] := FloatResult(arccosh(number));
MyWorksheet.WriteNumber(Row, 2, sollValues[Row].ResFloat);
// ACOSH - error result (arccos is not defined below 1
@ -1043,6 +1046,9 @@
SetLength(sollValues, Row+1);
if AFormat = sfOpenDocument then
sollValues[Row] := FloatResult(0)
else
if AFormat = sfExcel2 then
sollValues[Row] := ErrorResult(errFormulaNotSupported)
else
sollValues[Row] := ErrorResult(errOverFlow);
@ -1090,7 +1096,10 @@
else
MyWorksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
sollValues[Row] := FloatResult(arcsinh(number));
if AFormat = sfExcel2 then
sollValues[Row] := ErrorResult(errFormulaNotSupported)
else
sollValues[Row] := FloatResult(arcsinh(number));
MyWorksheet.WriteNumber(Row, 2, sollValues[Row].ResFloat);
// ATAN
@ -1120,7 +1129,10 @@
else
MyWorksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
sollValues[Row] := FloatResult(arctanh(number));
if AFormat = sfExcel2 then
sollValues[Row] := ErrorResult(errFormulaNotSupported)
else
sollValues[Row] := FloatResult(arctanh(number));
MyWorksheet.WriteNumber(Row, 2, sollValues[Row].ResFloat);
// ATANH - error result (arctan is only defined within ]-1,1[
@ -1137,6 +1149,9 @@
SetLength(sollValues, Row+1);
if AFormat = sfOpenDocument then
sollValues[Row] := FloatResult(0)
else
if AFormat = sfExcel2 then
sollValues[Row] := ErrorResult(errFormulaNotSupported)
else
sollValues[Row] := ErrorResult(errOverFlow);
@ -1185,8 +1200,12 @@
else
MyWorksheet.WriteFormula(Row, 1, formula);
SetLength(sollValues, Row+1);
sollValues[Row] := FloatResult(cosh(number));
MyWorksheet.WriteNumber(Row, 2, sollValues[Row].ResFloat);
if AFormat = sfExcel2 then
sollValues[Row] := ErrorResult(errFormulaNotSupported)
else begin
sollValues[Row] := FloatResult(cosh(number));
MyWorksheet.WriteNumber(Row, 2, sollValues[Row].ResFloat);
end;
// DEGREES
if AFormat <> sfExcel2 then
@ -1678,7 +1697,10 @@
MyWorksheet.WriteFormula(Row, 1, formula);
MyWorksheet.WriteNumber(Row, 2, sinh(number));
SetLength(sollValues, Row+1);
sollValues[Row] := FloatResult(sinh(number));
if AFormat = sfExcel2 then
sollValues[Row] := ErrorResult(errFormulaNotSupported)
else
sollValues[Row] := FloatResult(sinh(number));
// SINH of cell value
inc(Row);
@ -1692,7 +1714,10 @@
MyWorksheet.WriteFormula(Row, 1, formula);
MyWorksheet.WriteNumber(Row, 2, sinh(cellB1));
SetLength(sollValues, Row+1);
sollValues[Row] := FloatResult(sinh(cellB1));
if AFormat = sfExcel2 then
sollValues[Row] := ErrorResult(errFormulaNotSupported)
else
sollValues[Row] := FloatResult(sinh(cellB1));
// SQRT - valid result
inc(Row);
@ -1785,7 +1810,10 @@
MyWorksheet.WriteFormula(Row, 1, formula);
MyWorksheet.WriteNumber(Row, 2, tanh(number));
SetLength(sollValues, Row+1);
sollValues[Row] := FloatResult(tanh(number));
if AFormat = sfExcel2 then
sollValues[Row] := ErrorResult(errFormulaNotSupported)
else
sollValues[Row] := FloatResult(tanh(number));
// TANH of cell value
inc(Row);
@ -1799,7 +1827,11 @@
MyWorksheet.WriteFormula(Row, 1, formula);
MyWorksheet.WriteNumber(Row, 2, tanh(cellB1));
SetLength(sollValues, Row+1);
sollValues[Row] := FloatResult(tanh(cellB1));
if AFormat = sfExcel2 then
sollValues[Row] := ErrorResult(errFormulaNotSupported)
else
sollValues[Row] := FloatResult(tanh(cellB1));
{------------------------------------------------------------------------------}
{ Date/time functions }
@ -2087,7 +2119,10 @@
Myworksheet.WriteDateTimeFormat(Row, 1, nfShortDate);
Myworksheet.WriteDateTime(Row, 2, Date(), nfShortDate);
SetLength(sollValues, Row+1);
sollValues[Row] := DateTimeResult(Date());
if AFormat = sfExcel2 then
sollValues[Row] := ErrorResult(errFormulaNotSupported)
else
sollValues[Row] := DateTimeResult(Date());
// WEEKDAY / argument number
inc(Row);