You've already forked lazarus-ccr
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:
@ -660,6 +660,8 @@ type
|
||||
FBuiltinFontCount: Integer;
|
||||
FReadWriteFlag: TsReadWriteFlag;
|
||||
FCalculationLock: Integer;
|
||||
FDeleteFormulaLock: Integer;
|
||||
FNotificationLock: Integer;
|
||||
FRebuildFormulaLock: Integer;
|
||||
FActiveWorksheet: TsWorksheet;
|
||||
FOnOpenWorkbook: TNotifyEvent;
|
||||
@ -670,7 +672,6 @@ type
|
||||
FOnRemovingWorksheet: TsWorksheetEvent;
|
||||
FOnSelectWorksheet: TsWorksheetEvent;
|
||||
FOnReadCellData: TsWorkbookReadCellDataEvent;
|
||||
FLockCount: Integer;
|
||||
FSearchEngine: TObject;
|
||||
FCryptoInfo: TsCryptoInfo;
|
||||
{FrevisionsCrypto: TsCryptoInfo;} // Commented out because it needs revision handling
|
||||
@ -797,6 +798,8 @@ type
|
||||
function FixFormulas(ACorrection: TsFormulaCorrection; AData: Pointer;
|
||||
AParam: PtrInt): boolean;
|
||||
procedure RebuildFormulas;
|
||||
procedure LockFormulas;
|
||||
procedure UnlockFormulas;
|
||||
|
||||
{ Clipboard }
|
||||
procedure CopyToClipboardStream(AStream: TStream; AFormat: TsSpreadsheetFormat;
|
||||
@ -1252,41 +1255,46 @@ begin
|
||||
|
||||
// Find or create the formula cell
|
||||
lCell := GetCell(AFormula^.Row, AFormula^.Col);
|
||||
// Assign formula result
|
||||
case res.ResultType of
|
||||
rtEmpty : WriteBlank(lCell, true);
|
||||
rtError : WriteErrorValue(lCell, res.ResError);
|
||||
rtInteger : WriteNumber(lCell, res.ResInteger);
|
||||
rtFloat : WriteNumber(lCell, res.ResFloat);
|
||||
rtDateTime : WriteDateTime(lCell, res.ResDateTime);
|
||||
rtString : WriteText(lCell, res.ResString);
|
||||
rtHyperlink : begin
|
||||
link := ArgToString(res);
|
||||
p := pos(HYPERLINK_SEPARATOR, link);
|
||||
if p > 0 then
|
||||
begin
|
||||
txt := Copy(link, p+Length(HYPERLINK_SEPARATOR), Length(link));
|
||||
link := Copy(link, 1, p-1);
|
||||
end else
|
||||
txt := link;
|
||||
WriteHyperlink(lCell, link);
|
||||
WriteText(lCell, txt);
|
||||
end;
|
||||
rtBoolean : WriteBoolValue(lCell, res.ResBoolean);
|
||||
rtCell : begin
|
||||
lCellRef := (res.Worksheet as TsWorksheet).FindCell(res.ResRow, res.ResCol);
|
||||
if lCellRef <> nil then
|
||||
case lCellRef^.ContentType of
|
||||
cctNumber : WriteNumber(lCell, lCellRef^.NumberValue);
|
||||
cctDateTime : WriteDateTime(lCell, lCellRef^.DateTimeValue);
|
||||
cctUTF8String: WriteText(lCell, lCellRef^.UTF8StringValue);
|
||||
cctBool : WriteBoolValue(lCell, lCellRef^.Boolvalue);
|
||||
cctError : WriteErrorValue(lCell, lCellRef^.ErrorValue);
|
||||
cctEmpty : WriteBlank(lCell, true);
|
||||
end
|
||||
else
|
||||
WriteBlank(lCell, true);
|
||||
end;
|
||||
FWorkbook.LockFormulas;
|
||||
try
|
||||
// Assign formula result
|
||||
case res.ResultType of
|
||||
rtEmpty : WriteBlank(lCell, true);
|
||||
rtError : WriteErrorValue(lCell, res.ResError);
|
||||
rtInteger : WriteNumber(lCell, res.ResInteger);
|
||||
rtFloat : WriteNumber(lCell, res.ResFloat);
|
||||
rtDateTime : WriteDateTime(lCell, res.ResDateTime);
|
||||
rtString : WriteText(lCell, res.ResString);
|
||||
rtHyperlink : begin
|
||||
link := ArgToString(res);
|
||||
p := pos(HYPERLINK_SEPARATOR, link);
|
||||
if p > 0 then
|
||||
begin
|
||||
txt := Copy(link, p+Length(HYPERLINK_SEPARATOR), Length(link));
|
||||
link := Copy(link, 1, p-1);
|
||||
end else
|
||||
txt := link;
|
||||
WriteHyperlink(lCell, link);
|
||||
WriteText(lCell, txt);
|
||||
end;
|
||||
rtBoolean : WriteBoolValue(lCell, res.ResBoolean);
|
||||
rtCell : begin
|
||||
lCellRef := (res.Worksheet as TsWorksheet).FindCell(res.ResRow, res.ResCol);
|
||||
if lCellRef <> nil then
|
||||
case lCellRef^.ContentType of
|
||||
cctNumber : WriteNumber(lCell, lCellRef^.NumberValue);
|
||||
cctDateTime : WriteDateTime(lCell, lCellRef^.DateTimeValue);
|
||||
cctUTF8String: WriteText(lCell, lCellRef^.UTF8StringValue);
|
||||
cctBool : WriteBoolValue(lCell, lCellRef^.Boolvalue);
|
||||
cctError : WriteErrorValue(lCell, lCellRef^.ErrorValue);
|
||||
cctEmpty : WriteBlank(lCell, true);
|
||||
end
|
||||
else
|
||||
WriteBlank(lCell, true);
|
||||
end;
|
||||
end;
|
||||
finally
|
||||
FWorkbook.UnlockFormulas;
|
||||
end;
|
||||
|
||||
// Restore the formula. Could have been erased by WriteBlank or WriteText('')
|
||||
@ -3641,7 +3649,7 @@ end;
|
||||
-------------------------------------------------------------------------------}
|
||||
procedure TsWorksheet.DeleteFormula(ACell: PCell);
|
||||
begin
|
||||
if HasFormula(ACell) then begin
|
||||
if HasFormula(ACell) and (FWorkbook.FDeleteFormulaLock = 0) then begin
|
||||
FFormulas.DeleteFormula(ACell);
|
||||
ACell^.Flags := ACell^.Flags - [cfHasFormula, cf3dFormula];
|
||||
end;
|
||||
@ -4090,7 +4098,7 @@ begin
|
||||
FName := AName;
|
||||
if FWorkbook.FReadWriteFlag = rwfNormal then begin
|
||||
FWorkbook.RebuildFormulas;
|
||||
if (FWorkbook.FLockCount = 0) and Assigned(FWorkbook.FOnRenameWorksheet) then
|
||||
if (FWorkbook.FNotificationLock = 0) and Assigned(FWorkbook.FOnRenameWorksheet) then
|
||||
FWorkbook.FOnRenameWorksheet(FWorkbook, self);
|
||||
end;
|
||||
end;
|
||||
@ -4595,6 +4603,9 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
// Delete any pre-existing formula
|
||||
DeleteFormula(ACell);
|
||||
|
||||
ACell^.UTF8StringValue := AText;
|
||||
if (AText = '') then
|
||||
begin
|
||||
@ -4718,6 +4729,9 @@ end;
|
||||
procedure TsWorksheet.WriteNumber(ACell: PCell; ANumber: Double);
|
||||
begin
|
||||
if ACell <> nil then begin
|
||||
// Delete any pre-existing formula
|
||||
DeleteFormula(ACell);
|
||||
// Write number to cell
|
||||
ACell^.ContentType := cctNumber;
|
||||
ACell^.NumberValue := ANumber;
|
||||
ChangedCell(ACell^.Row, ACell^.Col);
|
||||
@ -4768,6 +4782,10 @@ begin
|
||||
raise EFPSpreadsheet.Create(rsInvalidNumberFormat);
|
||||
|
||||
if ACell <> nil then begin
|
||||
// Delete any pre-existing formula
|
||||
DeleteFormula(ACell);
|
||||
|
||||
// Write value to cell
|
||||
ACell^.ContentType := cctNumber;
|
||||
ACell^.NumberValue := ANumber;
|
||||
|
||||
@ -4830,6 +4848,13 @@ var
|
||||
fmt: TsCellFormat;
|
||||
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);
|
||||
try
|
||||
// Format string ok?
|
||||
@ -4844,9 +4869,6 @@ begin
|
||||
parser.Free;
|
||||
end;
|
||||
|
||||
ACell^.ContentType := cctNumber;
|
||||
ACell^.NumberValue := ANumber;
|
||||
|
||||
fmt := Workbook.GetCellFormat(ACell^.FormatIndex);
|
||||
if ANumFormat <> nfGeneral then begin
|
||||
fmt.NumberFormatIndex := Workbook.AddNumberFormat(ANumFormatString);
|
||||
@ -4930,6 +4952,9 @@ end;
|
||||
procedure TsWorksheet.WriteBoolValue(ACell: PCell; AValue: Boolean);
|
||||
begin
|
||||
if ACell <> nil then begin
|
||||
// Delete any pre-existing formula
|
||||
DeleteFormula(ACell);
|
||||
// Write value to cell
|
||||
ACell^.ContentType := cctBool;
|
||||
ACell^.BoolValue := AValue;
|
||||
ChangedCell(ACell^.Row, ACell^.Col);
|
||||
@ -5031,8 +5056,7 @@ begin
|
||||
if ACell = nil then
|
||||
exit;
|
||||
|
||||
if HasFormula(ACell) then
|
||||
DeleteFormula(ACell);
|
||||
DeleteFormula(ACell);
|
||||
|
||||
if AValue = '' then
|
||||
begin
|
||||
@ -5258,6 +5282,10 @@ begin
|
||||
raise EFPSpreadsheet.Create('[TsWorksheet.WriteCurrency] ANumFormat can only be nfCurrency or nfCurrencyRed');
|
||||
|
||||
if (ACell <> nil) then begin
|
||||
// Delete any pre-existing formula
|
||||
DeleteFormula(ACell);
|
||||
|
||||
// Write value to cell
|
||||
ACell^.ContentType := cctNumber;
|
||||
ACell^.NumberValue := AValue;
|
||||
|
||||
@ -5293,6 +5321,9 @@ end;
|
||||
procedure TsWorksheet.WriteDateTime(ACell: PCell; AValue: TDateTime);
|
||||
begin
|
||||
if ACell <> nil then begin
|
||||
// Delete pre-existing formula
|
||||
DeleteFormula(ACell);
|
||||
// Write date to cell
|
||||
ACell^.ContentType := cctDateTime;
|
||||
ACell^.DateTimeValue := AValue;
|
||||
ChangedCell(ACell^.Row, ACell^.Col);
|
||||
@ -5341,6 +5372,10 @@ var
|
||||
fmt: TsCellFormat;
|
||||
begin
|
||||
if ACell <> nil then begin
|
||||
// Delete any pre-existing formula
|
||||
DeleteFormula(ACell);
|
||||
|
||||
// Write date to cell
|
||||
ACell^.ContentType := cctDateTime;
|
||||
ACell^.DateTimeValue := AValue;
|
||||
|
||||
@ -5588,6 +5623,9 @@ end;
|
||||
procedure TsWorksheet.WriteErrorValue(ACell: PCell; AValue: TsErrorValue);
|
||||
begin
|
||||
if ACell <> nil then begin
|
||||
// Delete any pre-existing formula
|
||||
DeleteFormula(ACell);
|
||||
// Write value to cell
|
||||
ACell^.ContentType := cctError;
|
||||
ACell^.ErrorValue := AValue;
|
||||
ChangedCell(ACell^.Row, ACell^.Col);
|
||||
@ -8120,7 +8158,7 @@ end;
|
||||
-------------------------------------------------------------------------------}
|
||||
procedure TsWorkbook.DisableNotifications;
|
||||
begin
|
||||
inc(FLockCount);
|
||||
inc(FNotificationLock);
|
||||
end;
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
@ -8128,7 +8166,7 @@ end;
|
||||
-------------------------------------------------------------------------------}
|
||||
procedure TsWorkbook.EnableNotifications;
|
||||
begin
|
||||
dec(FLockCount);
|
||||
dec(FNotificationLock);
|
||||
end;
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
@ -8136,7 +8174,7 @@ end;
|
||||
-------------------------------------------------------------------------------}
|
||||
function TsWorkbook.NotificationsEnabled: Boolean;
|
||||
begin
|
||||
Result := (FLockCount = 0);
|
||||
Result := (FNotificationLock = 0);
|
||||
end;
|
||||
|
||||
{@@ ----------------------------------------------------------------------------
|
||||
@ -8399,7 +8437,7 @@ begin
|
||||
PrepareBeforeReading;
|
||||
ok := false;
|
||||
FReadWriteFlag := rwfRead;
|
||||
inc(FLockCount); // This locks various notifications from being sent
|
||||
inc(FNotificationLock); // This locks various notifications from being sent
|
||||
try
|
||||
AReader.ReadFromFile(AFileName, APassword, AParams);
|
||||
ok := true;
|
||||
@ -8410,7 +8448,7 @@ begin
|
||||
FFormatID := AFormatID;
|
||||
finally
|
||||
FReadWriteFlag := rwfNormal;
|
||||
dec(FLockCount);
|
||||
dec(FNotificationLock);
|
||||
if ok and Assigned(FOnOpenWorkbook) then // ok is true if file has been read successfully
|
||||
FOnOpenWorkbook(self); // send common notification
|
||||
end;
|
||||
@ -8535,7 +8573,7 @@ begin
|
||||
PrepareBeforeReading;
|
||||
FReadWriteFlag := rwfRead;
|
||||
ok := false;
|
||||
inc(FLockCount);
|
||||
inc(FNotificationLock);
|
||||
try
|
||||
AStream.Position := 0;
|
||||
AReader.ReadFromStream(AStream, APassword, AParams);
|
||||
@ -8547,7 +8585,7 @@ begin
|
||||
FFormatID := AFormatID;
|
||||
finally
|
||||
FReadWriteFlag := rwfNormal;
|
||||
dec(FLockCount);
|
||||
dec(FNotificationLock);
|
||||
if ok and Assigned(FOnOpenWorkbook) then // ok is true if stream has been read successfully
|
||||
FOnOpenWorkbook(self); // send common notification
|
||||
end;
|
||||
@ -8720,18 +8758,18 @@ begin
|
||||
// Set the name of the new worksheet.
|
||||
// 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.
|
||||
inc(FLockCount);
|
||||
inc(FNotificationLock);
|
||||
inc(FRebuildFormulaLock);
|
||||
try
|
||||
Result.Name := AName;
|
||||
finally
|
||||
dec(FLockCount);
|
||||
dec(FNotificationLock);
|
||||
dec(FRebuildFormulaLock);
|
||||
end;
|
||||
|
||||
// Send notification for new worksheet to listeners. They get the worksheet
|
||||
// name here as well.
|
||||
if (FLockCount = 0) and Assigned(FOnAddWorksheet) then
|
||||
if (FNotificationLock = 0) and Assigned(FOnAddWorksheet) then
|
||||
FOnAddWorksheet(self, Result);
|
||||
|
||||
// Make sure that there is an "active" worksheet
|
||||
@ -8766,7 +8804,7 @@ begin
|
||||
exit;
|
||||
|
||||
Result := AddWorksheet(AWorksheet.Name, ReplaceDuplicateName);
|
||||
inc(FLockCount);
|
||||
inc(FNotificationLock);
|
||||
try
|
||||
for cell in AWorksheet.Cells do
|
||||
begin
|
||||
@ -8787,7 +8825,7 @@ begin
|
||||
Result.CopyRow(r, r, AWorksheet);
|
||||
end;
|
||||
finally
|
||||
dec(FLockCount);
|
||||
dec(FNotificationLock);
|
||||
end;
|
||||
|
||||
Result.ChangedCell(r, c);
|
||||
@ -8971,7 +9009,7 @@ begin
|
||||
FActiveWorksheet := nil;
|
||||
FWorksheets.ForEachCall(RemoveWorksheetsCallback, nil);
|
||||
FWorksheets.Clear;
|
||||
if (FLockCount = 0) and Assigned(FOnRemoveWorksheet) then
|
||||
if (FNotificationLock = 0) and Assigned(FOnRemoveWorksheet) then
|
||||
FOnRemoveWorksheet(self, -1);
|
||||
end;
|
||||
|
||||
@ -9750,6 +9788,16 @@ begin
|
||||
FWorksheets.ForEachCall(RebuildFormulasCallback, nil);
|
||||
end;
|
||||
|
||||
procedure TsWorkbook.LockFormulas;
|
||||
begin
|
||||
inc(FDeleteFormulaLock);
|
||||
end;
|
||||
|
||||
procedure TsWorkbook.UnlockFormulas;
|
||||
begin
|
||||
dec(FDeleteFormulaLock);
|
||||
end;
|
||||
|
||||
|
||||
{ AData points to the deleted worksheet }
|
||||
procedure FixWorksheetDeletedCallback(ANode: TsExprNode; AData1, AData2: Pointer;
|
||||
|
@ -1913,7 +1913,7 @@ begin
|
||||
if IsDateTime(ResultFormula, nf, nfs, dt) then
|
||||
sheet.WriteDateTime(cell, dt) //, nf, nfs)
|
||||
else
|
||||
sheet.WriteNumber(cell, ResultFormula); //, nf, nfs);
|
||||
sheet.WriteNumber(cell, ResultFormula);
|
||||
end;
|
||||
|
||||
{ Formula token array }
|
||||
|
@ -640,6 +640,7 @@ var
|
||||
dataStr: String;
|
||||
formulaStr: String;
|
||||
formula: PsFormula;
|
||||
isSharedFormula: Boolean;
|
||||
nodeName: String;
|
||||
sstIndex: Integer;
|
||||
number: Double;
|
||||
@ -699,7 +700,7 @@ begin
|
||||
dataStr := dataStr + GetNodeValue(tnode);
|
||||
tnode := tnode.NextSibling;
|
||||
end;
|
||||
end else
|
||||
end;
|
||||
if (boReadFormulas in book.Options) and (nodeName = 'f') then
|
||||
begin
|
||||
// Formula to cell
|
||||
@ -755,77 +756,83 @@ begin
|
||||
datanode := datanode.NextSibling;
|
||||
end;
|
||||
|
||||
// get data type
|
||||
s := GetAttrValue(ANode, 't'); // "t" = data type
|
||||
if (s = '') and (dataStr = '') then
|
||||
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);
|
||||
if IsDateTimeFormat(numFmt) then
|
||||
begin
|
||||
if not IsTimeIntervalFormat(numFmt) then // no correction of time origin for "time interval" format
|
||||
number := ConvertExcelDateTimeToDateTime(number, FDateMode);
|
||||
sheet.WriteDateTime(cell, number);
|
||||
book.LockFormulas; // Protect formulas from being deleted by the WriteXXXX calls
|
||||
try
|
||||
// get data type
|
||||
s := GetAttrValue(ANode, 't'); // "t" = data type
|
||||
if (s = '') and (dataStr = '') then
|
||||
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);
|
||||
if IsDateTimeFormat(numFmt) then
|
||||
begin
|
||||
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
|
||||
else if IsTextFormat(numFmt) then
|
||||
sheet.WriteText(cell, dataStr)
|
||||
else
|
||||
sheet.WriteNumber(cell, number);
|
||||
end
|
||||
else
|
||||
if s = 's' then begin
|
||||
// String from shared strings table
|
||||
sstIndex := StrToInt(dataStr);
|
||||
sheet.WriteText(cell, FSharedStrings[sstIndex]);
|
||||
// Read rich-text parameters from the stream stored in the Objects of the stringlist
|
||||
if FSharedStrings.Objects[sstIndex] <> nil then
|
||||
begin
|
||||
ms := TMemoryStream(FSharedStrings.Objects[sstIndex]);
|
||||
ms.Position := 0;
|
||||
n := ms.ReadWord; // Count of array elements
|
||||
SetLength(cell^.RichTextParams, n);
|
||||
ms.ReadBuffer(cell^.RichTextParams[0], n*SizeOf(TsRichTextParam));
|
||||
end;
|
||||
end else
|
||||
if (s = 'str') or (s = 'inlineStr') then begin
|
||||
// literal string
|
||||
// formulaStr := cell^.FormulaValue;
|
||||
sheet.WriteText(cell, datastr);
|
||||
// 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)
|
||||
if s = 's' then begin
|
||||
// String from shared strings table
|
||||
sstIndex := StrToInt(dataStr);
|
||||
sheet.WriteText(cell, FSharedStrings[sstIndex]);
|
||||
// Read rich-text parameters from the stream stored in the Objects of the stringlist
|
||||
if FSharedStrings.Objects[sstIndex] <> nil then
|
||||
begin
|
||||
ms := TMemoryStream(FSharedStrings.Objects[sstIndex]);
|
||||
ms.Position := 0;
|
||||
n := ms.ReadWord; // Count of array elements
|
||||
SetLength(cell^.RichTextParams, n);
|
||||
ms.ReadBuffer(cell^.RichTextParams[0], n*SizeOf(TsRichTextParam));
|
||||
end;
|
||||
end else
|
||||
if (s = 'str') or (s = 'inlineStr') then begin
|
||||
// literal string
|
||||
sheet.WriteText(cell, datastr);
|
||||
end else
|
||||
if s = 'b' then
|
||||
// boolean
|
||||
sheet.WriteBoolValue(cell, dataStr='1')
|
||||
else
|
||||
raise EFPSpreadsheetReader.Create(rsUnknownErrorType);
|
||||
end else
|
||||
raise EFPSpreadsheetReader.Create(rsUnknownDataType);
|
||||
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
|
||||
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;
|
||||
|
||||
procedure TsSpreadOOXMLReader.ReadCellXfs(ANode: TDOMNode);
|
||||
|
@ -38,6 +38,7 @@ type
|
||||
procedure Test_Write_Read_CalcFormulas(AFormat: TsSpreadsheetformat;
|
||||
UseRPNFormula: Boolean);
|
||||
procedure Test_Write_Read_Calc3DFormulas(AFormat: TsSpreadsheetFormat);
|
||||
procedure Test_OverwriteFormulaTest(ATest: Integer; AFormat: TsSpreadsheetFormat);
|
||||
|
||||
published
|
||||
// Writes out formulas & reads them back.
|
||||
@ -83,7 +84,30 @@ type
|
||||
procedure Test_Write_Read_Calc3DFormula_OOXML;
|
||||
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;
|
||||
|
||||
implementation
|
||||
@ -842,6 +866,208 @@ begin
|
||||
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
|
||||
// Register so these tests are included in a full run
|
||||
RegisterTest(TSpreadWriteReadFormulaTests);
|
||||
|
Reference in New Issue
Block a user