fpspreadsheet: Simplify handling of 3d references. Fix bug related to 3d reference type 'Sheet1:Sheet2!A1'.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@6422 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2018-05-16 17:44:02 +00:00
parent 05a358bfd9
commit b94ba1fac6
7 changed files with 334 additions and 402 deletions

View File

@ -57,8 +57,8 @@ type
{ Tokens } { Tokens }
TsTokenType = ( TsTokenType = (
ttCell, ttCellRange, ttSheetName, ttCellRangeODS, // ttCell, ttCellRange, ttSheetName, ttCellRangeODS,
ttNumber, ttString, ttIdentifier, ttNumber, ttString, ttIdentifier, ttSpreadsheetAddress,
ttPlus, ttMinus, ttMul, ttDiv, ttConcat, ttPercent, ttPower, ttLeft, ttRight, ttPlus, ttMinus, ttMul, ttDiv, ttConcat, ttPercent, ttPower, ttLeft, ttRight,
ttLessThan, ttLargerThan, ttEqual, ttNotEqual, ttLessThanEqual, ttLargerThanEqual, ttLessThan, ttLargerThan, ttEqual, ttNotEqual, ttLessThanEqual, ttLargerThanEqual,
ttListSep, ttTrue, ttFalse, ttMissingArg, ttError, ttEOF ttListSep, ttTrue, ttFalse, ttMissingArg, ttError, ttEOF
@ -590,18 +590,19 @@ type
FRow, FCol: Cardinal; FRow, FCol: Cardinal;
FFlags: TsRelFlags; FFlags: TsRelFlags;
FCell: PCell; FCell: PCell;
FSheetName: String;
FIsRef: Boolean; FIsRef: Boolean;
FOtherSheet: Boolean;
protected protected
function GetCol: Cardinal; function GetCol: Cardinal;
function GetRow: Cardinal; function GetRow: Cardinal;
function GetSheet: TsWorksheet;
function GetSheetIndex: Integer; function GetSheetIndex: Integer;
procedure GetNodeValue(out Result: TsExpressionResult); override; function GetSheetName: String;
function GetWorkbook: TsWorkbook;
procedure GetNodeValue(out AResult: TsExpressionResult); override;
public public
constructor Create(AParser: TsExpressionParser; AWorksheet: TsWorksheet; constructor Create(AParser: TsExpressionParser; AWorksheet: TsWorksheet;
ARow, ACol: Cardinal; AFlags: TsRelFlags; OtherSheet: Boolean); overload; ASheetName: String; ARow, ACol: Cardinal; AFlags: TsRelFlags);
constructor Create(AParser: TsExpressionParser; AWorksheet: TsWorksheet;
ACellString: String; Othersheet: Boolean); overload;
function AsRPNItem(ANext: PRPNItem): PRPNItem; override; function AsRPNItem(ANext: PRPNItem): PRPNItem; override;
function AsString: string; override; function AsString: string; override;
procedure Check; override; procedure Check; override;
@ -619,7 +620,7 @@ type
// FWorksheet2: TsWorksheet; // FWorksheet2: TsWorksheet;
FRow: array[TsCellRangeIndex] of Cardinal; FRow: array[TsCellRangeIndex] of Cardinal;
FCol: array[TsCellRangeIndex] of Cardinal; FCol: array[TsCellRangeIndex] of Cardinal;
FSheet: array[TsCellRangeIndex] of Integer; FSheetIndex: array[TsCellRangeIndex] of Integer;
FFlags: TsRelFlags; FFlags: TsRelFlags;
F3dRange: Boolean; F3dRange: Boolean;
protected protected
@ -629,10 +630,7 @@ type
function GetWorkbook: TsWorkbook; function GetWorkbook: TsWorkbook;
public public
constructor Create(AParser: TsExpressionParser; AWorksheet: TsWorksheet; constructor Create(AParser: TsExpressionParser; AWorksheet: TsWorksheet;
ARangeString: String); overload; ASheet1, ASheet2: String; ARange: TsCellRange; AFlags: TsRelFlags);
constructor Create(AParser: TsExpressionParser; AWorksheet: TsWorksheet;
ASheet1, ASheet2: String; ARow1,ACol1, ARow2, ACol2: Cardinal;
AFlags: TsRelFlags; Is3DRange: Boolean); overload;
function AsRPNItem(ANext: PRPNItem): PRPNItem; override; function AsRPNItem(ANext: PRPNItem): PRPNItem; override;
function AsString: String; override; function AsString: String; override;
procedure Check; override; procedure Check; override;
@ -652,12 +650,16 @@ type
FTokenType: TsTokenType; FTokenType: TsTokenType;
FSheetNameTerminator: Char; FSheetNameTerminator: Char;
FSavedSheetNameTerminator: Char; FSavedSheetNameTerminator: Char;
FCellRange: TsCellRange;
FFlags: TsRelFlags;
FSheet1, FSheet2: String;
private private
FParser: TsExpressionParser; FParser: TsExpressionParser;
function GetCurrentChar: Char; function GetCurrentChar: Char;
procedure ScanError(Msg: String); procedure ScanError(Msg: String);
protected protected
procedure SetSource(const AValue: String); virtual; procedure SetSource(const AValue: String); virtual;
function DoCellRangeODS: TsTokenType;
function DoError: TsTokenType; function DoError: TsTokenType;
function DoIdentifier: TsTokenType; function DoIdentifier: TsTokenType;
function DoNumber: TsTokenType; function DoNumber: TsTokenType;
@ -676,6 +678,10 @@ type
out ARow1, ACol1, ARow2, ACol2: Cardinal; out AFlags: TsRelFlags); out ARow1, ACol1, ARow2, ACol2: Cardinal; out AFlags: TsRelFlags);
function GetToken: TsTokenType; function GetToken: TsTokenType;
property Token: String read FToken; property Token: String read FToken;
property TokenCellRange: TsCellRange read FCellRange;
property TokenFlags: TsRelFlags read FFlags;
property TokenSheet1: String read FSheet1;
property TokenSheet2: String read FSheet2;
property TokenType: TsTokenType read FTokenType; property TokenType: TsTokenType read FTokenType;
property Source: String read FSource write SetSource; property Source: String read FSource write SetSource;
property Pos: Integer read FPos; property Pos: Integer read FPos;
@ -699,17 +705,13 @@ type
FDialect: TsFormulaDialect; FDialect: TsFormulaDialect;
FSourceCell: PCell; FSourceCell: PCell;
FDestCell: PCell; FDestCell: PCell;
// FActiveCell: PCell;
procedure CheckEOF; procedure CheckEOF;
// procedure CheckNodes(var ALeft, ARight: TsExprNode);
// function ConvertNode(Todo: TsExprNode; ToType: TsResultType): TsExprNode;
function GetAsBoolean: Boolean; function GetAsBoolean: Boolean;
function GetAsDateTime: TDateTime; function GetAsDateTime: TDateTime;
function GetAsFloat: TsExprFloat; function GetAsFloat: TsExprFloat;
function GetAsInteger: Int64; function GetAsInteger: Int64;
function GetAsString: String; function GetAsString: String;
function GetRPNFormula: TsRPNFormula; function GetRPNFormula: TsRPNFormula;
// function MatchNodes(Todo, Match: TsExprNode): TsExprNode;
procedure SetBuiltIns(const AValue: TsBuiltInExprCategories); procedure SetBuiltIns(const AValue: TsBuiltInExprCategories);
procedure SetDialect(const AValue: TsFormulaDialect); procedure SetDialect(const AValue: TsFormulaDialect);
procedure SetIdentifiers(const AValue: TsExprIdentifierDefs); procedure SetIdentifiers(const AValue: TsExprIdentifierDefs);
@ -945,6 +947,99 @@ begin
FSavedSheetNameTerminator := '!'; FSavedSheetNameTerminator := '!';
end; end;
{ Scans until closing square bracket is reached. In OpenDocument, this is
a cell or cell range identifier.
It has the structure [sheet1.C1R1:sheet2.C2R2] }
function TsExpressionScanner.DoCellRangeODS: TsTokenType;
type
TScannerStateODS = (ssSheet1, ssCol1, ssRow1, ssSheet2, ssCol2, ssRow2);
var
C: Char;
prevC: Char;
state: TScannerStateODS;
val: Integer;
begin
FSheet1 := '';
FSheet2 := '';
FCellRange.Row1 := Cardinal(-1);
FCellRange.Col1 := Cardinal(-1);
FCellRange.Row2 := Cardinal(-1);
FCellRange.Col2 := Cardinal(-1);
FFlags := rfAllRel;
state := ssSheet1;
FToken := '';
C := NextPos;
prevC := #0;
while (C <> ']') and (C <> cNULL) do begin
case C of
cNULL: ScanError(rsUnexpectedEndOfExpression);
'.': begin
if (state = ssSheet1) then
begin
FSheet1 := FToken;
state := ssCol1;
end else
if (state = ssSheet2) then
begin
FSheet2 := FToken;
state := ssCol2;
end else
ScanError(rsIllegalODSCellRange);
FToken := '';
val := 0;
end;
':': if (state = ssRow1) then
begin
FCellRange.Row1 := val-1;
state := ssSheet2;
FToken := '';
end else
ScanError(rsIllegalODSCellRange);
'$': case state of
ssCol1: if prevC = '.' then Exclude(FFlags, rfRelCol) else Exclude(FFlags, rfRelRow);
ssCol2: if prevC = '.' then Exclude(FFlags, rfRelCol2) else Exclude(FFlags, rfRelRow2);
end;
else
if (state in [ssSheet1, ssSheet2]) then
FToken := FToken + C
else
case C of
'A'..'Z':
val := val*10 + ord(C) - ord('A');
'a'..'z':
val := val*10 + ord(C) - ord('a');
'0'..'9':
if state = ssCol1 then begin
FCellRange.Col1 := val;
val := ord(C) - ord('0');
state := ssRow1;
end else
if state = ssCol2 then begin
FCellRange.Col2 := val;
val := ord(C) - ord('0');
state := ssRow2;
end;
end;
end;
prevC := C;
C := NextPos;
end;
if C <> ']' then
ScanError(Format(rsRightSquareBracketExpected, [FPos, C]));
case state of
ssRow1:
if val > 0 then FCellRange.Row1 := val - 1 else ScanError(rsIllegalODSCellRange);
ssRow2:
if val > 0 then FCellRange.Row2 := val - 1 else ScanError(rsIllegalODSCellRange);
end;
if FCellRange.Col2 = Cardinal(-1) then Exclude(FFlags, rfRelCol2);
if FCellRange.Row2 = Cardinal(-1) then Exclude(FFlags, rfRelRow2);
C := NextPos;
Result := ttSpreadsheetAddress;
FTokenType := Result;
end;
function TsExpressionScanner.DoDelimiter: TsTokenType; function TsExpressionScanner.DoDelimiter: TsTokenType;
var var
B : Boolean; B : Boolean;
@ -1014,33 +1109,24 @@ var
flags: TsRelFlags; flags: TsRelFlags;
begin begin
C := CurrentChar; C := CurrentChar;
if C = FSheetNameTerminator then C := NextPos; while (not IsWordDelim(C)) and (C <> cNULL) do
while (not IsWordDelim(C)) and (C <> cNull) and (C <> FSheetNameTerminator) do
begin begin
{
if ((FParser.Dialect = fdOpenDocument) and (C = ']')) then begin
C := NextPos;
FSheetNameTerminator := FSavedSheetNameTerminator;
break;
end;
}
FToken := FToken + C; FToken := FToken + C;
C := NextPos; C := NextPos;
end; end;
if C = FSheetNameTerminator then if ParseCellRangeString(FToken, FSheet1, FSheet2,
FCellRange.Row1, FCellRange.Col1, FCellRange.Row2, FCellRange.Col2, FFlags
) and (C <> '(')
then
begin begin
C := NextPos; Result := ttSpreadsheetAddress;
result := ttSheetName;
exit; exit;
end; end;
S := LowerCase(FToken); S := LowerCase(FToken);
if ParseCellString(S, row, col, flags) and (C <> '(') then
Result := ttCell if (S = 'true') and (C <> '(') then
else if ParseCellRangeString(S, row, col, row2, col2, flags) and (C <> '(') then
Result := ttCellRange
else if (S = 'true') and (C <> '(') then
Result := ttTrue Result := ttTrue
else if (S = 'false') and (C <> '(') then else if (S = 'false') and (C <> '(') then
Result := ttFalse Result := ttFalse
@ -1166,53 +1252,6 @@ begin
C := NextPos; C := NextPos;
end; end;
(*
function TsExpressionScanner.DoSquareBracket: TsTokenType;
var
C: Char;
r1,c1,r2,c2: Cardinal;
flags: TsRelFlags;
isRange: Boolean;
sheetName: String;
begin
isRange := false;
FToken := '';
sheetName := '';
C := NextPos;
while (C <> ']') do
begin
case C of
cNull: ScanError(rsUnexpectedEndOfExpression);
'.' : begin
sheetName := FToken;
FToken := '';
end;
':' : begin isRange := true; FToken := FToken + C; end;
else FToken := FToken + C;
end;
C := NextPos;
end;
C := NextPos;
if sheetName <> '' then begin
if isRange then
begin
if ParseCellRangeString(FToken, r1, c1, r2, c2, flags) then
Result := ttCellRange
else
Result := ttError;
// ScanError(Format(SErrInvalidCellRange, [FToken]));
end else
begin
if ParseCellString(FToken, r1, c1, flags) then
Result := ttCell
else
Result := ttError;
// ScanError(Format(SErrInvalidCell, [FToken]));
end;
end;*)
function TsExpressionScanner.DoString: TsTokenType; function TsExpressionScanner.DoString: TsTokenType;
@ -1257,8 +1296,8 @@ begin
FToken := ''; FToken := '';
SkipWhiteSpace; SkipWhiteSpace;
C := FChar^; C := FChar^;
if {(FParser.Dialect = fdOpenDocument) and }(C = '[') then if (C = '[') then
Result := ttCellRangeODS Result := DoCellRangeODS
else if C = cNull then else if C = cNull then
Result := ttEOF Result := ttEOF
else if IsDelim(C) then else if IsDelim(C) then
@ -1738,10 +1777,9 @@ var
optional: Boolean; optional: Boolean;
token: String; token: String;
prevTokenType: TsTokenType; prevTokenType: TsTokenType;
sheetname, sheetname2: String; sheetname1, sheetname2: String;
r1, c1, r2, c2: Cardinal; rng: TsCellRange;
flags: TsRelFlags; flags: TsRelFlags;
sheet: TsWorksheet;
begin begin
{$ifdef debugexpr} Writeln('Primitive : ',TokenName(TokenType),': ',CurrentToken);{$endif debugexpr} {$ifdef debugexpr} Writeln('Primitive : ',TokenName(TokenType),': ',CurrentToken);{$endif debugexpr}
SetLength(Args, 0); SetLength(Args, 0);
@ -1761,40 +1799,18 @@ begin
Result := TsConstExprNode.CreateBoolean(self, false) Result := TsConstExprNode.CreateBoolean(self, false)
else if (TokenType = ttString) then else if (TokenType = ttString) then
Result := TsConstExprNode.CreateString(self, CurrentToken) Result := TsConstExprNode.CreateString(self, CurrentToken)
else if (TokenType = ttCell) then else if (TokenType = ttSpreadsheetAddress) then
Result := TsCellExprNode.Create(self, FWorksheet, CurrentToken, false)
else if (TokenType = ttCellRange) then
Result := TsCellRangeExprNode.Create(self, FWorksheet, CurrentToken)
else if (TokenType = ttCellRangeODS) then
begin begin
FScanner.GetCellRangeParamsODS(sheetname, sheetname2, r1, c1, r2, c2, flags); sheetname1 := FScanner.TokenSheet1;
if (sheetname2 = '') and (r2 = cardinal(-1)) and (c2 = cardinal(-1)) then sheetname2 := FScanner.TokenSheet2;
begin rng := FScanner.TokenCellRange;
if sheetname = '' then flags := FScanner.TokenFlags;
Result := TsCellExprNode.Create(self, FWorksheet, r1, c1, flags, false) if (sheetname2 = '') and
else begin (rng.Row2 = Cardinal(-1)) and (rng.Col2 = Cardinal(-1))
sheet := FWorksheet.Workbook.GetWorksheetByName(sheetName); then
Result := TsCellExprNode.Create(self, sheet, r1, c1, flags, true); Result := TsCellExprNode.Create(self, FWorksheet, sheetname1, rng.Row1, rng.Col1, flags)
end;
end
else else
Result := TsCellRangeExprNode.Create(self, FWorksheet, sheetname, sheetname2, Result := TsCellRangeExprNode.Create(self, FWorksheet, sheetname1, sheetname2, rng, flags)
r1, c1, r2, c2, flags, (sheetname <> ''));
end
else if (TokenType = ttSheetName) then
begin
sheetName := CurrentToken;
GetToken;
if TokenType = ttCell then begin
sheet := FWorksheet.Workbook.GetWorksheetByName(sheetName);
Result := TsCellExprNode.Create(self, sheet, CurrentToken, true)
end else
if TokenType = ttCellRange then begin
if FDialect = fdOpenDocument then
Result := TsCellRangeExprNode.Create(self, FWorksheet, CurrentToken)
else
Result := TsCellrangeExprNode.Create(self, FWorksheet, sheetName+'!'+CurrentToken);
end;
end end
else if (TokenType = ttError) then else if (TokenType = ttError) then
Result := TsConstExprNode.CreateError(self, CurrentToken) Result := TsConstExprNode.CreateError(self, CurrentToken)
@ -1989,8 +2005,8 @@ procedure TsExpressionParser.SetRPNFormula(const AFormula: TsRPNFormula);
ID: TsExprIdentifierDef; ID: TsExprIdentifierDef;
i, n: Integer; i, n: Integer;
args: TsExprArgumentArray; args: TsExprArgumentArray;
sheet: TsWorksheet;
sn, sn2: string; sn, sn2: string;
rng: TsCellRange;
begin begin
if AIndex < 0 then if AIndex < 0 then
exit; exit;
@ -2007,7 +2023,7 @@ procedure TsExpressionParser.SetRPNFormula(const AFormula: TsRPNFormula);
else else
begin begin
flags := AFormula[AIndex].RelFlags; flags := AFormula[AIndex].RelFlags;
ANode := TsCellExprNode.Create(self, FWorksheet, r, c, flags, false); ANode := TsCellExprNode.Create(self, FWorksheet, '', r, c, flags);
end; end;
dec(AIndex); dec(AIndex);
end; end;
@ -2021,28 +2037,27 @@ procedure TsExpressionParser.SetRPNFormula(const AFormula: TsRPNFormula);
else else
begin begin
flags := AFormula[AIndex].RelFlags; flags := AFormula[AIndex].RelFlags;
sheet := FWorksheet.Workbook.GetWorksheetByIndex(idx); sn := FWorksheet.Workbook.GetWorksheetByIndex(idx).Name;
ANode := TsCellExprNode.Create(Self, sheet, r, c, flags, true); ANode := TsCellExprNode.Create(Self, FWorksheet, sn, r, c, flags);
end; end;
dec(AIndex); dec(AIndex);
end; end;
fekCellRange, fekCellRange3D: fekCellRange, fekCellRange3D:
begin begin
r := AFormula[AIndex].Row; rng.Row1 := AFormula[AIndex].Row;
c := AFormula[AIndex].Col; rng.Col1 := AFormula[AIndex].Col;
r2 := AFormula[AIndex].Row2; rng.Row2 := AFormula[AIndex].Row2;
c2 := AFormula[AIndex].Col2; rng.Col2 := AFormula[AIndex].Col2;
flags := AFormula[AIndex].RelFlags; flags := AFormula[AIndex].RelFlags;
if fek = fekCellRange then if fek = fekCellRange then
ANode := TsCellRangeExprNode.Create(self, FWorksheet, ANode := TsCellRangeExprNode.Create(self, FWorksheet, '', '', rng, flags)
FWorksheet.Name, FWorksheet.Name, r, c, r2, c2, flags, false)
else begin else begin
sn := FWorksheet.Workbook.GetWorksheetByIndex(AFormula[AIndex].Sheet).Name; sn := FWorksheet.Workbook.GetWorksheetByIndex(AFormula[AIndex].Sheet).Name;
if AFormula[AIndex].Sheet2 = -1 then if AFormula[AIndex].Sheet2 <> -1 then
sn2 := sn sn2 := FWorksheet.Workbook.GetWorksheetByIndex(AFormula[AIndex].Sheet2).Name
else else
sn2 := FWorksheet.Workbook.GetWorksheetByIndex(AFormula[AIndex].Sheet2).Name; sn2 := '';
ANode := TsCellRangeExprNode.Create(self, FWorksheet, sn,sn2, r,c, r2,c2, flags, true); ANode := TsCellRangeExprNode.Create(self, FWorksheet, sn,sn2, rng, flags);
end; end;
dec(AIndex); dec(AIndex);
end; end;
@ -3743,62 +3758,33 @@ begin
FCallBack(Result, FArgumentParams); FCallBack(Result, FArgumentParams);
end; end;
(*
{ TsSheetNameExprNode }
constructor TsSheetNameExprNode.Create(AParser: TsExpressionParser;
ASheetName: string);
begin
FParser := AParser;
FSheetName := ASheetName;
end;
function TsSheetNameExprNode.AsRPNItem(ANext: PRPNItem): PRPNItem;
begin
Result := ANext;
end;
function TsSheetnameExprNode.AsString: string;
begin
Result := '';
end;
*)
{ TsCellExprNode } { TsCellExprNode }
constructor TsCellExprNode.Create(AParser: TsExpressionParser; constructor TsCellExprNode.Create(AParser: TsExpressionParser;
AWorksheet: TsWorksheet; ARow,ACol: Cardinal; AFlags: TsRelFlags; AWorksheet: TsWorksheet; ASheetName: String; ARow, ACol: Cardinal;
OtherSheet: Boolean); AFlags: TsRelFlags);
begin begin
FParser := AParser; FParser := AParser;
FWorksheet := AWorksheet; FWorksheet := AWorksheet;
FSheetName := ASheetName;
FRow := ARow; FRow := ARow;
FCol := ACol; FCol := ACol;
FFlags := AFlags; FFlags := AFlags;
FCell := AWorksheet.FindCell(FRow, FCol); FCell := GetSheet.FindCell(FRow, FCol);
FOtherSheet := OtherSheet;
end;
constructor TsCellExprNode.Create(AParser: TsExpressionParser;
AWorksheet: TsWorksheet; ACellString: String; OtherSheet: Boolean);
var
r, c: Cardinal;
flags: TsRelFlags;
begin
ParseCellString(ACellString, r, c, flags);
Create(AParser, AWorksheet, r, c, flags, OtherSheet);
end; end;
function TsCellExprNode.AsRPNItem(ANext: PRPNItem): PRPNItem; function TsCellExprNode.AsRPNItem(ANext: PRPNItem): PRPNItem;
begin begin
if FIsRef then if FIsRef then
begin begin
if FOtherSheet then if Has3dLink then
Result := RPNCellRef3D(GetSheetIndex, GetRow, GetCol, FFlags, ANext) Result := RPNCellRef3D(GetSheetIndex, GetRow, GetCol, FFlags, ANext)
else else
Result := RPNCellRef(GetRow, GetCol, FFlags, ANext) Result := RPNCellRef(GetRow, GetCol, FFlags, ANext)
end else end else
begin begin
if FOtherSheet then if Has3dLink then
Result := RPNCellValue3D(GetSheetIndex, GetRow, GetCol, FFlags, ANext) Result := RPNCellValue3D(GetSheetIndex, GetRow, GetCol, FFlags, ANext)
else else
Result := RPNCellValue(GetRow, GetCol, FFlags, ANext); Result := RPNCellValue(GetRow, GetCol, FFlags, ANext);
@ -3811,15 +3797,15 @@ var
begin begin
r := Getrow; r := Getrow;
c := GetCol; c := GetCol;
if FOtherSheet then if Has3dLink then
case FParser.Dialect of case FParser.Dialect of
fdExcelA1: fdExcelA1:
Result := Format('%s!%s', [FWorksheet.Name, GetCellString(r, c, FFlags)]); Result := Format('%s!%s', [GetSheetName, GetCellString(r, c, FFlags)]);
fdExcelR1C1: fdExcelR1C1:
Result := Format('%s!%s', [FWorksheet.Name, Result := Format('%s!%s', [GetSheetName,
GetCellString_R1C1(r, c, FFlags, FParser.FSourceCell^.Row, FParser.FSourceCell^.Col)]); GetCellString_R1C1(r, c, FFlags, FParser.FSourceCell^.Row, FParser.FSourceCell^.Col)]);
fdOpenDocument: fdOpenDocument:
Result := Format('[%s.%s]', [FWorksheet.Name, GetCellString(r, c, FFlags)]); Result := Format('[%s.%s]', [GetSheetName, GetCellString(r, c, FFlags)]);
end end
else else
case FParser.Dialect of case FParser.Dialect of
@ -3854,7 +3840,7 @@ begin
Result := FCol - FParser.FSourceCell^.Col + FParser.FDestCell^.Col; Result := FCol - FParser.FSourceCell^.Col + FParser.FDestCell^.Col;
end; end;
procedure TsCellExprNode.GetNodeValue(out Result: TsExpressionResult); procedure TsCellExprNode.GetNodeValue(out AResult: TsExpressionResult);
var var
cell: PCell; cell: PCell;
begin begin
@ -3871,10 +3857,10 @@ begin
raise ECalcEngine.CreateFmt(rsCircularReference, [GetCellString(cell^.Row, cell^.Col)]); raise ECalcEngine.CreateFmt(rsCircularReference, [GetCellString(cell^.Row, cell^.Col)]);
end; end;
Result.ResultType := rtCell; AResult.ResultType := rtCell;
Result.ResRow := GetRow; AResult.ResRow := GetRow;
Result.ResCol := GetCol; AResult.ResCol := GetCol;
Result.Worksheet := FWorksheet; AResult.Worksheet := GetSheet;
end; end;
{ See: GetCol } { See: GetCol }
@ -3885,17 +3871,38 @@ begin
Result := FRow - FParser.FSourceCell^.Row + FParser.FDestCell^.Row; Result := FRow - FParser.FSourceCell^.Row + FParser.FDestCell^.Row;
end; end;
function TsCellExprNode.GetSheetIndex: Integer; function TsCellExprNode.GetSheet: TsWorksheet;
var
book: TsWorkbook;
begin begin
book := FWorksheet.Workbook; if FSheetName = '' then
Result := book.GetWorksheetIndex(FWorksheet); Result := FWorksheet
else
Result := GetWorkbook.GetWorksheetByName(FSheetName);
end;
function TsCellExprNode.GetSheetIndex: Integer;
begin
if FSheetName = '' then
Result := GetWorkbook.GetWorksheetIndex(FWorksheet)
else
Result := GetWorkbook.GetWorksheetIndex(FSheetName);
end;
function TsCellExprNode.GetSheetName: String;
begin
if FSheetName = '' then
Result := FWorksheet.Name
else
Result := FSheetName;
end;
function TsCellExprNode.GetWorkbook: TsWorkbook;
begin
Result := FWorksheet.Workbook;
end; end;
function TsCellExprNode.Has3DLink: Boolean; function TsCellExprNode.Has3DLink: Boolean;
begin begin
Result := FOtherSheet; Result := FSheetName <> '';
end; end;
function TsCellExprNode.NodeType: TsResultType; function TsCellExprNode.NodeType: TsResultType;
@ -3903,152 +3910,71 @@ begin
Result := rtCell; Result := rtCell;
end; end;
(*
{ TsSheetCellExprNode }
constructor TsSheetCellExprNode.Create(AParser: TsExpressionParser;
AWorksheet: TsWorksheet; ACellString: String);
var
r, c: Cardinal;
flags: TsRelFlags;
p: Integer;
sheetname: String;
begin
(*
case AParser.Dialect of
fdExcelA1, fdExcelR1C1: p := pos('!', ACellString);
fdOpendocument: p := pos('.', ACellString);
else raise Exception.Create('TsSheetCellExprNode: Parser dialect not supported.');
end;
sheetname := copy(ACellString, 1, p-1);
ACellString := copy(ACellString, p+1, MaxInt);
*)
ParseCellString(ACellString, r, c, flags);
Create(AParser, AWorksheet, r, c, flags);
end;
function TsSheetCellExprNode.AsRPNItem(ANext: PRPNItem): PRPNItem;
begin
if FIsRef then
Result := RPNCellRef3D(GetSheetIndex, GetRow, GetCol, FFlags, ANext)
else
Result := RPNCellValue3D(GetSheetIndex, GetRow, GetCol, FFlags, ANext);
end;
function TsSheetCellExprNode.AsString: String;
begin
case FParser.Dialect of
fdExcelA1:
Result := Format('%s!%s', [
FWorksheet.Name,
GetCellString(GetRow, GetCol, FFlags)
]);
fdExcelR1C1:
Result := Format('%s!%s', [
FWorksheet.Name,
GetCellString_R1C1(GetRow, GetCol, FFlags, FParser.FSourceCell^.Row, FParser.FSourceCell^.Col)
]);
fdOpenDocument:
Result := Format('[%s.%s]', [
FWorksheet.Name,
GetCellString(GetRow, GetCol, FFlags)
]);
else
raise Exception.Create('TsSheetCellExprNode: Parser dialect not supported.');
end;
end;
function TsSheetCellExprNode.GetSheetIndex: Integer;
var
book: TsWorkbook;
begin
book := FWorksheet.Workbook;
Result := book.GetWorksheetIndex(FWorksheet);
end;
*)
{ TsCellExprNode }
{ TsCellRangeExprNode } { TsCellRangeExprNode }
constructor TsCellRangeExprNode.Create(AParser: TsExpressionParser; constructor TsCellRangeExprNode.Create(AParser: TsExpressionParser;
AWorksheet: TsWorksheet; ARangeString: String); AWorksheet: TsWorksheet; ASheet1, ASheet2: String; ARange: TsCellRange;
var AFlags: TsRelFlags);
r1, c1, r2, c2: Cardinal;
sheet1, sheet2: String;
flags: TsRelFlags;
begin begin
ParseCellRangeString(ARangeString, sheet1, sheet2, r1, c1, r2, c2, flags); if (ASheet1 = '') and (ASheet2 <> '') then
if (sheet1 = '') then begin raise Exception.Create('Invalid parameters in cell range');
sheet1 := AWorksheet.Name;
sheet2 := sheet1;
end;
Create(AParser, AWorksheet, sheet1, sheet2, r1, c1, r2, c2,
flags, (AWorksheet.Name <> sheet1) );
end;
constructor TsCellRangeExprNode.Create(AParser: TsExpressionParser;
AWorksheet: TsWorksheet; ASheet1, ASheet2: String;
ARow1, ACol1, ARow2, ACol2: Cardinal; AFlags: tsRelFlags; Is3DRange: Boolean);
var
tmp: Integer;
begin
FParser := AParser; FParser := AParser;
FWorksheet := AWorksheet; FWorksheet := AWorksheet;
FFlags := []; FFlags := [];
if (ASheet1 = '') and (ASheet2 <> '') then F3dRange := ((ASheet1 <> '') and (ASheet2 <> '') { and (ASheet1 <> ASheet2)}) or
raise Exception.Create('Invalid parameters in cell range'); ((ASheet1 <> '') and (ASheet2 = ''));
FSheet[1] := GetWorkbook.GetWorksheetIndex(ASheet1); FSheetIndex[1] := GetWorkbook.GetWorksheetIndex(ASheet1);
if ASheet2 <> '' then if ASheet2 <> '' then
FSheet[2] := GetWorkbook.GetWorksheetIndex(ASheet2) FSheetIndex[2] := GetWorkbook.GetWorksheetIndex(ASheet2)
else else
FSheet[2] := FSheet[1]; FSheetIndex[2] := FSheetIndex[1];
EnsureOrder(FSheet[1], FSheet[2]); EnsureOrder(FSheetIndex[1], FSheetIndex[2]);
if ARow2 = Cardinal(-1) then if ARange.Row2 = Cardinal(-1) then
ARow2 := ARow1; ARange.Row2 := ARange.Row1;
if ARow1 <= ARow2 then if ARange.Row1 <= Arange.Row2 then
begin begin
FRow[1] := ARow1; FRow[1] := ARange.Row1;
FRow[2] := ARow2; FRow[2] := ARange.Row2;
FCol[1] := ACol1; FCol[1] := ARange.Col1;
if rfRelRow in AFlags then Include(FFlags, rfRelRow); if rfRelRow in AFlags then Include(FFlags, rfRelRow);
if rfRelRow2 in AFlags then Include(FFlags, rfRelRow2); if rfRelRow2 in AFlags then Include(FFlags, rfRelRow2);
end else end else
begin begin
FRow[1] := ARow2; FRow[1] := ARange.Row2;
FRow[2] := ARow1; FRow[2] := ARange.Row1;
if rfRelRow in AFlags then Include(FFlags, rfRelRow2); if rfRelRow in AFlags then Include(FFlags, rfRelRow2);
if rfRelRow2 in AFlags then Include(FFlags, rfRelRow); if rfRelRow2 in AFlags then Include(FFlags, rfRelRow);
end; end;
if ACol2 = Cardinal(-1) then if ARange.Col2 = Cardinal(-1) then
ACol2 := ACol1; ARange.Col2 := ARange.Col1;
if ACol1 <= ACol2 then if ARange.Col1 <= ARange.Col2 then
begin begin
FCol[1] := ACol1; FCol[1] := ARange.Col1;
FCol[2] := ACol2; FCol[2] := ARange.Col2;
if (rfRelCol in AFlags) then Include(FFlags, rfRelCol); if (rfRelCol in AFlags) then Include(FFlags, rfRelCol);
if (rfRelCol2 in AFlags) then Include(FFlags, rfRelCol2); if (rfRelCol2 in AFlags) then Include(FFlags, rfRelCol2);
end else end else
begin begin
FCol[1] := ACol2; FCol[1] := ARange.Col2;
FCol[2] := ACol1; FCol[2] := ARange.Col1;
if (rfRelCol in AFlags) then Include(FFlags, rfRelCol2); if (rfRelCol in AFlags) then Include(FFlags, rfRelCol2);
if (rfRelCol2 in AFlags) then Include(FFlags, rfRelCol); if (rfRelCol2 in AFlags) then Include(FFlags, rfRelCol);
end; end;
F3dRange := Is3dRange;
end; end;
function TsCellRangeExprNode.AsRPNItem(ANext: PRPNItem): PRPNItem; function TsCellRangeExprNode.AsRPNItem(ANext: PRPNItem): PRPNItem;
begin begin
if F3dRange then if F3dRange then
Result := RPNCellRange3D( Result := RPNCellRange3D(
FSheet[1], GetRow(1), Integer(GetCol(1)), FSheetIndex[1], GetRow(1), Integer(GetCol(1)),
FSheet[2], GetRow(2), Integer(GetCol(2)), FSheetIndex[2], GetRow(2), Integer(GetCol(2)),
FFlags, ANext FFlags, ANext
) )
else else
@ -4064,14 +3990,14 @@ var
r1, c1, r2, c2: Cardinal; r1, c1, r2, c2: Cardinal;
s1, s2: String; s1, s2: String;
begin begin
if FSheet[1] = -1 then if FSheetIndex[1] = -1 then
s1 := FWorksheet.Name s1 := FWorksheet.Name
else else
s1 := Workbook.GetWorksheetByIndex(FSheet[1]).Name; s1 := Workbook.GetWorksheetByIndex(FSheetIndex[1]).Name;
if FSheet[2] = -1 then if FSheetIndex[2] = -1 then
s2 := FWorksheet.Name s2 := FWorksheet.Name
else else
s2 := Workbook.GetWorksheetByIndex(FSheet[2]).Name; s2 := Workbook.GetWorksheetByIndex(FSheetIndex[2]).Name;
r1 := GetRow(1); r1 := GetRow(1);
c1 := GetCol(1); c1 := GetCol(1);
r2 := GetRow(2); r2 := GetRow(2);
@ -4134,7 +4060,7 @@ begin
begin begin
r[i] := GetRow(i); r[i] := GetRow(i);
c[i] := GetCol(i); c[i] := GetCol(i);
s[i] := FSheet[i]; s[i] := FSheetIndex[i];
end; end;
if not F3dRange then begin if not F3dRange then begin

View File

@ -944,7 +944,14 @@ begin
ASheet2 := copy(s1, p+1, MaxInt); ASheet2 := copy(s1, p+1, MaxInt);
end; end;
end; end;
Result := ParseCellRangeString(s2, ARow1, ACol1, ARow2, ACol2, AFlags);
p := pos(':', s2);
if p = 0 then begin
ARow2 := Cardinal(-1);
ACol2 := Cardinal(-1);
Result := ParseCellString(s2, ARow1, ACol1, AFlags);
end else
Result := ParseCellRangeString(s2, ARow1, ACol1, ARow2, ACol2, AFlags);
end; end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------

View File

@ -58,7 +58,7 @@ interface
uses uses
Classes, SysUtils, fpcanvas, lconvencoding, Classes, SysUtils, fpcanvas, lconvencoding,
fpsTypes, fpspreadsheet, fpsrpn, fpsTypes, fpspreadsheet,
xlscommon, xlscommon,
{$ifdef USE_NEW_OLE} {$ifdef USE_NEW_OLE}
fpolebasic, fpolebasic,
@ -1502,7 +1502,6 @@ procedure TsSpreadBIFF5Writer.WriteDefinedName(AStream: TStream;
var var
memstream: TMemoryStream; memstream: TMemoryStream;
rng: TsCellRange; rng: TsCellRange;
idx: Integer;
j: Integer; j: Integer;
begin begin
// Since this is a variable length record we begin by writing the formula // Since this is a variable length record we begin by writing the formula
@ -1761,9 +1760,7 @@ end;
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
procedure TsSpreadBIFF5Writer.WriteGlobalLinkTable(AStream: TStream); procedure TsSpreadBIFF5Writer.WriteGlobalLinkTable(AStream: TStream);
var var
sheet: TsWorksheet;
globalLinks: TsBIFFExternSheetList; globalLinks: TsBIFFExternSheetList;
sheetList: TsBIFFExternSheetList;
i: Integer; i: Integer;
begin begin
{ collect the global links data } { collect the global links data }
@ -1912,11 +1909,7 @@ end;
procedure TsSpreadBIFF5Writer.WriteLocalLinkTable(AStream: TStream; procedure TsSpreadBIFF5Writer.WriteLocalLinkTable(AStream: TStream;
AWorksheet: TsWorksheet); AWorksheet: TsWorksheet);
var var
L: TStringList; i, n: Integer;
cell: PCell;
found: Boolean;
i, j, n: Integer;
sheet: TsWorksheet;
externSheetList: TsBIFFExternSheetList; externSheetList: TsBIFFExternSheetList;
externsheet: TsBIFFExternSheet; externsheet: TsBIFFExternSheet;
begin begin
@ -1990,7 +1983,7 @@ function TsSpreadBIFF5Writer.WriteRPNSheetIndex(AStream: TStream;
var var
p: Int64; p: Int64;
externSheetList: TsBIFFExternSheetList; externSheetList: TsBIFFExternSheetList;
externSheetIdx1, externSheetIdx2: Integer; externSheetIdx: Integer;
s: String; s: String;
begin begin
if ADocumentURL <> '' then // Supporting only internal links if ADocumentURL <> '' then // Supporting only internal links
@ -2001,7 +1994,7 @@ begin
externSheetList := FLinkLists.GetLocalLinks(FWorksheet); externSheetList := FLinkLists.GetLocalLinks(FWorksheet);
s := FWorkbook.GetWorksheetByIndex(ASheet1).Name; s := FWorkbook.GetWorksheetByIndex(ASheet1).Name;
externSheetIdx1 := externSheetList.IndexOfSheet(s); externSheetIdx := externSheetList.IndexOfSheet(s);
if ASheet2 = -1 then if ASheet2 = -1 then
ASheet2 := ASheet1; ASheet2 := ASheet1;
@ -2009,7 +2002,7 @@ begin
// One-based index of the EXTERNBOOK record to which this reference belongs. // One-based index of the EXTERNBOOK record to which this reference belongs.
// For internal references ("3D references") this must be written as a // For internal references ("3D references") this must be written as a
// negative value. // negative value.
AStream.WriteWord(WordToLE(word(-(externSheetIdx1 + 1)))); AStream.WriteWord(WordToLE(word(-(externSheetIdx + 1))));
// 8 unused bytes // 8 unused bytes
AStream.WriteQWord(0); AStream.WriteQWord(0);

View File

@ -55,7 +55,7 @@ interface
uses uses
Classes, SysUtils, fpcanvas, DateUtils, contnrs, lazutf8, Classes, SysUtils, fpcanvas, DateUtils, contnrs, lazutf8,
fpstypes, fpspreadsheet, fpsrpn, xlscommon, fpstypes, fpspreadsheet, xlscommon,
{$ifdef USE_NEW_OLE} {$ifdef USE_NEW_OLE}
fpolebasic, fpolebasic,
{$else} {$else}
@ -729,7 +729,6 @@ end;
function TsBIFF8ExternSheetList.IndexOfSheets(ABookName: String; function TsBIFF8ExternSheetList.IndexOfSheets(ABookName: String;
ASheetIndex1, ASheetIndex2: Integer): Integer; ASheetIndex1, ASheetIndex2: Integer): Integer;
var var
book: TsBIFF8ExternBook;
P: PsBIFF8ExternSheet; P: PsBIFF8ExternSheet;
tmp: Integer; tmp: Integer;
idx: Integer; idx: Integer;
@ -3134,13 +3133,9 @@ end;
procedure TsSpreadBIFF8Writer.WriteDefinedNames(AStream: TStream); procedure TsSpreadBIFF8Writer.WriteDefinedNames(AStream: TStream);
var var
externbook: TsBIFF8ExternBook;
bookIdx: Integer; bookIdx: Integer;
sheet: TsWorksheet; sheet: TsWorksheet;
i: Integer; i: Integer;
idx: Word;
extSheetIdx: Integer;
sheetList: TsBIFFExternSheetList;
begin begin
if (FBiff8ExternBooks = nil) or (FBiff8ExternSheets = nil) then if (FBiff8ExternBooks = nil) or (FBiff8ExternSheets = nil) then
exit; exit;

View File

@ -1790,8 +1790,6 @@ var
ansistr: AnsiString; ansistr: AnsiString;
s: String; s: String;
sheetlist: TsBIFFExternSheetList; sheetlist: TsBIFFExternSheetList;
sheet: TsBIFFExternSheet;
idx: Integer;
begin begin
sheetList := FLinkLists.GetSheetList(AWorksheet); sheetList := FLinkLists.GetSheetList(AWorksheet);
@ -3964,7 +3962,6 @@ procedure TsSpreadBIFFWriter.WriteDefinedNames(AStream: TStream);
var var
sheet: TsWorksheet; sheet: TsWorksheet;
i: Integer; i: Integer;
idx: Word;
extSheetIdx: Integer; extSheetIdx: Integer;
sheetList: TsBIFFExternSheetList; sheetList: TsBIFFExternSheetList;
begin begin

View File

@ -709,106 +709,112 @@ var
sollValues: array of TsExpressionResult; sollValues: array of TsExpressionResult;
formula, actualformula: String; formula, actualformula: String;
begin begin
// Create test workbook TempFile := GetTempFileName;
workbook := TsWorkbook.Create;
try try
workbook.Options := workbook.Options + [boCalcBeforeSaving]; // Create test workbook
workbook := TsWorkbook.Create;
try
workbook.Options := workbook.Options + [boCalcBeforeSaving];
sheet1 := workBook.AddWorksheet('Sheet1'); sheet1 := workBook.AddWorksheet('Sheet1');
sheet2 := workbook.AddWorksheet('Sheet2'); sheet2 := workbook.AddWorksheet('Sheet2');
sheet3 := workbook.AddWorksheet('Sheet3'); sheet3 := workbook.AddWorksheet('Sheet3');
{ Write out test formulas. { Write out test formulas.
This include file creates various formulas in column A and stores This include file creates various formulas in column A and stores
the expected results in the array SollValues. } the expected results in the array SollValues. }
Row := 0; Row := 0;
TempFile := GetTempFileName; {$I testcases_calc3dformula.inc}
{$I testcases_calc3dformula.inc} workbook.WriteToFile(TempFile, AFormat, true);
workbook.WriteToFile(TempFile, AFormat, true); finally
finally workbook.Free;
workbook.Free; end;
end;
// Open the workbook // Open the workbook
workbook := TsWorkbook.Create; workbook := TsWorkbook.Create;
try try
workbook.Options := workbook.Options + [boReadFormulas]; workbook.Options := workbook.Options + [boReadFormulas];
workbook.ReadFromFile(TempFile, AFormat); workbook.ReadFromFile(TempFile, AFormat);
if AFormat = sfExcel2 then workbook.CalcFormulas;
Fail('This test should not be executed')
else
sheet1 := workbook.GetWorksheetByName('Sheet1');
if sheet1=nil then
Fail('Error in test code. Failed to get named worksheet');
for row := 0 to sheet1.GetLastRowIndex do if AFormat = sfExcel2 then
begin Fail('This test should not be executed')
cell := sheet1.FindCell(Row, 0); else
if (Cell = nil) then sheet1 := workbook.GetWorksheetByName('Sheet1');
Fail('Error in test code: failed to get cell ' + CellNotation(sheet1, Row, 0)); if sheet1=nil then
formula := sheet1.ReadAsText(cell); Fail('Error in test code. Failed to get named worksheet');
cell := sheet1.FindCell(Row, 1); for row := 0 to sheet1.GetLastRowIndex do
if (cell = nil) then begin
fail('Error in test code: Failed to get cell ' + CellNotation(sheet1, Row, 1)); cell := sheet1.FindCell(Row, 0);
case cell^.ContentType of if (Cell = nil) then
cctBool : actual := BooleanResult(cell^.BoolValue); Fail('Error in test code: failed to get cell ' + CellNotation(sheet1, Row, 0));
cctNumber : actual := FloatResult(cell^.NumberValue); formula := sheet1.ReadAsText(cell);
cctDateTime : actual := DateTimeResult(cell^.DateTimeValue);
cctUTF8String : actual := StringResult(cell^.UTF8StringValue);
cctError : actual := ErrorResult(cell^.ErrorValue);
cctEmpty : actual := EmptyResult;
else fail('ContentType not supported');
end;
actualformula := cell^.FormulaValue;
expected := SollValues[row]; cell := sheet1.FindCell(Row, 1);
// Cell does not store integers! if (cell = nil) then
if expected.ResultType = rtInteger then expected := FloatResult(expected.ResInteger); fail('Error in test code: Failed to get cell ' + CellNotation(sheet1, Row, 1));
case cell^.ContentType of
cctBool : actual := BooleanResult(cell^.BoolValue);
cctNumber : actual := FloatResult(cell^.NumberValue);
cctDateTime : actual := DateTimeResult(cell^.DateTimeValue);
cctUTF8String : actual := StringResult(cell^.UTF8StringValue);
cctError : actual := ErrorResult(cell^.ErrorValue);
cctEmpty : actual := EmptyResult;
else fail('ContentType not supported');
end;
actualformula := cell^.FormulaValue;
(* expected := SollValues[row];
// The now function result is volatile, i.e. changes continuously. The // Cell does not store integers!
// time for the soll value was created such that we can expect to have if expected.ResultType = rtInteger then expected := FloatResult(expected.ResInteger);
// the file value in the same second. Therefore we neglect the milliseconds.
if formula = '=NOW()' then begin
// Round soll value to seconds
DecodeTime(expected.ResDateTime, hr,min,sec,msec);
expected.ResDateTime := EncodeTime(hr, min, sec, 0);
// Round formula value to seconds
DecodeTime(actual.ResDateTime, hr,min,sec,msec);
actual.ResDateTime := EncodeTime(hr,min,sec,0);
end; *)
case actual.ResultType of {
rtBoolean: // The now function result is volatile, i.e. changes continuously. The
CheckEquals(BoolToStr(expected.ResBoolean), BoolToStr(actual.ResBoolean), // time for the soll value was created such that we can expect to have
'Test read calculated formula result mismatch, cell '+CellNotation(sheet1, Row, 1)); // the file value in the same second. Therefore we neglect the milliseconds.
rtFloat: if formula = '=NOW()' then begin
{$if (defined(mswindows)) or (FPC_FULLVERSION>=20701)} // Round soll value to seconds
// FPC 2.6.x and trunk on Windows need this, also FPC trunk on Linux x64 DecodeTime(expected.ResDateTime, hr,min,sec,msec);
CheckEquals(expected.ResFloat, actual.ResFloat, expected.ResDateTime := EncodeTime(hr, min, sec, 0);
'Test read calculated formula result mismatch, cell '+CellNotation(sheet1, Row, 1)); // Round formula value to seconds
{$else} DecodeTime(actual.ResDateTime, hr,min,sec,msec);
// Non-Windows: test without error margin actual.ResDateTime := EncodeTime(hr,min,sec,0);
CheckEquals(expected.ResFloat, actual.ResFloat, end; }
'Test read calculated formula result mismatch, cell '+CellNotation(sheet1, Row, 1));
{$endif} case actual.ResultType of
rtString: rtBoolean:
CheckEquals(expected.ResString, actual.ResString, CheckEquals(BoolToStr(expected.ResBoolean), BoolToStr(actual.ResBoolean),
'Test read calculated formula result mismatch, cell '+CellNotation(sheet1, Row, 1)); 'Test read calculated formula result mismatch, cell '+CellNotation(sheet1, Row, 1));
rtError: rtFloat:
CheckEquals( {$if (defined(mswindows)) or (FPC_FULLVERSION>=20701)}
GetEnumName(TypeInfo(TsErrorValue), ord(expected.ResError)), // FPC 2.6.x and trunk on Windows need this, also FPC trunk on Linux x64
GetEnumname(TypeInfo(TsErrorValue), ord(actual.ResError)), CheckEquals(expected.ResFloat, actual.ResFloat,
'Test read calculated formula error value mismatch, cell '+CellNotation(sheet1, Row, 1)); 'Test read calculated formula result mismatch, cell '+CellNotation(sheet1, Row, 1));
{$else}
// Non-Windows: test without error margin
CheckEquals(expected.ResFloat, actual.ResFloat,
'Test read calculated formula result mismatch, cell '+CellNotation(sheet1, Row, 1));
{$endif}
rtString:
CheckEquals(expected.ResString, actual.ResString,
'Test read calculated formula result mismatch, cell '+CellNotation(sheet1, Row, 1));
rtError:
CheckEquals(
GetEnumName(TypeInfo(TsErrorValue), ord(expected.ResError)),
GetEnumname(TypeInfo(TsErrorValue), ord(actual.ResError)),
'Test read calculated formula error value mismatch, cell '+CellNotation(sheet1, Row, 1));
end;
CheckEquals(formula, actualformula,
'Read formula string mismatch, cell ' +CellNotation(sheet1, Row, 1));
end; end;
CheckEquals( finally
formula, actualformula, 'Read formula string mismatch, cell ' +CellNotation(sheet1, Row, 1)); workbook.Free;
end; end;
finally finally
workbook.Free;
DeleteFile(TempFile); DeleteFile(TempFile);
end; end;
end; end;

View File

@ -12,6 +12,7 @@
sheet3.WriteText(1, 2, 'B'); // C2 = 'B' sheet3.WriteText(1, 2, 'B'); // C2 = 'B'
sheet3.WriteNumber(1, 1, 2.0); // B2 = 2.0 sheet3.WriteNumber(1, 1, 2.0); // B2 = 2.0
sheet3.WriteNumber(3, 4, 100); // E4 = 100.0
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@ -78,6 +79,13 @@
SetLength(SollValues, Row+1); SetLength(SollValues, Row+1);
SollValues[Row] := FloatResult(10.0); SollValues[Row] := FloatResult(10.0);
inc(Row); { A10 }
formula := 'SUM(Sheet2:Sheet3!E4)';
sheet1.WriteText(Row, 0, formula);
sheet1.WriteFormula(Row, 1, formula);
SetLength(SollValues, Row+1);
SollValues[Row] := FloatResult(110.0);
{ {
inc(Row); inc(Row);
formula := 'D1&Sheet2!B3%"BC"'; formula := 'D1&Sheet2!B3%"BC"';