You've already forked lazarus-ccr
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:
@@ -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
|
||||||
|
@@ -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.
|
||||||
|
@@ -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);
|
||||||
|
@@ -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
|
||||||
|
@@ -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')
|
||||||
|
Reference in New Issue
Block a user