fpspreadsheet: Replace prev commit by better fix for formulas with return a blank cell being lost.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@5752 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2017-02-18 16:54:50 +00:00
parent cf167af00d
commit ef4c18081d
5 changed files with 102 additions and 33 deletions

View File

@@ -1231,7 +1231,9 @@ var
p: Integer;
link, txt: String;
cell: PCell;
formula: String;
begin
formula := ACell^.FormulaValue;
ACell^.Flags := ACell^.Flags + [cfCalculating] - [cfCalculated];
parser := TsSpreadsheetParser.Create(self);
@@ -1283,6 +1285,8 @@ begin
parser.Free;
end;
// Restore the formula. Could have been erased by WriteBlank or WriteText('')
ACell^.FormulaValue := formula;
ACell^.Flags := ACell^.Flags + [cfCalculated] - [cfCalculating];
end;
@@ -1315,7 +1319,6 @@ begin
for cell in FCells do
if HasFormula(cell) and (cell^.ContentType <> cctError) then
CalcFormula(cell);
finally
dec(FWorkbook.FCalculationLock);
end;
@@ -4823,14 +4826,16 @@ end;
{@@ ----------------------------------------------------------------------------
Writes an empty cell
@param ACel Pointer to the cell
@param ACel Pointer to the cell
Note: Empty cells are useful when, for example, a border line extends
along a range of cells including empty cells.
-------------------------------------------------------------------------------}
procedure TsWorksheet.WriteBlank(ACell: PCell);
begin
if ACell <> nil then begin
//ACell^.FormulaValue := '';
ACell^.FormulaValue := '';
// NOTE: Erase the formula because if it would return a non-blank result
// this would be very confusing!
if HasHyperlink(ACell) then
WriteText(ACell, '') // '' will be replaced by the hyperlink target.
else

View File

@@ -65,6 +65,7 @@ type
procedure ReadNumber(AStream: TStream); override;
procedure ReadRowColXF(AStream: TStream; out ARow, ACol: Cardinal; out AXF: Word); override;
procedure ReadRowInfo(AStream: TStream); override;
function ReadRPNAttr(AStream: TStream; AIdentifier: Byte): Boolean; override;
function ReadRPNFunc(AStream: TStream): Word; override;
procedure ReadRPNSharedFormulaBase(AStream: TStream; out ARow, ACol: Cardinal); override;
function ReadRPNTokenArraySize(AStream: TStream): Word; override;
@@ -931,6 +932,16 @@ begin
lRow^.FormatIndex := XFToFormatIndex(xf);
end;
function TsSpreadBIFF2Reader.ReadRPNAttr(AStream: TStream; AIdentifier: Byte): Boolean;
begin
Result := false;
case AIdentifier of
$01: AStream.ReadByte; // tAttrVolatile
else exit; // others not supported by fpspreadsheet --> Result = false
end;
Result := true;
end;
{@@ ----------------------------------------------------------------------------
Reads the identifier for an RPN function with fixed argument count from the
stream.

View File

@@ -452,6 +452,7 @@ type
procedure ReadRowColXF(AStream: TStream; out ARow, ACol: Cardinal; out AXF: Word); virtual;
// Read row info
procedure ReadRowInfo(AStream: TStream); virtual;
function ReadRPNAttr(AStream: TStream; AIdentifier: Byte): boolean; virtual;
// Read the array of RPN tokens of a formula
procedure ReadRPNCellAddress(AStream: TStream; out ARow, ACol: Cardinal;
out AFlags: TsRelFlags); virtual;
@@ -2154,6 +2155,24 @@ begin
FWorksheet.WriteRowInfo(rowrec.RowIndex, lRow);
end;
{@@ ----------------------------------------------------------------------------
Reads the special attribute of an RPN formula element.
Attributes are ignored by fpspreadsheet.
Structure is value for BIFF3 - BIFF8. Must be overridden for BIFF2.
Returns false if the structure is too complex for fps.
-------------------------------------------------------------------------------}
function TsSpreadBIFFReader.ReadRPNAttr(AStream: TStream; AIdentifier: Byte): Boolean;
var
w: Word;
begin
Result := false;
case AIdentifier of
$01: AStream.ReadWord; // tAttrVolatile token
else exit; // others not supported by fps --> Result = false
end;
Result := true;
end;
{@@ ----------------------------------------------------------------------------
Reads the cell address used in an RPN formula element.
Evaluates the corresponding bits to distinguish between absolute and
@@ -2511,6 +2530,11 @@ begin
while (AStream.Position < p0 + ARPNTokenArraySize) and supported do begin
token := AStream.ReadByte;
case token of
INT_EXCEL_TOKEN_TATTR:
begin
b := AStream.ReadByte;
supported := ReadRPNAttr(AStream, b);
end;
INT_EXCEL_TOKEN_TREFV:
begin
ReadRPNCellAddress(AStream, r, c, flags);

View File

@@ -34,12 +34,21 @@ const
INT_EXCEL_TOKEN_TPAREN = $15; // Operator in parenthesis
{ Constant Operand Tokens, 3.8}
INT_EXCEL_TOKEN_TMISSARG = $16; //missing operand
INT_EXCEL_TOKEN_TSTR = $17; //string
INT_EXCEL_TOKEN_TERR = $1C; //error value
INT_EXCEL_TOKEN_TBOOL = $1D; //boolean
INT_EXCEL_TOKEN_TINT = $1E; //(unsigned) integer
INT_EXCEL_TOKEN_TNUM = $1F; //floating-point
INT_EXCEL_TOKEN_TMISSARG = $16; // missing operand
INT_EXCEL_TOKEN_TSTR = $17; // string
INT_EXCEL_TOKEN_TERR = $1C; // error value
INT_EXCEL_TOKEN_TBOOL = $1D; // boolean
INT_EXCEL_TOKEN_TINT = $1E; // (unsigned) integer
INT_EXCEL_TOKEN_TNUM = $1F; // floating-point
{ Control Tokens, Special Tokens, 3.10 }
// 01H tExp Matrix formula or shared formula
// 02H tTbl Multiple operation table
// 15H tParen Parentheses
// 18H tNlr Natural language reference (BIFF8)
INT_EXCEL_TOKEN_TATTR = $19; // tAttr Special attribute
// 1AH tSheet Start of external sheet reference (BIFF2-BIFF4)
// 1BH tEndSheet End of external sheet reference (BIFF2-BIFF4)
{ Operand Tokens }
// _R: reference; _V: value; _A: array
@@ -259,14 +268,6 @@ const
INT_EXCEL_SHEET_FUNC_HYPERLINK = 359; // BIFF8 only
{ Control Tokens, Special Tokens }
// 01H tExp Matrix formula or shared formula
// 02H tTbl Multiple operation table
// 15H tParen Parentheses
// 18H tNlr Natural language reference (BIFF8)
INT_EXCEL_TOKEN_TATTR = $19; // tAttr Special attribute
// 1AH tSheet Start of external sheet reference (BIFF2-BIFF4)
// 1BH tEndSheet End of external sheet reference (BIFF2-BIFF4)
implementation

View File

@@ -308,6 +308,13 @@ type
Tooltip: String;
end;
TSharedFormulaData = class
Worksheet: TsWorksheet;
Row: Integer;
Col: Integer;
Formula: String;
end;
const
PATTERN_TYPES: array [TsFillStyle] of string = (
'none', // fsNoFill
@@ -327,7 +334,7 @@ const
'lightDown', // fsThinStripeDiagDown
'darkTrellis', // fsHatchDiag
'lightTrellis', // fsHatchThinDiag
'darkTellis', // fsHatchTickDiag
'darkTellis', // fsHatchThickDiag
'lightGrid' // fsHatchThinHor
);
@@ -359,19 +366,25 @@ destructor TsSpreadOOXMLReader.Destroy;
var
j: Integer;
begin
for j := FFillList.Count-1 downto 0 do TObject(FFillList[j]).Free;
for j := FFillList.Count-1 downto 0 do
TObject(FFillList[j]).Free;
FFillList.Free;
for j := FBorderList.Count-1 downto 0 do TObject(FBorderList[j]).Free;
for j := FBorderList.Count-1 downto 0 do
TObject(FBorderList[j]).Free;
FBorderList.Free;
for j := FHyperlinkList.Count-1 downto 0 do TObject(FHyperlinkList[j]).Free;
for j := FHyperlinkList.Count-1 downto 0 do
TObject(FHyperlinkList[j]).Free;
FHyperlinkList.Free;
for j := FSharedStrings.Count-1 downto 0 do
if FSharedstrings.Objects[j] <> nil then FSharedStrings.Objects[j].Free;
FSharedStrings.Objects[j].Free;
FSharedStrings.Free;
FSharedFormulaBaseList.Free; // Don't free items, they are worksheet cells
for j := FSharedFormulaBaseList.Count-1 downto 0 do
TObject(FSharedFormulaBaseList[j]).Free;
FSharedFormulaBaseList.Free;
// FCellFormatList, FNumFormatList and FFontList are destroyed by ancestor
@@ -582,7 +595,9 @@ procedure TsSpreadOOXMLReader.ReadCell(ANode: TDOMNode; AWorksheet: TsWorksheet)
var
addr, s: String;
rowIndex, colIndex: Cardinal;
cell, sharedformulabase: PCell;
cell: PCell;
lCell: TCell;
sharedFormulabase: TSharedFormulaData;
datanode, tnode: TDOMNode;
dataStr: String;
formulaStr: String;
@@ -655,18 +670,28 @@ begin
s := GetAttrValue(datanode, 'ref');
if (s <> '') then // This defines the shared formula range
begin
AWorksheet.WriteFormula(cell, FormulaStr);
AWorksheet.WriteFormula(cell, formulaStr);
// We store the shared formula base in the SharedFormulaBaseList.
// The list index is identical with the 'si' attribute of the node.
FSharedFormulaBaseList.Add(cell);
sharedformulabase := TSharedFormulaData.Create;
sharedformulabase.Worksheet := FWorksheet;
sharedformulabase.Row := rowindex;
sharedformulabase.Col := colindex;
sharedformulabase.Formula := formulaStr;
FSharedFormulaBaseList.Add(sharedformulabase);
end else
begin
// Get index into the SharedFormulaBaseList
// Get index into the SharedFormulaBaseList...
s := GetAttrValue(datanode, 'si');
if s <> '' then
begin
sharedformulabase := PCell(FSharedFormulaBaseList[StrToInt(s)]);
FWorksheet.CopyFormula(sharedformulabase, rowindex, colindex);
sharedformulabase := TSharedFormulaData(FSharedFormulaBaseList[StrToInt(s)]);
// ... and copy shared formula to destination cell
InitCell(sharedformulabase.Row, sharedformulabase.Col, lCell);
lCell.Formulavalue := sharedformulabase.Formula;
lCell.Worksheet := sharedformulabase.Worksheet;
FWorksheet.CopyFormula(@lCell, cell);
cell^.ContentType := cctFormula;
end;
end;
end
@@ -681,8 +706,9 @@ begin
s := GetAttrValue(ANode, 't'); // "t" = data type
if (s = '') and (dataStr = '') then
begin
formulaStr := cell^.FormulaValue;
AWorksheet.WriteBlank(cell); // this erases the formula!!!
if formulaStr <> '' then cell^.FormulaValue := formulaStr;
cell^.FormulaValue := formulaStr;
end else
if (s = '') or (s = 'n') then begin
// Number or date/time, depending on format
@@ -713,10 +739,12 @@ begin
ms.ReadBuffer(cell^.RichTextParams[0], n*SizeOf(TsRichTextParam));
end;
end else
if (s = 'str') or (s = 'inlineStr') then
if (s = 'str') or (s = 'inlineStr') then begin
// literal string
AWorksheet.WriteText(cell, datastr)
else
formulaStr := cell^.FormulaValue;
AWorksheet.WriteText(cell, datastr);
cell^.FormulaValue := formulaStr;
end else
if s = 'b' then
// boolean
AWorksheet.WriteBoolValue(cell, dataStr='1')