fpspreadsheet: Fix entering new data in existing formula not erasing formula. Add corresponding unit test cases.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@6545 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2018-07-05 12:51:54 +00:00
parent 592bea80e8
commit b9979977f6
4 changed files with 406 additions and 125 deletions

View File

@@ -660,6 +660,8 @@ type
FBuiltinFontCount: Integer; FBuiltinFontCount: Integer;
FReadWriteFlag: TsReadWriteFlag; FReadWriteFlag: TsReadWriteFlag;
FCalculationLock: Integer; FCalculationLock: Integer;
FDeleteFormulaLock: Integer;
FNotificationLock: Integer;
FRebuildFormulaLock: Integer; FRebuildFormulaLock: Integer;
FActiveWorksheet: TsWorksheet; FActiveWorksheet: TsWorksheet;
FOnOpenWorkbook: TNotifyEvent; FOnOpenWorkbook: TNotifyEvent;
@@ -670,7 +672,6 @@ type
FOnRemovingWorksheet: TsWorksheetEvent; FOnRemovingWorksheet: TsWorksheetEvent;
FOnSelectWorksheet: TsWorksheetEvent; FOnSelectWorksheet: TsWorksheetEvent;
FOnReadCellData: TsWorkbookReadCellDataEvent; FOnReadCellData: TsWorkbookReadCellDataEvent;
FLockCount: Integer;
FSearchEngine: TObject; FSearchEngine: TObject;
FCryptoInfo: TsCryptoInfo; FCryptoInfo: TsCryptoInfo;
{FrevisionsCrypto: TsCryptoInfo;} // Commented out because it needs revision handling {FrevisionsCrypto: TsCryptoInfo;} // Commented out because it needs revision handling
@@ -797,6 +798,8 @@ type
function FixFormulas(ACorrection: TsFormulaCorrection; AData: Pointer; function FixFormulas(ACorrection: TsFormulaCorrection; AData: Pointer;
AParam: PtrInt): boolean; AParam: PtrInt): boolean;
procedure RebuildFormulas; procedure RebuildFormulas;
procedure LockFormulas;
procedure UnlockFormulas;
{ Clipboard } { Clipboard }
procedure CopyToClipboardStream(AStream: TStream; AFormat: TsSpreadsheetFormat; procedure CopyToClipboardStream(AStream: TStream; AFormat: TsSpreadsheetFormat;
@@ -1252,41 +1255,46 @@ begin
// Find or create the formula cell // Find or create the formula cell
lCell := GetCell(AFormula^.Row, AFormula^.Col); lCell := GetCell(AFormula^.Row, AFormula^.Col);
// Assign formula result FWorkbook.LockFormulas;
case res.ResultType of try
rtEmpty : WriteBlank(lCell, true); // Assign formula result
rtError : WriteErrorValue(lCell, res.ResError); case res.ResultType of
rtInteger : WriteNumber(lCell, res.ResInteger); rtEmpty : WriteBlank(lCell, true);
rtFloat : WriteNumber(lCell, res.ResFloat); rtError : WriteErrorValue(lCell, res.ResError);
rtDateTime : WriteDateTime(lCell, res.ResDateTime); rtInteger : WriteNumber(lCell, res.ResInteger);
rtString : WriteText(lCell, res.ResString); rtFloat : WriteNumber(lCell, res.ResFloat);
rtHyperlink : begin rtDateTime : WriteDateTime(lCell, res.ResDateTime);
link := ArgToString(res); rtString : WriteText(lCell, res.ResString);
p := pos(HYPERLINK_SEPARATOR, link); rtHyperlink : begin
if p > 0 then link := ArgToString(res);
begin p := pos(HYPERLINK_SEPARATOR, link);
txt := Copy(link, p+Length(HYPERLINK_SEPARATOR), Length(link)); if p > 0 then
link := Copy(link, 1, p-1); begin
end else txt := Copy(link, p+Length(HYPERLINK_SEPARATOR), Length(link));
txt := link; link := Copy(link, 1, p-1);
WriteHyperlink(lCell, link); end else
WriteText(lCell, txt); txt := link;
end; WriteHyperlink(lCell, link);
rtBoolean : WriteBoolValue(lCell, res.ResBoolean); WriteText(lCell, txt);
rtCell : begin end;
lCellRef := (res.Worksheet as TsWorksheet).FindCell(res.ResRow, res.ResCol); rtBoolean : WriteBoolValue(lCell, res.ResBoolean);
if lCellRef <> nil then rtCell : begin
case lCellRef^.ContentType of lCellRef := (res.Worksheet as TsWorksheet).FindCell(res.ResRow, res.ResCol);
cctNumber : WriteNumber(lCell, lCellRef^.NumberValue); if lCellRef <> nil then
cctDateTime : WriteDateTime(lCell, lCellRef^.DateTimeValue); case lCellRef^.ContentType of
cctUTF8String: WriteText(lCell, lCellRef^.UTF8StringValue); cctNumber : WriteNumber(lCell, lCellRef^.NumberValue);
cctBool : WriteBoolValue(lCell, lCellRef^.Boolvalue); cctDateTime : WriteDateTime(lCell, lCellRef^.DateTimeValue);
cctError : WriteErrorValue(lCell, lCellRef^.ErrorValue); cctUTF8String: WriteText(lCell, lCellRef^.UTF8StringValue);
cctEmpty : WriteBlank(lCell, true); cctBool : WriteBoolValue(lCell, lCellRef^.Boolvalue);
end cctError : WriteErrorValue(lCell, lCellRef^.ErrorValue);
else cctEmpty : WriteBlank(lCell, true);
WriteBlank(lCell, true); end
end; else
WriteBlank(lCell, true);
end;
end;
finally
FWorkbook.UnlockFormulas;
end; end;
// Restore the formula. Could have been erased by WriteBlank or WriteText('') // Restore the formula. Could have been erased by WriteBlank or WriteText('')
@@ -3641,7 +3649,7 @@ end;
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
procedure TsWorksheet.DeleteFormula(ACell: PCell); procedure TsWorksheet.DeleteFormula(ACell: PCell);
begin begin
if HasFormula(ACell) then begin if HasFormula(ACell) and (FWorkbook.FDeleteFormulaLock = 0) then begin
FFormulas.DeleteFormula(ACell); FFormulas.DeleteFormula(ACell);
ACell^.Flags := ACell^.Flags - [cfHasFormula, cf3dFormula]; ACell^.Flags := ACell^.Flags - [cfHasFormula, cf3dFormula];
end; end;
@@ -4090,7 +4098,7 @@ begin
FName := AName; FName := AName;
if FWorkbook.FReadWriteFlag = rwfNormal then begin if FWorkbook.FReadWriteFlag = rwfNormal then begin
FWorkbook.RebuildFormulas; FWorkbook.RebuildFormulas;
if (FWorkbook.FLockCount = 0) and Assigned(FWorkbook.FOnRenameWorksheet) then if (FWorkbook.FNotificationLock = 0) and Assigned(FWorkbook.FOnRenameWorksheet) then
FWorkbook.FOnRenameWorksheet(FWorkbook, self); FWorkbook.FOnRenameWorksheet(FWorkbook, self);
end; end;
end; end;
@@ -4595,6 +4603,9 @@ begin
end; end;
end; end;
// Delete any pre-existing formula
DeleteFormula(ACell);
ACell^.UTF8StringValue := AText; ACell^.UTF8StringValue := AText;
if (AText = '') then if (AText = '') then
begin begin
@@ -4718,6 +4729,9 @@ end;
procedure TsWorksheet.WriteNumber(ACell: PCell; ANumber: Double); procedure TsWorksheet.WriteNumber(ACell: PCell; ANumber: Double);
begin begin
if ACell <> nil then begin if ACell <> nil then begin
// Delete any pre-existing formula
DeleteFormula(ACell);
// Write number to cell
ACell^.ContentType := cctNumber; ACell^.ContentType := cctNumber;
ACell^.NumberValue := ANumber; ACell^.NumberValue := ANumber;
ChangedCell(ACell^.Row, ACell^.Col); ChangedCell(ACell^.Row, ACell^.Col);
@@ -4768,6 +4782,10 @@ begin
raise EFPSpreadsheet.Create(rsInvalidNumberFormat); raise EFPSpreadsheet.Create(rsInvalidNumberFormat);
if ACell <> nil then begin if ACell <> nil then begin
// Delete any pre-existing formula
DeleteFormula(ACell);
// Write value to cell
ACell^.ContentType := cctNumber; ACell^.ContentType := cctNumber;
ACell^.NumberValue := ANumber; ACell^.NumberValue := ANumber;
@@ -4830,6 +4848,13 @@ var
fmt: TsCellFormat; fmt: TsCellFormat;
begin begin
if ACell <> nil then begin if ACell <> nil then begin
// Delete any pre-existing formula
DeleteFormula(ACell);
// Write value to cell
ACell^.ContentType := cctNumber;
ACell^.NumberValue := ANumber;
parser := TsNumFormatParser.Create(ANumFormatString, FWorkbook.FormatSettings); parser := TsNumFormatParser.Create(ANumFormatString, FWorkbook.FormatSettings);
try try
// Format string ok? // Format string ok?
@@ -4844,9 +4869,6 @@ begin
parser.Free; parser.Free;
end; end;
ACell^.ContentType := cctNumber;
ACell^.NumberValue := ANumber;
fmt := Workbook.GetCellFormat(ACell^.FormatIndex); fmt := Workbook.GetCellFormat(ACell^.FormatIndex);
if ANumFormat <> nfGeneral then begin if ANumFormat <> nfGeneral then begin
fmt.NumberFormatIndex := Workbook.AddNumberFormat(ANumFormatString); fmt.NumberFormatIndex := Workbook.AddNumberFormat(ANumFormatString);
@@ -4930,6 +4952,9 @@ end;
procedure TsWorksheet.WriteBoolValue(ACell: PCell; AValue: Boolean); procedure TsWorksheet.WriteBoolValue(ACell: PCell; AValue: Boolean);
begin begin
if ACell <> nil then begin if ACell <> nil then begin
// Delete any pre-existing formula
DeleteFormula(ACell);
// Write value to cell
ACell^.ContentType := cctBool; ACell^.ContentType := cctBool;
ACell^.BoolValue := AValue; ACell^.BoolValue := AValue;
ChangedCell(ACell^.Row, ACell^.Col); ChangedCell(ACell^.Row, ACell^.Col);
@@ -5031,8 +5056,7 @@ begin
if ACell = nil then if ACell = nil then
exit; exit;
if HasFormula(ACell) then DeleteFormula(ACell);
DeleteFormula(ACell);
if AValue = '' then if AValue = '' then
begin begin
@@ -5258,6 +5282,10 @@ begin
raise EFPSpreadsheet.Create('[TsWorksheet.WriteCurrency] ANumFormat can only be nfCurrency or nfCurrencyRed'); raise EFPSpreadsheet.Create('[TsWorksheet.WriteCurrency] ANumFormat can only be nfCurrency or nfCurrencyRed');
if (ACell <> nil) then begin if (ACell <> nil) then begin
// Delete any pre-existing formula
DeleteFormula(ACell);
// Write value to cell
ACell^.ContentType := cctNumber; ACell^.ContentType := cctNumber;
ACell^.NumberValue := AValue; ACell^.NumberValue := AValue;
@@ -5293,6 +5321,9 @@ end;
procedure TsWorksheet.WriteDateTime(ACell: PCell; AValue: TDateTime); procedure TsWorksheet.WriteDateTime(ACell: PCell; AValue: TDateTime);
begin begin
if ACell <> nil then begin if ACell <> nil then begin
// Delete pre-existing formula
DeleteFormula(ACell);
// Write date to cell
ACell^.ContentType := cctDateTime; ACell^.ContentType := cctDateTime;
ACell^.DateTimeValue := AValue; ACell^.DateTimeValue := AValue;
ChangedCell(ACell^.Row, ACell^.Col); ChangedCell(ACell^.Row, ACell^.Col);
@@ -5341,6 +5372,10 @@ var
fmt: TsCellFormat; fmt: TsCellFormat;
begin begin
if ACell <> nil then begin if ACell <> nil then begin
// Delete any pre-existing formula
DeleteFormula(ACell);
// Write date to cell
ACell^.ContentType := cctDateTime; ACell^.ContentType := cctDateTime;
ACell^.DateTimeValue := AValue; ACell^.DateTimeValue := AValue;
@@ -5588,6 +5623,9 @@ end;
procedure TsWorksheet.WriteErrorValue(ACell: PCell; AValue: TsErrorValue); procedure TsWorksheet.WriteErrorValue(ACell: PCell; AValue: TsErrorValue);
begin begin
if ACell <> nil then begin if ACell <> nil then begin
// Delete any pre-existing formula
DeleteFormula(ACell);
// Write value to cell
ACell^.ContentType := cctError; ACell^.ContentType := cctError;
ACell^.ErrorValue := AValue; ACell^.ErrorValue := AValue;
ChangedCell(ACell^.Row, ACell^.Col); ChangedCell(ACell^.Row, ACell^.Col);
@@ -8120,7 +8158,7 @@ end;
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
procedure TsWorkbook.DisableNotifications; procedure TsWorkbook.DisableNotifications;
begin begin
inc(FLockCount); inc(FNotificationLock);
end; end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
@@ -8128,7 +8166,7 @@ end;
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
procedure TsWorkbook.EnableNotifications; procedure TsWorkbook.EnableNotifications;
begin begin
dec(FLockCount); dec(FNotificationLock);
end; end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
@@ -8136,7 +8174,7 @@ end;
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
function TsWorkbook.NotificationsEnabled: Boolean; function TsWorkbook.NotificationsEnabled: Boolean;
begin begin
Result := (FLockCount = 0); Result := (FNotificationLock = 0);
end; end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
@@ -8399,7 +8437,7 @@ begin
PrepareBeforeReading; PrepareBeforeReading;
ok := false; ok := false;
FReadWriteFlag := rwfRead; FReadWriteFlag := rwfRead;
inc(FLockCount); // This locks various notifications from being sent inc(FNotificationLock); // This locks various notifications from being sent
try try
AReader.ReadFromFile(AFileName, APassword, AParams); AReader.ReadFromFile(AFileName, APassword, AParams);
ok := true; ok := true;
@@ -8410,7 +8448,7 @@ begin
FFormatID := AFormatID; FFormatID := AFormatID;
finally finally
FReadWriteFlag := rwfNormal; FReadWriteFlag := rwfNormal;
dec(FLockCount); dec(FNotificationLock);
if ok and Assigned(FOnOpenWorkbook) then // ok is true if file has been read successfully if ok and Assigned(FOnOpenWorkbook) then // ok is true if file has been read successfully
FOnOpenWorkbook(self); // send common notification FOnOpenWorkbook(self); // send common notification
end; end;
@@ -8535,7 +8573,7 @@ begin
PrepareBeforeReading; PrepareBeforeReading;
FReadWriteFlag := rwfRead; FReadWriteFlag := rwfRead;
ok := false; ok := false;
inc(FLockCount); inc(FNotificationLock);
try try
AStream.Position := 0; AStream.Position := 0;
AReader.ReadFromStream(AStream, APassword, AParams); AReader.ReadFromStream(AStream, APassword, AParams);
@@ -8547,7 +8585,7 @@ begin
FFormatID := AFormatID; FFormatID := AFormatID;
finally finally
FReadWriteFlag := rwfNormal; FReadWriteFlag := rwfNormal;
dec(FLockCount); dec(FNotificationLock);
if ok and Assigned(FOnOpenWorkbook) then // ok is true if stream has been read successfully if ok and Assigned(FOnOpenWorkbook) then // ok is true if stream has been read successfully
FOnOpenWorkbook(self); // send common notification FOnOpenWorkbook(self); // send common notification
end; end;
@@ -8720,18 +8758,18 @@ begin
// Set the name of the new worksheet. // Set the name of the new worksheet.
// For this we turn off notification of listeners. This is not necessary here // For this we turn off notification of listeners. This is not necessary here
// because it will be repeated at end when OnAddWorksheet is executed below. // because it will be repeated at end when OnAddWorksheet is executed below.
inc(FLockCount); inc(FNotificationLock);
inc(FRebuildFormulaLock); inc(FRebuildFormulaLock);
try try
Result.Name := AName; Result.Name := AName;
finally finally
dec(FLockCount); dec(FNotificationLock);
dec(FRebuildFormulaLock); dec(FRebuildFormulaLock);
end; end;
// Send notification for new worksheet to listeners. They get the worksheet // Send notification for new worksheet to listeners. They get the worksheet
// name here as well. // name here as well.
if (FLockCount = 0) and Assigned(FOnAddWorksheet) then if (FNotificationLock = 0) and Assigned(FOnAddWorksheet) then
FOnAddWorksheet(self, Result); FOnAddWorksheet(self, Result);
// Make sure that there is an "active" worksheet // Make sure that there is an "active" worksheet
@@ -8766,7 +8804,7 @@ begin
exit; exit;
Result := AddWorksheet(AWorksheet.Name, ReplaceDuplicateName); Result := AddWorksheet(AWorksheet.Name, ReplaceDuplicateName);
inc(FLockCount); inc(FNotificationLock);
try try
for cell in AWorksheet.Cells do for cell in AWorksheet.Cells do
begin begin
@@ -8787,7 +8825,7 @@ begin
Result.CopyRow(r, r, AWorksheet); Result.CopyRow(r, r, AWorksheet);
end; end;
finally finally
dec(FLockCount); dec(FNotificationLock);
end; end;
Result.ChangedCell(r, c); Result.ChangedCell(r, c);
@@ -8971,7 +9009,7 @@ begin
FActiveWorksheet := nil; FActiveWorksheet := nil;
FWorksheets.ForEachCall(RemoveWorksheetsCallback, nil); FWorksheets.ForEachCall(RemoveWorksheetsCallback, nil);
FWorksheets.Clear; FWorksheets.Clear;
if (FLockCount = 0) and Assigned(FOnRemoveWorksheet) then if (FNotificationLock = 0) and Assigned(FOnRemoveWorksheet) then
FOnRemoveWorksheet(self, -1); FOnRemoveWorksheet(self, -1);
end; end;
@@ -9750,6 +9788,16 @@ begin
FWorksheets.ForEachCall(RebuildFormulasCallback, nil); FWorksheets.ForEachCall(RebuildFormulasCallback, nil);
end; end;
procedure TsWorkbook.LockFormulas;
begin
inc(FDeleteFormulaLock);
end;
procedure TsWorkbook.UnlockFormulas;
begin
dec(FDeleteFormulaLock);
end;
{ AData points to the deleted worksheet } { AData points to the deleted worksheet }
procedure FixWorksheetDeletedCallback(ANode: TsExprNode; AData1, AData2: Pointer; procedure FixWorksheetDeletedCallback(ANode: TsExprNode; AData1, AData2: Pointer;

View File

@@ -1913,7 +1913,7 @@ begin
if IsDateTime(ResultFormula, nf, nfs, dt) then if IsDateTime(ResultFormula, nf, nfs, dt) then
sheet.WriteDateTime(cell, dt) //, nf, nfs) sheet.WriteDateTime(cell, dt) //, nf, nfs)
else else
sheet.WriteNumber(cell, ResultFormula); //, nf, nfs); sheet.WriteNumber(cell, ResultFormula);
end; end;
{ Formula token array } { Formula token array }

View File

@@ -640,6 +640,7 @@ var
dataStr: String; dataStr: String;
formulaStr: String; formulaStr: String;
formula: PsFormula; formula: PsFormula;
isSharedFormula: Boolean;
nodeName: String; nodeName: String;
sstIndex: Integer; sstIndex: Integer;
number: Double; number: Double;
@@ -699,7 +700,7 @@ begin
dataStr := dataStr + GetNodeValue(tnode); dataStr := dataStr + GetNodeValue(tnode);
tnode := tnode.NextSibling; tnode := tnode.NextSibling;
end; end;
end else end;
if (boReadFormulas in book.Options) and (nodeName = 'f') then if (boReadFormulas in book.Options) and (nodeName = 'f') then
begin begin
// Formula to cell // Formula to cell
@@ -755,77 +756,83 @@ begin
datanode := datanode.NextSibling; datanode := datanode.NextSibling;
end; end;
// get data type book.LockFormulas; // Protect formulas from being deleted by the WriteXXXX calls
s := GetAttrValue(ANode, 't'); // "t" = data type try
if (s = '') and (dataStr = '') then // get data type
sheet.WriteBlank(cell, true) // true --> do not erase the formula!!! s := GetAttrValue(ANode, 't'); // "t" = data type
else if (s = '') and (dataStr = '') then
if (s = '') or (s = 'n') then begin sheet.WriteBlank(cell, true) // true --> do not erase the formula!!!
// Number or date/time, depending on format else
number := StrToFloat(dataStr, FPointSeparatorSettings); if (s = '') or (s = 'n') then begin
if IsDateTimeFormat(numFmt) then // Number or date/time, depending on format
begin number := StrToFloat(dataStr, FPointSeparatorSettings);
if not IsTimeIntervalFormat(numFmt) then // no correction of time origin for "time interval" format if IsDateTimeFormat(numFmt) then
number := ConvertExcelDateTimeToDateTime(number, FDateMode); begin
sheet.WriteDateTime(cell, number); if not IsTimeIntervalFormat(numFmt) then // no correction of time origin for "time interval" format
number := ConvertExcelDateTimeToDateTime(number, FDateMode);
sheet.WriteDateTime(cell, number);
end
else if IsTextFormat(numFmt) then
sheet.WriteText(cell, dataStr)
else
sheet.WriteNumber(cell, number);
end end
else if IsTextFormat(numFmt) then
sheet.WriteText(cell, dataStr)
else else
sheet.WriteNumber(cell, number); if s = 's' then begin
end // String from shared strings table
else sstIndex := StrToInt(dataStr);
if s = 's' then begin sheet.WriteText(cell, FSharedStrings[sstIndex]);
// String from shared strings table // Read rich-text parameters from the stream stored in the Objects of the stringlist
sstIndex := StrToInt(dataStr); if FSharedStrings.Objects[sstIndex] <> nil then
sheet.WriteText(cell, FSharedStrings[sstIndex]); begin
// Read rich-text parameters from the stream stored in the Objects of the stringlist ms := TMemoryStream(FSharedStrings.Objects[sstIndex]);
if FSharedStrings.Objects[sstIndex] <> nil then ms.Position := 0;
begin n := ms.ReadWord; // Count of array elements
ms := TMemoryStream(FSharedStrings.Objects[sstIndex]); SetLength(cell^.RichTextParams, n);
ms.Position := 0; ms.ReadBuffer(cell^.RichTextParams[0], n*SizeOf(TsRichTextParam));
n := ms.ReadWord; // Count of array elements end;
SetLength(cell^.RichTextParams, n); end else
ms.ReadBuffer(cell^.RichTextParams[0], n*SizeOf(TsRichTextParam)); if (s = 'str') or (s = 'inlineStr') then begin
end; // literal string
end else sheet.WriteText(cell, datastr);
if (s = 'str') or (s = 'inlineStr') then begin end else
// literal string if s = 'b' then
// formulaStr := cell^.FormulaValue; // boolean
sheet.WriteText(cell, datastr); sheet.WriteBoolValue(cell, dataStr='1')
// cell^.FormulaValue := formulaStr;
end else
if s = 'b' then
// boolean
sheet.WriteBoolValue(cell, dataStr='1')
else
if s = 'e' then begin
// error value
if dataStr = '#NULL!' then
sheet.WriteErrorValue(cell, errEmptyIntersection)
else if dataStr = '#DIV/0!' then
sheet.WriteErrorValue(cell, errDivideByZero)
else if dataStr = '#VALUE!' then
sheet.WriteErrorValue(cell, errWrongType)
else if dataStr = '#REF!' then
sheet.WriteErrorValue(cell, errIllegalRef)
else if dataStr = '#NAME?' then
sheet.WriteErrorValue(cell, errWrongName)
else if dataStr = '#NUM!' then
sheet.WriteErrorValue(cell, errOverflow)
else if dataStr = '#N/A' then
sheet.WriteErrorValue(cell, errArgError)
else if dataStr = '' then
// rare case...
// see http://forum.lazarus.freepascal.org/index.php/topic,38726.0.html
sheet.WriteBlank(cell)
else else
raise EFPSpreadsheetReader.Create(rsUnknownErrorType); if s = 'e' then begin
end else // error value
raise EFPSpreadsheetReader.Create(rsUnknownDataType); if dataStr = '#NULL!' then
sheet.WriteErrorValue(cell, errEmptyIntersection)
else if dataStr = '#DIV/0!' then
sheet.WriteErrorValue(cell, errDivideByZero)
else if dataStr = '#VALUE!' then
sheet.WriteErrorValue(cell, errWrongType)
else if dataStr = '#REF!' then
sheet.WriteErrorValue(cell, errIllegalRef)
else if dataStr = '#NAME?' then
sheet.WriteErrorValue(cell, errWrongName)
else if dataStr = '#NUM!' then
sheet.WriteErrorValue(cell, errOverflow)
else if dataStr = '#N/A' then
sheet.WriteErrorValue(cell, errArgError)
else if dataStr = '' then
// rare case...
// see http://forum.lazarus.freepascal.org/index.php/topic,38726.0.html
sheet.WriteBlank(cell)
else
raise EFPSpreadsheetReader.Create(rsUnknownErrorType);
if FIsVirtualMode then
book.OnReadCellData(book, rowIndex, colIndex, cell); end else
raise EFPSpreadsheetReader.Create(rsUnknownDataType);
if FIsVirtualMode then
book.OnReadCellData(book, rowIndex, colIndex, cell);
finally
book.UnlockFormulas;
end;
end; end;
procedure TsSpreadOOXMLReader.ReadCellXfs(ANode: TDOMNode); procedure TsSpreadOOXMLReader.ReadCellXfs(ANode: TDOMNode);

View File

@@ -38,6 +38,7 @@ type
procedure Test_Write_Read_CalcFormulas(AFormat: TsSpreadsheetformat; procedure Test_Write_Read_CalcFormulas(AFormat: TsSpreadsheetformat;
UseRPNFormula: Boolean); UseRPNFormula: Boolean);
procedure Test_Write_Read_Calc3DFormulas(AFormat: TsSpreadsheetFormat); procedure Test_Write_Read_Calc3DFormulas(AFormat: TsSpreadsheetFormat);
procedure Test_OverwriteFormulaTest(ATest: Integer; AFormat: TsSpreadsheetFormat);
published published
// Writes out formulas & reads them back. // Writes out formulas & reads them back.
@@ -83,7 +84,30 @@ type
procedure Test_Write_Read_Calc3DFormula_OOXML; procedure Test_Write_Read_Calc3DFormula_OOXML;
procedure Test_Write_Read_Calc3DFormula_ODS; procedure Test_Write_Read_Calc3DFormula_ODS;
{ Overwrite formula with other content }
procedure Test_OverwriteFormula_Number_BIFF2;
procedure Test_OverwriteFormula_Number_BIFF5;
procedure Test_OverwriteFormula_Number_BIFF8;
procedure Test_OverwriteFormula_Number_OOXML;
procedure Test_OverwriteFormula_Number_ODS;
procedure Test_OverwriteFormula_Text_BIFF2;
procedure Test_OverwriteFormula_Text_BIFF5;
procedure Test_OverwriteFormula_Text_BIFF8;
procedure Test_OverwriteFormula_Text_OOXML;
procedure Test_OverwriteFormula_Text_ODS;
procedure Test_OverwriteFormula_Bool_BIFF2;
procedure Test_OverwriteFormula_Bool_BIFF5;
procedure Test_OverwriteFormula_Bool_BIFF8;
procedure Test_OverwriteFormula_Bool_OOXML;
procedure Test_OverwriteFormula_Bool_ODS;
procedure Test_OverwriteFormula_Error_BIFF2;
procedure Test_OverwriteFormula_Error_BIFF5;
procedure Test_OverwriteFormula_Error_BIFF8;
procedure Test_OverwriteFormula_Error_OOXML;
procedure Test_OverwriteFormula_Error_ODS;
end; end;
implementation implementation
@@ -842,6 +866,208 @@ begin
end; end;
{------------------------------------------------------------------------------}
procedure TSpreadWriteReadFormulaTests.Test_OverwriteFormulaTest(ATest: Integer;
AFormat: TsSpreadsheetFormat);
type
TSollValues = record
NumberValue: Integer;
TextValue: String;
BoolValue: Boolean;
ErrorValue: TsErrorValue;
end;
const
SollValue: TSollValues = (
NumberValue: 100;
TextValue: 'abc';
BoolValue: false;
ErrorValue: errIllegalRef
);
var
tempfile: String;
book: TsWorkbook;
sheet: TsWorksheet;
x: Float;
s: String;
b: Boolean;
err: TsErrorValue;
cell: PCell;
begin
tempFile := GetTempFileName;
book := TsWorkbook.Create;
try
book.Options := book.Options + [boAutoCalc];
sheet := book.AddWorksheet('Test');
sheet.WriteFormula(0, 0, '=1+1');
case ATest of
0: sheet.WriteNumber(0, 0, sollValue.NumberValue);
1: sheet.WriteText(0, 0, sollValue.TextValue);
2: sheet.WriteBoolValue(0, 0, sollValue.BoolValue);
3: sheet.WriteErrorValue(0, 0, sollValue.ErrorValue);
end;
cell := sheet.FindCell(0, 0);
CheckEquals(true, cell <> nil, 'Cell A1 not found before saving');
case ATest of
0: begin
x := sheet.ReadAsNumber(0, 0);
CheckEquals(sollValue.NumberValue, x, 'Cell number content mismatch before saving');
end;
1: begin
s := sheet.ReadAsText(0, 0);
CheckEquals(sollValue.TextValue, s, 'Cell string content mismatch before saving');
end;
2: begin
b := cell^.BoolValue;
CheckEquals(sollValue.BoolValue, b, 'Cell boolean content mismatch before saving');
end;
3: begin
err := cell^.ErrorValue;
CheckEquals(ord(sollValue.ErrorValue), ord(err), 'Cell error ontent mismatch before saving');
end;
end;
book.WriteToFile(tempFile, AFormat, true);
finally
book.Free;
end;
book := TsWorkbook.Create;
try
book.Options := book.Options + [boReadFormulas, boAutoCalc];
book.ReadFromFile(tempFile, AFormat);
sheet := book.GetWorksheetByIndex(0);
cell := sheet.FindCell(0, 0);
CheckEquals(true, cell <> nil, 'Cell A1 not found after reading');
case ATest of
0: begin
x := sheet.ReadAsNumber(Cell);
CheckEquals(sollValue.NumberValue, x, 'Cell number content mismatch before saving');
end;
1: begin
s := sheet.ReadAsText(cell);
CheckEquals(sollValue.TextValue, s, 'Cell string content mismatch before saving');
end;
2: begin
b := cell^.BoolValue;
CheckEquals(sollValue.BoolValue, b, 'Cell boolean content mismatch before saving');
end;
3: begin
err := cell^.ErrorValue;
CheckEquals(ord(sollValue.ErrorValue), ord(err), 'Cell error ontent mismatch before saving');
end;
end;
finally
book.Free;
deleteFile(tempFile);
end;
end;
procedure TSpreadWriteReadFormulaTests.Test_OverwriteFormula_Number_BIFF2;
begin
Test_OverwriteFormulaTest(0, sfExcel2);
end;
procedure TSpreadWriteReadFormulaTests.Test_OverwriteFormula_Number_BIFF5;
begin
Test_OverwriteFormulaTest(0, sfExcel5);
end;
procedure TSpreadWriteReadFormulaTests.Test_OverwriteFormula_Number_BIFF8;
begin
Test_OverwriteFormulaTest(0, sfExcel8);
end;
procedure TSpreadWriteReadFormulaTests.Test_OverwriteFormula_Number_OOXML;
begin
Test_OverwriteFormulaTest(0, sfOOXML);
end;
procedure TSpreadWriteReadFormulaTests.Test_OverwriteFormula_Number_ODS;
begin
Test_OverwriteFormulaTest(0, sfOpenDocument);
end;
procedure TSpreadWriteReadFormulaTests.Test_OverwriteFormula_Text_BIFF2;
begin
Test_OverwriteFormulaTest(1, sfExcel2);
end;
procedure TSpreadWriteReadFormulaTests.Test_OverwriteFormula_Text_BIFF5;
begin
Test_OverwriteFormulaTest(1, sfExcel5);
end;
procedure TSpreadWriteReadFormulaTests.Test_OverwriteFormula_Text_BIFF8;
begin
Test_OverwriteFormulaTest(1, sfExcel8);
end;
procedure TSpreadWriteReadFormulaTests.Test_OverwriteFormula_Text_OOXML;
begin
Test_OverwriteFormulaTest(1, sfOOXML);
end;
procedure TSpreadWriteReadFormulaTests.Test_OverwriteFormula_Text_ODS;
begin
Test_OverwriteFormulaTest(1, sfOpenDocument);
end;
procedure TSpreadWriteReadFormulaTests.Test_OverwriteFormula_Bool_BIFF2;
begin
Test_OverwriteFormulaTest(2, sfExcel2);
end;
procedure TSpreadWriteReadFormulaTests.Test_OverwriteFormula_Bool_BIFF5;
begin
Test_OverwriteFormulaTest(2, sfExcel5);
end;
procedure TSpreadWriteReadFormulaTests.Test_OverwriteFormula_Bool_BIFF8;
begin
Test_OverwriteFormulaTest(2, sfExcel8);
end;
procedure TSpreadWriteReadFormulaTests.Test_OverwriteFormula_Bool_OOXML;
begin
Test_OverwriteFormulaTest(2, sfOOXML);
end;
procedure TSpreadWriteReadFormulaTests.Test_OverwriteFormula_Bool_ODS;
begin
Test_OverwriteFormulaTest(2, sfOpenDocument);
end;
procedure TSpreadWriteReadFormulaTests.Test_OverwriteFormula_Error_BIFF2;
begin
Test_OverwriteFormulaTest(3, sfExcel2);
end;
procedure TSpreadWriteReadFormulaTests.Test_OverwriteFormula_Error_BIFF5;
begin
Test_OverwriteFormulaTest(3, sfExcel5);
end;
procedure TSpreadWriteReadFormulaTests.Test_OverwriteFormula_Error_BIFF8;
begin
Test_OverwriteFormulaTest(3, sfExcel8);
end;
procedure TSpreadWriteReadFormulaTests.Test_OverwriteFormula_Error_OOXML;
begin
Test_OverwriteFormulaTest(3, sfOOXML);
end;
procedure TSpreadWriteReadFormulaTests.Test_OverwriteFormula_Error_ODS;
begin
Test_OverwriteFormulaTest(3, sfOpenDocument);
end;
initialization initialization
// Register so these tests are included in a full run // Register so these tests are included in a full run
RegisterTest(TSpreadWriteReadFormulaTests); RegisterTest(TSpreadWriteReadFormulaTests);