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

View File

@@ -65,6 +65,7 @@ type
procedure ReadNumber(AStream: TStream); override; procedure ReadNumber(AStream: TStream); override;
procedure ReadRowColXF(AStream: TStream; out ARow, ACol: Cardinal; out AXF: Word); override; procedure ReadRowColXF(AStream: TStream; out ARow, ACol: Cardinal; out AXF: Word); override;
procedure ReadRowInfo(AStream: TStream); override; procedure ReadRowInfo(AStream: TStream); override;
function ReadRPNAttr(AStream: TStream; AIdentifier: Byte): Boolean; override;
function ReadRPNFunc(AStream: TStream): Word; override; function ReadRPNFunc(AStream: TStream): Word; override;
procedure ReadRPNSharedFormulaBase(AStream: TStream; out ARow, ACol: Cardinal); override; procedure ReadRPNSharedFormulaBase(AStream: TStream; out ARow, ACol: Cardinal); override;
function ReadRPNTokenArraySize(AStream: TStream): Word; override; function ReadRPNTokenArraySize(AStream: TStream): Word; override;
@@ -931,6 +932,16 @@ begin
lRow^.FormatIndex := XFToFormatIndex(xf); lRow^.FormatIndex := XFToFormatIndex(xf);
end; 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 Reads the identifier for an RPN function with fixed argument count from the
stream. stream.

View File

@@ -452,6 +452,7 @@ type
procedure ReadRowColXF(AStream: TStream; out ARow, ACol: Cardinal; out AXF: Word); virtual; procedure ReadRowColXF(AStream: TStream; out ARow, ACol: Cardinal; out AXF: Word); virtual;
// Read row info // Read row info
procedure ReadRowInfo(AStream: TStream); virtual; procedure ReadRowInfo(AStream: TStream); virtual;
function ReadRPNAttr(AStream: TStream; AIdentifier: Byte): boolean; virtual;
// Read the array of RPN tokens of a formula // Read the array of RPN tokens of a formula
procedure ReadRPNCellAddress(AStream: TStream; out ARow, ACol: Cardinal; procedure ReadRPNCellAddress(AStream: TStream; out ARow, ACol: Cardinal;
out AFlags: TsRelFlags); virtual; out AFlags: TsRelFlags); virtual;
@@ -2154,6 +2155,24 @@ begin
FWorksheet.WriteRowInfo(rowrec.RowIndex, lRow); FWorksheet.WriteRowInfo(rowrec.RowIndex, lRow);
end; 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. Reads the cell address used in an RPN formula element.
Evaluates the corresponding bits to distinguish between absolute and Evaluates the corresponding bits to distinguish between absolute and
@@ -2511,6 +2530,11 @@ begin
while (AStream.Position < p0 + ARPNTokenArraySize) and supported do begin while (AStream.Position < p0 + ARPNTokenArraySize) and supported do begin
token := AStream.ReadByte; token := AStream.ReadByte;
case token of case token of
INT_EXCEL_TOKEN_TATTR:
begin
b := AStream.ReadByte;
supported := ReadRPNAttr(AStream, b);
end;
INT_EXCEL_TOKEN_TREFV: INT_EXCEL_TOKEN_TREFV:
begin begin
ReadRPNCellAddress(AStream, r, c, flags); ReadRPNCellAddress(AStream, r, c, flags);

View File

@@ -34,12 +34,21 @@ const
INT_EXCEL_TOKEN_TPAREN = $15; // Operator in parenthesis INT_EXCEL_TOKEN_TPAREN = $15; // Operator in parenthesis
{ Constant Operand Tokens, 3.8} { Constant Operand Tokens, 3.8}
INT_EXCEL_TOKEN_TMISSARG = $16; //missing operand INT_EXCEL_TOKEN_TMISSARG = $16; // missing operand
INT_EXCEL_TOKEN_TSTR = $17; //string INT_EXCEL_TOKEN_TSTR = $17; // string
INT_EXCEL_TOKEN_TERR = $1C; //error value INT_EXCEL_TOKEN_TERR = $1C; // error value
INT_EXCEL_TOKEN_TBOOL = $1D; //boolean INT_EXCEL_TOKEN_TBOOL = $1D; // boolean
INT_EXCEL_TOKEN_TINT = $1E; //(unsigned) integer INT_EXCEL_TOKEN_TINT = $1E; // (unsigned) integer
INT_EXCEL_TOKEN_TNUM = $1F; //floating-point 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 } { Operand Tokens }
// _R: reference; _V: value; _A: array // _R: reference; _V: value; _A: array
@@ -259,14 +268,6 @@ const
INT_EXCEL_SHEET_FUNC_HYPERLINK = 359; // BIFF8 only 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 implementation

View File

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