fpspreadsheet: Reduce support for shared formulas (too many problems...), only reading from xls and xlsx files supported.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3998 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2015-03-06 12:12:45 +00:00
parent 1af6c4c6f1
commit 9365a1dff2
14 changed files with 260 additions and 1650 deletions

View File

@ -63,16 +63,6 @@ begin
RPNNumber(5.0, RPNNumber(5.0,
RPNFunc(fekAdd, RPNFunc(fekAdd,
nil))))); nil)))));
(*
// Write a shared formula "=E1+100" to the cell range F1:F5
// Please note that shared formulas are not written by sfOOXML and sfOpenDocument formats.
MyCell := MyWorksheet.WriteRPNFormula(0, 5, CreateRPNFormula(
RPNCellOffset(0, -1, [rfRelRow, rfRelCol],
RPNNumber(100,
RPNFunc(fekAdd,
nil)))));
MyWorksheet.UseSharedFormula('F1:F5', MyCell);
*)
end; end;
procedure WriteSecondWorksheet(); procedure WriteSecondWorksheet();

View File

@ -5,7 +5,7 @@ unit fpsclasses;
interface interface
uses uses
Classes, SysUtils, AVL_Tree, avglvltree, Classes, SysUtils, AVL_Tree, //avglvltree,
fpstypes; fpstypes;
type type
@ -175,7 +175,8 @@ type
implementation implementation
uses uses
Math, fpsUtils; {%H-}Math,
fpsUtils;
{ Helper function for sorting } { Helper function for sorting }
@ -233,9 +234,6 @@ begin
end; end;
function TsRowColEnumerator.MoveNext: Boolean; function TsRowColEnumerator.MoveNext: Boolean;
var
r1,c1,r2,c2: Cardinal;
item: TsRowCol;
begin begin
if FCurrentNode <> nil then begin if FCurrentNode <> nil then begin
if FReverse then if FReverse then

View File

@ -602,8 +602,8 @@ end;
procedure TsCSVWriter.WriteSheet(AStream: TStream; AWorksheet: TsWorksheet); procedure TsCSVWriter.WriteSheet(AStream: TStream; AWorksheet: TsWorksheet);
var var
r, c: Cardinal; r: Cardinal;
LastRow, LastCol: Cardinal; LastRow: Cardinal;
cell: PCell; cell: PCell;
begin begin
FWorksheet := AWorksheet; FWorksheet := AWorksheet;
@ -616,23 +616,12 @@ begin
FCSVBuilder.SetOutput(AStream); FCSVBuilder.SetOutput(AStream);
LastRow := FWorksheet.GetLastOccupiedRowIndex; LastRow := FWorksheet.GetLastOccupiedRowIndex;
LastCol := FWorksheet.GetLastOccupiedColIndex;
for r := 0 to LastRow do for r := 0 to LastRow do
begin begin
for cell in FWorksheet.Cells.GetRowEnumerator(r) do for cell in FWorksheet.Cells.GetRowEnumerator(r) do
WriteCellToStream(AStream, cell); WriteCellToStream(AStream, cell);
FCSVBuilder.AppendRow; FCSVBuilder.AppendRow;
end; end;
{
for c := 0 to LastCol do
begin
Cell := FWorksheet.FindCell(r, c);
if Cell <> nil then
WriteCellCallback(Cell, AStream);
if c = LastCol then
FCSVBuilder.AppendRow;
end;
}
finally finally
FreeAndNil(FCSVBuilder); FreeAndNil(FCSVBuilder);
end; end;

View File

@ -682,7 +682,9 @@ type
FDirty: Boolean; FDirty: Boolean;
FWorksheet: TsWorksheet; FWorksheet: TsWorksheet;
FDialect: TsFormulaDialect; FDialect: TsFormulaDialect;
FActiveCell: PCell; FSourceCell: PCell;
FDestCell: PCell;
// FActiveCell: PCell;
procedure CheckEOF; procedure CheckEOF;
procedure CheckNodes(var ALeft, ARight: TsExprNode); procedure CheckNodes(var ALeft, ARight: TsExprNode);
function ConvertNode(Todo: TsExprNode; ToType: TsResultType): TsExprNode; function ConvertNode(Todo: TsExprNode; ToType: TsResultType): TsExprNode;
@ -730,10 +732,11 @@ type
destructor Destroy; override; destructor Destroy; override;
function IdentifierByName(AName: ShortString): TsExprIdentifierDef; virtual; function IdentifierByName(AName: ShortString): TsExprIdentifierDef; virtual;
procedure Clear; procedure Clear;
function CopyMode: Boolean;
function Evaluate: TsExpressionResult; function Evaluate: TsExpressionResult;
procedure EvaluateExpression(out Result: TsExpressionResult); procedure EvaluateExpression(out Result: TsExpressionResult);
procedure PrepareCopyMode(ASourceCell, ADestCell: PCell);
function ResultType: TsResultType; function ResultType: TsResultType;
function SharedFormulaMode: Boolean;
property AsFloat: TsExprFloat read GetAsFloat; property AsFloat: TsExprFloat read GetAsFloat;
property AsInteger: Int64 read GetAsInteger; property AsInteger: Int64 read GetAsInteger;
@ -747,7 +750,7 @@ type
property RPNFormula: TsRPNFormula read GetRPNFormula write SetRPNFormula; property RPNFormula: TsRPNFormula read GetRPNFormula write SetRPNFormula;
property Identifiers: TsExprIdentifierDefs read FIdentifiers write SetIdentifiers; property Identifiers: TsExprIdentifierDefs read FIdentifiers write SetIdentifiers;
property BuiltIns: TsBuiltInExprCategories read FBuiltIns write SetBuiltIns; property BuiltIns: TsBuiltInExprCategories read FBuiltIns write SetBuiltIns;
property ActiveCell: PCell read FActiveCell write FActiveCell; // property ActiveCell: PCell read FActiveCell write FActiveCell;
property Worksheet: TsWorksheet read FWorksheet; property Worksheet: TsWorksheet read FWorksheet;
property Dialect: TsFormulaDialect read FDialect write FDialect; property Dialect: TsFormulaDialect read FDialect write FDialect;
end; end;
@ -1299,6 +1302,22 @@ begin
end; end;
end; end;
{ Prepares copy mode: The formula is contained in ASourceCell and will be
modified such as seen from ADestCell. }
procedure TsExpressionParser.PrepareCopyMode(ASourceCell, ADestCell: PCell);
begin
FSourceCell := ASourceCell;
FDestCell := ADestCell;
end;
{ Signals that the parser is in "CopyMode", i.e. there is are source and
destination cells. All relative references in the formula of the source cell
habe to be adapted as seen from the destination cell. }
function TsExpressionParser.CopyMode: Boolean;
begin
Result := (FDestCell <> nil) and (FSourceCell <> nil);
end;
procedure TsExpressionParser.CreateHashList; procedure TsExpressionParser.CreateHashList;
var var
ID: TsExprIdentifierDef; ID: TsExprIdentifierDef;
@ -1975,14 +1994,14 @@ begin
CreateNodeFromRPN(FExprNode, index); CreateNodeFromRPN(FExprNode, index);
if Assigned(FExprNode) then FExprNode.Check; if Assigned(FExprNode) then FExprNode.Check;
end; end;
(*
{ Signals that the parser is in SharedFormulaMode, i.e. there is an active cell { Signals that the parser is in SharedFormulaMode, i.e. there is an active cell
to which all relative addresses have to be adapted. } to which all relative addresses have to be adapted. }
function TsExpressionParser.SharedFormulaMode: Boolean; function TsExpressionParser.SharedFormulaMode: Boolean;
begin begin
Result := (ActiveCell <> nil) and (ActiveCell^.SharedFormulaBase <> nil); Result := (ActiveCell <> nil) and (ActiveCell^.SharedFormulaBase <> nil);
end; end;
*)
function TsExpressionParser.TokenType: TsTokenType; function TsExpressionParser.TokenType: TsTokenType;
begin begin
Result := FScanner.TokenType; Result := FScanner.TokenType;
@ -3811,37 +3830,27 @@ begin
end; end;
{ Calculates the row address of the node's cell for various cases: { Calculates the row address of the node's cell for various cases:
(1) SharedFormula mode: (1) Copy mode:
The "ActiveCell" of the parser is the cell for which the formula is The "DestCell" of the parser is the cell for which the formula is
calculated. If the formula contains a relative address in the cell node calculated. The "SourceCell" contains the formula. If the formula contains
the function calculates the row address of the cell represented by the a relative address in the cell node the function calculates the row
node as seen from the active cell. address of the cell represented by the node as seen from the DestCell.
If the formula contains an absolute address the function returns the row If the formula contains an absolute address the function returns the row
address of the SharedFormulaBase of the ActiveCell. address of the SourceCell.
(2) Normal mode: (2) Normal mode:
Returns the "true" row address of the cell assigned to the formula node. } Returns the "true" row address of the cell assigned to the formula node. }
function TsCellExprNode.GetCol: Cardinal; function TsCellExprNode.GetCol: Cardinal;
begin begin
if FParser.SharedFormulaMode then
begin
// A shared formula is stored in the SharedFormulaBase cell of the ActiveCell
// Since the cell data stored in the node are those used by the formula in
// the SharedFormula, the current node is relative to the SharedFormulaBase
if rfRelCol in FFlags then
Result := FCol - FParser.ActiveCell^.SharedFormulaBase^.Col + FParser.ActiveCell^.Col
else
Result := FCol; //FParser.ActiveCell^.SharedFormulaBase^.Col;
end
else
// Normal mode
Result := FCol; Result := FCol;
if FParser.CopyMode and (rfRelCol in FFlags) then
Result := FCol - FParser.FSourceCell^.Col + FParser.FDestCell^.Col;
end; end;
procedure TsCellExprNode.GetNodeValue(out Result: TsExpressionResult); procedure TsCellExprNode.GetNodeValue(out Result: TsExpressionResult);
var var
cell: PCell; cell: PCell;
begin begin
if Parser.SharedFormulaMode then if Parser.CopyMode then
cell := FWorksheet.FindCell(GetRow, GetCol) cell := FWorksheet.FindCell(GetRow, GetCol)
else else
cell := FCell; cell := FCell;
@ -3863,15 +3872,9 @@ end;
{ See GetCol } { See GetCol }
function TsCellExprNode.GetRow: Cardinal; function TsCellExprNode.GetRow: Cardinal;
begin begin
if Parser.SharedFormulaMode then
begin
if rfRelRow in FFlags then
Result := FRow - FParser.ActiveCell^.SharedFormulaBase^.Row + FParser.ActiveCell^.Row
else
Result := FRow; //FParser.ActiveCell^.SharedFormulaBase^.Row;
end
else
Result := FRow; Result := FRow;
if Parser.CopyMode and (rfRelRow in FFlags) then
Result := FRow - FParser.FSourceCell^.Row + FParser.FDestCell^.Row;
end; end;
function TsCellExprNode.NodeType: TsResultType; function TsCellExprNode.NodeType: TsResultType;
@ -3998,7 +4001,6 @@ end;
function ArgToInt(Arg: TsExpressionResult): Integer; function ArgToInt(Arg: TsExpressionResult): Integer;
var var
cell: PCell; cell: PCell;
s: String;
begin begin
Result := 0; Result := 0;
case Arg.ResultType of case Arg.ResultType of

View File

@ -4058,11 +4058,13 @@ begin
parser := TsSpreadsheetParser.Create(FWorksheet); parser := TsSpreadsheetParser.Create(FWorksheet);
try try
parser.Dialect := fdOpenDocument; parser.Dialect := fdOpenDocument;
{
if ACell^.SharedFormulaBase <> nil then if ACell^.SharedFormulaBase <> nil then
begin begin
parser.ActiveCell := ACell; parser.ActiveCell := ACell;
parser.Expression := ACell^.SharedFormulaBase^.FormulaValue; parser.Expression := ACell^.SharedFormulaBase^.FormulaValue;
end else end else
}
parser.Expression := ACell^.FormulaValue; parser.Expression := ACell^.FormulaValue;
formula := Parser.LocalizedExpression[FPointSeparatorSettings]; formula := Parser.LocalizedExpression[FPointSeparatorSettings];
finally finally

View File

@ -234,11 +234,6 @@ type
procedure WriteRPNFormula(ACell: PCell; procedure WriteRPNFormula(ACell: PCell;
AFormula: TsRPNFormula); overload; AFormula: TsRPNFormula); overload;
procedure WriteSharedFormula(ARow1, ACol1, ARow2, ACol2: Cardinal;
const AFormula: String); overload;
procedure WriteSharedFormula(ACellRange: String;
const AFormula: String); overload;
function WriteUTF8Text(ARow, ACol: Cardinal; AText: ansistring): PCell; overload; function WriteUTF8Text(ARow, ACol: Cardinal; AText: ansistring): PCell; overload;
procedure WriteUTF8Text(ACell: PCell; AText: ansistring); overload; procedure WriteUTF8Text(ACell: PCell; AText: ansistring); overload;
@ -330,15 +325,10 @@ type
procedure WriteWordwrap(ACell: PCell; AValue: boolean); overload; procedure WriteWordwrap(ACell: PCell; AValue: boolean); overload;
{ Formulas } { Formulas }
function BuildRPNFormula(ACell: PCell): TsRPNFormula; function BuildRPNFormula(ACell: PCell; ADestCell: PCell = nil): TsRPNFormula;
procedure CalcFormula(ACell: PCell); procedure CalcFormula(ACell: PCell);
procedure CalcFormulas; procedure CalcFormulas;
function ConvertRPNFormulaToStringFormula(const AFormula: TsRPNFormula): String; function ConvertRPNFormulaToStringFormula(const AFormula: TsRPNFormula): String;
function FindSharedFormulaBase(ACell: PCell): PCell;
function FindSharedFormulaRange(ACell: PCell; out ARow1, ACol1, ARow2, ACol2: Cardinal): Boolean;
procedure FixSharedFormulas;
procedure SplitSharedFormula(ACell: PCell);
function UseSharedFormula(ARow, ACol: Cardinal; ASharedFormulaBase: PCell): PCell;
function GetCalcState(ACell: PCell): TsCalcState; function GetCalcState(ACell: PCell): TsCalcState;
procedure SetCalcState(ACell: PCell; AValue: TsCalcState); procedure SetCalcState(ACell: PCell; AValue: TsCalcState);
@ -586,7 +576,6 @@ type
FFontList: TFPList; FFontList: TFPList;
{ Internal methods } { Internal methods }
procedure FixSharedFormulas;
procedure GetLastRowColIndex(out ALastRow, ALastCol: Cardinal); procedure GetLastRowColIndex(out ALastRow, ALastCol: Cardinal);
procedure PrepareBeforeReading; procedure PrepareBeforeReading;
procedure PrepareBeforeSaving; procedure PrepareBeforeSaving;
@ -979,15 +968,13 @@ begin
end; end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Returns TRUE if the cell contains a formula (direct or shared, does not matter). Returns TRUE if the cell contains a formula.
@param ACell Pointer to the cell checked @param ACell Pointer to the cell checked
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
function HasFormula(ACell: PCell): Boolean; function HasFormula(ACell: PCell): Boolean;
begin begin
Result := Assigned(ACell) and ( Result := Assigned(ACell) and (Length(ACell^.FormulaValue) > 0);
(ACell^.SharedFormulaBase <> nil) or (Length(ACell^.FormulaValue) > 0)
);
end; end;
function CompareCells(Item1, Item2: Pointer): Integer; function CompareCells(Item1, Item2: Pointer): Integer;
@ -1108,11 +1095,14 @@ end;
Helper function which constructs an rpn formula from the cell's string Helper function which constructs an rpn formula from the cell's string
formula. This is needed, for example, when writing a formula to xls biff formula. This is needed, for example, when writing a formula to xls biff
file format. file format.
If the cell belongs to a shared formula the formula is taken from the The formula is stored in ACell.
shared formula base cell, cell references are adapted accordingly to the If ADestCell is not nil then the relative references are adjusted as seen
location of the cell. from ADestCell. This means that this function returns the formula that
would be created if ACell is copied to the location of ADestCell.
Needed for copying formulas and for splitting shared formulas.
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
function TsWorksheet.BuildRPNFormula(ACell: PCell): TsRPNFormula; function TsWorksheet.BuildRPNFormula(ACell: PCell;
ADestCell: PCell = nil): TsRPNFormula;
var var
parser: TsSpreadsheetParser; parser: TsSpreadsheetParser;
begin begin
@ -1122,10 +1112,8 @@ begin
end; end;
parser := TsSpreadsheetParser.Create(self); parser := TsSpreadsheetParser.Create(self);
try try
if (ACell^.SharedFormulaBase <> nil) then begin if ADestCell <> nil then
parser.ActiveCell := ACell; parser.PrepareCopyMode(ACell, ADestCell);
parser.Expression := ACell^.SharedFormulaBase^.FormulaValue;
end else
parser.Expression := ACell^.FormulaValue; parser.Expression := ACell^.FormulaValue;
Result := parser.RPNFormula; Result := parser.RPNFormula;
finally finally
@ -1145,27 +1133,16 @@ procedure TsWorksheet.CalcFormula(ACell: PCell);
var var
parser: TsSpreadsheetParser; parser: TsSpreadsheetParser;
res: TsExpressionResult; res: TsExpressionResult;
formula: String;
cell: PCell;
p: Integer; p: Integer;
link, txt: String; link, txt: String;
cell: PCell;
begin begin
ACell^.Flags := ACell^.Flags + [cfCalculating] - [cfCalculated]; ACell^.Flags := ACell^.Flags + [cfCalculating] - [cfCalculated];
parser := TsSpreadsheetParser.Create(self); parser := TsSpreadsheetParser.Create(self);
try try
if ACell^.SharedFormulaBase = nil then
begin
formula := ACell^.FormulaValue;
parser.ActiveCell := nil;
end else
begin
formula := ACell^.SharedFormulaBase^.FormulaValue;
parser.ActiveCell := ACell;
end;
try try
parser.Expression := formula; parser.Expression := ACell^.FormulaValue;
res := parser.Evaluate; res := parser.Evaluate;
except except
on E:ECalcEngine do on E:ECalcEngine do
@ -1248,9 +1225,7 @@ begin
node := FCells.FindLowest; node := FCells.FindLowest;
while Assigned(node) do begin while Assigned(node) do begin
cell := PCell(node.Data); cell := PCell(node.Data);
if (cell^.ContentType <> cctError) and if (cell^.ContentType <> cctError) and HasFormula(cell) then
(HasFormula(cell) or HasFormula(cell^.SharedFormulaBase))
then
CalcFormula(cell); CalcFormula(cell);
node := FCells.FindSuccessor(node); node := FCells.FindSuccessor(node);
end; end;
@ -1496,7 +1471,6 @@ end;
function TsWorksheet.ValidHyperlink(AValue: String; out AErrMsg: String): Boolean; function TsWorksheet.ValidHyperlink(AValue: String; out AErrMsg: String): Boolean;
var var
uri: TUri; uri: TUri;
mark: String;
sheet: TsWorksheet; sheet: TsWorksheet;
r, c: Cardinal; r, c: Cardinal;
begin begin
@ -1664,7 +1638,7 @@ begin
if IsMergeBase(AFromCell) then if IsMergeBase(AFromCell) then
begin begin
FindMergedRange(AFromCell, row1, col1, row2, col2); FindMergedRange(AFromCell, row1, col1, row2, col2);
MergeCells(toRow, toCol, toRow + row2 - row1, toCol + col2 - col1); MergeCells(toRow, toCol, toRow + LongInt(row2) - LongInt(row1), toCol + LongInt(col2) - LongInt(col1));
end; end;
// Copy comment // Copy comment
@ -1743,7 +1717,6 @@ end;
procedure TsWorksheet.CopyFormula(AFromCell, AToCell: PCell); procedure TsWorksheet.CopyFormula(AFromCell, AToCell: PCell);
var var
rpnFormula: TsRPNFormula; rpnFormula: TsRPNFormula;
lCell: TCell;
begin begin
if (AFromCell = nil) or (AToCell = nil) then if (AFromCell = nil) or (AToCell = nil) then
exit; exit;
@ -1753,11 +1726,7 @@ begin
else else
begin begin
// Here we convert the formula to an rpn formula as seen from source... // Here we convert the formula to an rpn formula as seen from source...
// (The mechanism needs the ActiveCell of the parser which is only rpnFormula := BuildRPNFormula(AFromCell, AToCell);
// valid if the cell contains a shared formula)
lCell := AToCell^;
lCell.SharedFormulaBase := AFromCell;
rpnFormula := BuildRPNFormula(@lCell);
// ... and here we reconstruct the string formula as seen from destination cell. // ... and here we reconstruct the string formula as seen from destination cell.
AToCell^.FormulaValue := ConvertRPNFormulaToStringFormula(rpnFormula); AToCell^.FormulaValue := ConvertRPNFormulaToStringFormula(rpnFormula);
end; end;
@ -1811,7 +1780,7 @@ end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Deletes a specified cell. If the cell belongs to a merged block its content Deletes a specified cell. If the cell belongs to a merged block its content
and formatting is erased. Otherwise the cell is destroyed, its memory is and formatting is erased. Otherwise the cell is destroyed and its memory is
released. released.
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
procedure TsWorksheet.DeleteCell(ACell: PCell); procedure TsWorksheet.DeleteCell(ACell: PCell);
@ -1831,12 +1800,6 @@ begin
exit; exit;
end; end;
// Is base of shared formula block? Recreate individual formulas
if ACell^.SharedFormulaBase = ACell then
SplitSharedFormula(ACell);
// Belongs to shared formula block? --> nothing to do
// Destroy the cell, and remove it from the tree // Destroy the cell, and remove it from the tree
RemoveAndFreeCell(ACell^.Row, ACell^.Col); RemoveAndFreeCell(ACell^.Row, ACell^.Col);
end; end;
@ -2619,7 +2582,6 @@ end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
If a cell contains a formula (string formula or RPN formula) the formula If a cell contains a formula (string formula or RPN formula) the formula
is returned as a string in Excel syntax. is returned as a string in Excel syntax.
If the cell belongs to a shared formula the adapted shared formula is returned.
@param ACell Pointer to the cell considered @param ACell Pointer to the cell considered
@param ALocalized If true, the formula is returned with decimal and list @param ALocalized If true, the formula is returned with decimal and list
@ -2636,40 +2598,19 @@ begin
if ACell = nil then if ACell = nil then
exit; exit;
if HasFormula(ACell) then begin if HasFormula(ACell) then begin
// case (1): Formula is localized and has to be converted to default syntax
if ALocalized then if ALocalized then
begin begin
// case (1): Formula is localized and has to be converted to default syntax // !!!! Is this comment correct?
parser := TsSpreadsheetParser.Create(self); parser := TsSpreadsheetParser.Create(self);
try try
if ACell^.SharedFormulaBase <> nil then begin
// case (1a): shared formula
parser.ActiveCell := ACell;
parser.Expression := ACell^.SharedFormulaBase^.FormulaValue;
end else begin
// case (1b): normal formula
parser.ActiveCell := nil;
parser.Expression := ACell^.FormulaValue; parser.Expression := ACell^.FormulaValue;
end;
Result := parser.LocalizedExpression[Workbook.FormatSettings]; Result := parser.LocalizedExpression[Workbook.FormatSettings];
finally finally
parser.Free; parser.Free;
end; end;
end end
else else
// case (2): Formula is in default syntax // case (2): Formula is already in default syntax
if ACell^.SharedFormulaBase <> nil then
begin
// case (2a): shared formula
parser := TsSpreadsheetParser.Create(self);
try
parser.ActiveCell := ACell;
parser.Expression := ACell^.SharedFormulaBase^.FormulaValue;
Result := parser.Expression;
finally
parser.Free;
end;
end else
// case (2b): normal formula
Result := ACell^.FormulaValue; Result := ACell^.FormulaValue;
end; end;
end; end;
@ -3121,7 +3062,6 @@ end;
procedure TsWorksheet.UnmergeCells(ARow, ACol: Cardinal); procedure TsWorksheet.UnmergeCells(ARow, ACol: Cardinal);
var var
rng: PsCellRange; rng: PsCellRange;
r, c: Cardinal;
cell: PCell; cell: PCell;
begin begin
rng := FMergedCells.FindRangeWithCell(ARow, ACol); rng := FMergedCells.FindRangeWithCell(ARow, ACol);
@ -3155,23 +3095,6 @@ begin
UnmergeCells(rng.Row1, rng.Col1); UnmergeCells(rng.Row1, rng.Col1);
end; end;
{@@ ----------------------------------------------------------------------------
Finds the upper left cell of a shared formula block to which the specified
cell belongs. This is the "shared formula base".
@param ACell Cell under investigation
@return A pointer to the cell in the upper left corner of the shared formula
block to which ACell belongs. If ACell is not part of a shared formula
block then the function returns NIL.
-------------------------------------------------------------------------------}
function TsWorksheet.FindSharedFormulaBase(ACell: PCell): PCell;
begin
if ACell = nil then
Result := nil
else
Result := ACell^.SharedFormulaBase;
end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Determines the merged cell block to which a particular cell belongs Determines the merged cell block to which a particular cell belongs
@ -3244,90 +3167,6 @@ begin
end; end;
{ Shared formulas }
{@@ ----------------------------------------------------------------------------
Determines the cell block sharing the same formula which is used by a given cell
Note: the block may not be contiguous. The function returns the outer edges
of the range.
@param ACell Pointer to the cell being investigated
@param ARow1 (output) Top row index of the shared formula block
@param ACol1 (outout) Left column index of the shared formula block
@param ARow2 (output) Bottom row index of the shared formula block
@param ACol2 (output) Right column index of the shared formula block
@return True if the cell belongs to a shared formula block, False if not or
if the cell does not exist at all.
-------------------------------------------------------------------------------}
function TsWorksheet.FindSharedFormulaRange(ACell: PCell;
out ARow1, ACol1, ARow2, ACol2: Cardinal): Boolean;
var
r, c: Cardinal;
cell: PCell;
base: PCell;
lastCol, lastRow: Cardinal;
begin
base := FindSharedFormulaBase(ACell);
if base = nil then begin
Result := false;
exit;
end;
// Assuming that the shared formula block is rectangular, we start at the base...
ARow1 := base^.Row;
ARow2 := ARow1;
ACol1 := base^.Col;
ACol2 := ACol1;
lastCol := GetLastOccupiedColIndex;
lastRow := GetLastOccupiedRowIndex;
// ... and go along first COLUMN to find the end of the shared formula block, ...
for c := ACol1+1 to lastCol do
begin
cell := FindCell(ARow1, c);
if (cell <> nil) and (cell^.SharedFormulaBase = base) then
ACol2 := c;
end;
// ... and go along first ROW to find the end of the shared formula block
for r := ARow1 + 1 to lastRow do
begin
cell := FindCell(r, ACol1);
if (cell <> nil) and (cell^.SharedFormulaBase = base) then
ARow2 := r;
end;
Result := true;
end;
{@@ ----------------------------------------------------------------------------
A shared formula must contain at least two cells. If there is only a single
cell then the shared formula is converted to a regular one.
Is called before writing to stream.
-------------------------------------------------------------------------------}
procedure TsWorksheet.FixSharedFormulas;
var
r,c, r1,c1, r2,c2: Cardinal;
cell: PCell;
// firstRow, firstCol, lastRow, lastCol: Cardinal;
begin
for cell in Cells do
if FindSharedFormulaRange(cell, r1, c1, r2, c2) and (r1 = r2) and (c1 = c2) then
cell^.SharedFormulaBase := nil;
{
firstRow := GetFirstRowIndex;
firstCol := GetFirstColIndex;
lastRow := GetLastOccupiedRowIndex;
lastCol := GetLastOccupiedColIndex;
for r := firstRow to lastRow do
for c := firstCol to lastCol do
begin
cell := FindCell(r, c);
if FindSharedFormulaRange(cell, r1, c1, r2, c2) and (r1 = r2) and (c1 = c2) then
cell^.SharedFormulaBase := nil;
end;
}
end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Removes the comment from a cell and releases the memory occupied by the node. Removes the comment from a cell and releases the memory occupied by the node.
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
@ -3745,72 +3584,6 @@ begin
FLastRowIndex := GetLastRowIndex(true); FLastRowIndex := GetLastRowIndex(true);
end; end;
{@@ ----------------------------------------------------------------------------
Splits a shared formula range to which the specified cell belongs into
individual cells. Each cell gets the same formula as it had in the block.
This is required because insertion and deletion of columns/rows make shared
formulas very complicated.
-------------------------------------------------------------------------------}
procedure TsWorksheet.SplitSharedFormula(ACell: PCell);
var
r, c: Cardinal;
baseRow, baseCol: Cardinal;
lastRow, lastCol: Cardinal;
cell: PCell;
rpnFormula: TsRPNFormula;
begin
if (ACell = nil) or (ACell^.SharedFormulaBase = nil) then
exit;
lastRow := GetLastOccupiedRowIndex;
lastCol := GetLastOccupiedColIndex;
baseRow := ACell^.SharedFormulaBase^.Row;
baseCol := ACell^.SharedFormulaBase^.Col;
for r := baseRow to lastRow do
for c := baseCol to lastCol do
begin
cell := FindCell(r, c);
if (cell = nil) or (cell^.SharedFormulaBase = nil) then
continue;
if (cell^.SharedFormulaBase^.Row = baseRow) and
(cell^.SharedFormulaBase^.Col = baseCol) then
begin
// This method converts the shared formula to an rpn formula as seen from cell...
rpnFormula := BuildRPNFormula(cell);
// ... and this reconstructs the string formula, again as seen from cell.
cell^.FormulaValue := ConvertRPNFormulaToStringFormula(rpnFormula);
// Remove the SharedFormulaBase information --> cell is isolated.
cell^.SharedFormulaBase := nil;
end;
end;
end;
{@@ ----------------------------------------------------------------------------
Defines a cell range sharing the "same" formula. Note that relative cell
references are updated for each cell in the range.
@param ARow Row of the cell
@param ACol Column index of the cell
@param ASharedFormulaBase Cell containing the shared formula
Note: An exception is raised if the cell already contains a formula (and is
different from the ASharedFormulaBase cell).
-------------------------------------------------------------------------------}
function TsWorksheet.UseSharedFormula(ARow, ACol: Cardinal;
ASharedFormulaBase: PCell): PCell;
begin
if ASharedFormulaBase = nil then begin
Result := nil;
exit;
end;
Result := GetCell(ARow, ACol);
Result.SharedFormulaBase := ASharedFormulaBase;
if (Result^.FormulaValue <> '') and
((ASharedFormulaBase.Row <> ARow) and (ASharedFormulaBase.Col <> ACol))
then
raise Exception.CreateFmt('[TsWorksheet.UseSharedFormula] Cell %s uses a shared formula, but contains an own formula.',
[GetCellString(ARow, ACol)]);
end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Writes UTF-8 encoded text to a cell. Writes UTF-8 encoded text to a cell.
@ -3855,7 +3628,6 @@ begin
begin begin
if (Workbook.GetCellFormat(ACell^.FormatIndex).UsedFormattingFields = []) and if (Workbook.GetCellFormat(ACell^.FormatIndex).UsedFormattingFields = []) and
(ACell^.Flags * [cfHyperlink, cfHasComment, cfMerged] = []) and (ACell^.Flags * [cfHyperlink, cfHasComment, cfMerged] = []) and
(ACell^.SharedFormulaBase = nil) and
(ACell^.FormulaValue = '') (ACell^.FormulaValue = '')
then then
begin begin
@ -4052,8 +3824,6 @@ end;
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);
var
hyperlink: TsHyperlink;
begin begin
if ACell <> nil then begin if ACell <> nil then begin
if HasHyperlink(ACell) then if HasHyperlink(ACell) then
@ -4818,57 +4588,6 @@ begin
ChangedCell(ACell^.Row, ACell^.Col); ChangedCell(ACell^.Row, ACell^.Col);
end; end;
{@@ ----------------------------------------------------------------------------
Writes a formula to a cell and shares it with other cells.
@param ARow1, ACol1 Row and column index of the top left corner of
the range sharing the formula. The cell in this
cell stores the formula.
@param ARow2, ACol2 Row and column of the bottom right corner of the
range sharing the formula.
@param AFormula Formula in Excel notation
-------------------------------------------------------------------------------}
procedure TsWorksheet.WriteSharedFormula(ARow1, ACol1, ARow2, ACol2: Cardinal;
const AFormula: String);
var
cell: PCell;
r, c: Cardinal;
begin
if (ARow1 > ARow2) or (ACol1 > ACol2) then
raise Exception.Create('[TsWorksheet.WriteSharedFormula] Rows/cols not ordered correctly: ARow1 <= ARow2, ACol1 <= ACol2.');
if (ARow1 = ARow2) and (ACol1 = ACol2) then
raise Exception.Create('[TsWorksheet.WriteSharedFormula] A shared formula range must contain at least two cells.');
// The cell at the top/left corner of the cell range is the "SharedFormulaBase".
// It is the only cell which stores the formula.
cell := WriteFormula(ARow1, ACol1, AFormula);
for r := ARow1 to ARow2 do
for c := ACol1 to ACol2 do
UseSharedFormula(r, c, cell);
end;
{@@ ----------------------------------------------------------------------------
Writes a formula to a cell and shares it with other cells.
@param ACellRangeStr Range of cells which will use the shared formula.
The range is given as a string in Excel notation,
such as A1:B5, or A1
@param AFormula Formula (in Excel notation) to be shared. The cell
addresses are relative to the top/left cell of the
range.
-------------------------------------------------------------------------------}
procedure TsWorksheet.WriteSharedFormula(ACellRange: String;
const AFormula: String);
var
r1,r2, c1,c2: Cardinal;
begin
if ParseCellRangeString(ACellRange, r1, c1, r2, c2) then
WriteSharedFormula(r1, c1, r2, c2, AFormula)
else
raise Exception.Create('[TsWorksheet.WriteSharedFormula] No valid cell range string.');
end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Adds font specification to the formatting of a cell. Looks in the workbook's Adds font specification to the formatting of a cell. Looks in the workbook's
FontList and creates an new entry if the font is not used so far. Returns the FontList and creates an new entry if the font is not used so far. Returns the
@ -5699,20 +5418,12 @@ end;
function TsWorksheet.CalcAutoRowHeight(ARow: Cardinal): Single; function TsWorksheet.CalcAutoRowHeight(ARow: Cardinal): Single;
var var
cell: PCell; cell: PCell;
col: Integer;
h0: Single; h0: Single;
begin begin
Result := 0; Result := 0;
h0 := Workbook.GetDefaultFontSize; h0 := Workbook.GetDefaultFontSize;
for cell in Cells.GetRowEnumerator(ARow) do for cell in Cells.GetRowEnumerator(ARow) do
Result := Max(Result, ReadCellFont(cell).Size / h0); Result := Max(Result, ReadCellFont(cell).Size / h0);
{
for col := GetFirstColIndex to GetLastColIndex do begin
cell := FindCell(ARow, col);
if cell <> nil then
Result := Max(Result, ReadCellFont(cell).Size / h0);
end;
}
end; end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
@ -5901,43 +5612,13 @@ procedure TsWorksheet.DeleteCol(ACol: Cardinal);
var var
col: PCol; col: PCol;
i: Integer; i: Integer;
r, rr, cc: Cardinal; r: Cardinal;
cell, basecell, nextcell: PCell; cell: PCell;
firstRow, lastCol, lastRow: Cardinal; firstRow, lastRow: Cardinal;
begin begin
lastCol := GetLastColIndex;
lastRow := GetLastOccupiedRowIndex; lastRow := GetLastOccupiedRowIndex;
firstRow := GetFirstRowIndex; firstRow := GetFirstRowIndex;
// Loop along the column to be deleted and fix shared formulas
for r := firstRow to lastRow do
begin
cell := FindCell(r, ACol);
// Fix shared formulas: if the deleted column contains the shared formula base
// of a shared formula block then the shared formula has to be moved to the
// next column
if (cell <> nil) and (cell^.SharedFormulaBase = cell) then begin
basecell := cell;
nextcell := FindCell(r, ACol+1); // cell in next column and same row
// Next cell in col at the right does not share this formula --> done with this formula
if (nextcell = nil) or (nextcell^.SharedFormulaBase <> cell) then
continue;
// Write adapted formula to the cell below.
WriteFormula(nextcell, basecell^.Formulavalue); //ReadFormulaAsString(nextcell));
// Have all cells sharing the formula use the new formula base
for rr := r to lastRow do
for cc := ACol+1 to lastCol do
begin
cell := FindCell(rr, cc);
if (cell <> nil) and (cell^.SharedFormulaBase = basecell) then
cell^.SharedFormulaBase := nextcell
else
break;
end;
end;
end;
// Fix merged cells // Fix merged cells
FMergedCells.DeleteRowOrCol(ACol, false); FMergedCells.DeleteRowOrCol(ACol, false);
@ -5981,41 +5662,12 @@ procedure TsWorksheet.DeleteRow(ARow: Cardinal);
var var
row: PRow; row: PRow;
i: Integer; i: Integer;
c, rr, cc: Cardinal; c: Cardinal;
firstCol, lastCol, lastRow: Cardinal; firstCol, lastCol: Cardinal;
cell, nextcell, basecell: PCell; cell: PCell;
begin begin
firstCol := GetFirstColIndex; firstCol := GetFirstColIndex;
lastCol := GetLastOccupiedColIndex; lastCol := GetLastOccupiedColIndex;
lastRow := GetLastOccupiedRowIndex;
// Loop along the row to be deleted and fix shared formulas
for c := firstCol to lastCol do
begin
cell := FindCell(ARow, c);
// Fix shared formulas: if the deleted row contains the shared formula base
// of a shared formula block then the shared formula has to be moved to the
// next row
if (cell <> nil) and (cell^.SharedFormulaBase = cell) then begin
basecell := cell;
nextcell := FindCell(ARow+1, c); // cell in next row at same column
// Next cell in row below does not share this formula --> done with this formula
if (nextcell = nil) or (nextcell^.SharedFormulaBase <> cell) then
continue;
// Write adapted formula to the cell below.
WriteFormula(nextcell, basecell^.FormulaValue); //ReadFormulaAsString(nextcell));
// Have all cells sharing the formula use the new formula base
for rr := ARow+1 to lastRow do
for cc := c to lastCol do
begin
cell := FindCell(rr, cc);
if (cell <> nil) and (cell^.SharedFormulaBase = basecell) then
cell^.SharedFormulaBase := nextcell
else
break;
end;
end;
end;
// Fix merged cells // Fix merged cells
FMergedCells.DeleteRowOrCol(ARow, true); FMergedCells.DeleteRowOrCol(ARow, true);
@ -6027,7 +5679,7 @@ begin
FHyperlinks.DeleteRowOrCol(ARow, true); FHyperlinks.DeleteRowOrCol(ARow, true);
// Delete cells // Delete cells
for c := lastCol downto 0 do for c := lastCol downto firstCol do
RemoveAndFreeCell(ARow, c); RemoveAndFreeCell(ARow, c);
// Update row index of cell records // Update row index of cell records
@ -6061,15 +5713,9 @@ procedure TsWorksheet.InsertCol(ACol: Cardinal);
var var
col: PCol; col: PCol;
i: Integer; i: Integer;
r: Cardinal;
cell: PCell; cell: PCell;
rng: PsCellRange; rng: PsCellRange;
begin begin
// Handling of shared formula references is too complicated for me...
// Split them into isolated cell formulas
for cell in FCells do
SplitSharedFormula(cell);
// Update column index of comments // Update column index of comments
FComments.InsertRowOrCol(ACol, false); FComments.InsertRowOrCol(ACol, false);
@ -6091,24 +5737,15 @@ begin
// Fix merged cells // Fix merged cells
for rng in FMergedCells do for rng in FMergedCells do
// rng := PsCellRange(FMergedCells.GetFirst);
// while rng <> nil do
begin begin
// The new column is at the LEFT of the merged block // The new column is at the LEFT of the merged block
// --> Shift entire range to the right by 1 column // --> Shift entire range to the right by 1 column
if (ACol < rng^.Col1) then if (ACol < rng^.Col1) then
begin begin
// The former first column is no longer marged --> un-tag its cells // The former first column is no longer merged --> un-tag its cells
for cell in Cells.GetColEnumerator(rng^.Col1, rng^.Row1, rng^.Row2) do for cell in Cells.GetColEnumerator(rng^.Col1, rng^.Row1, rng^.Row2) do
Exclude(cell^.Flags, cfMerged); Exclude(cell^.Flags, cfMerged);
{
for r := rng^.Row1 to rng^.Row2 do
begin
cell := FindCell(r, rng^.Col1);
if cell <> nil then
Exclude(cell^.Flags, cfMerged);
end;
}
// Shift merged block to the right // Shift merged block to the right
// Don't call "MergeCells" here - this would add a new merged block // Don't call "MergeCells" here - this would add a new merged block
// because of the new merge base! --> infinite loop! // because of the new merge base! --> infinite loop!
@ -6117,21 +5754,11 @@ begin
// The right column needs to be tagged // The right column needs to be tagged
for cell in Cells.GetColEnumerator(rng^.Col2, rng^.Row1, rng^.Row2) do for cell in Cells.GetColEnumerator(rng^.Col2, rng^.Row1, rng^.Row2) do
Include(cell^.Flags, cfMerged); Include(cell^.Flags, cfMerged);
{
for r := rng^.Row1 to rng^.Row2 do
begin
cell := FindCell(R, rng^.Col2);
if cell <> nil then
Include(cell^.Flags, cfMerged);
end;
}
end else end else
// The new column goes through this cell block --> Shift only the right // The new column goes through this cell block --> Shift only the right
// column of the range to the right by 1 // column of the range to the right by 1
if (ACol >= rng^.Col1) and (ACol <= rng^.Col2) then if (ACol >= rng^.Col1) and (ACol <= rng^.Col2) then
MergeCells(rng^.Row1, rng^.Col1, rng^.Row2, rng^.Col2+1); MergeCells(rng^.Row1, rng^.Col1, rng^.Row2, rng^.Col2+1);
// Continue with next merged block
// rng := PsCellRange(FMergedCells.GetNext);
end; end;
ChangedCell(0, ACol); ChangedCell(0, ACol);
@ -6187,15 +5814,9 @@ procedure TsWorksheet.InsertRow(ARow: Cardinal);
var var
row: PRow; row: PRow;
i: Integer; i: Integer;
c: Cardinal;
cell: PCell; cell: PCell;
rng: PsCellRange; rng: PsCellRange;
begin begin
// Handling of shared formula references is too complicated for me...
// Splits them into isolated cell formulas
for cell in FCells do
SplitSharedFormula(cell);
// Update row index of cell comments // Update row index of cell comments
FComments.InsertRowOrCol(ARow, true); FComments.InsertRowOrCol(ARow, true);
@ -6217,8 +5838,6 @@ begin
// Fix merged cells // Fix merged cells
for rng in FMergedCells do for rng in FMergedCells do
// rng := PsCellRange(FMergedCells.GetFirst);
// while rng <> nil do
begin begin
// The new row is ABOVE the merged block --> Shift entire range down by 1 row // The new row is ABOVE the merged block --> Shift entire range down by 1 row
if (ARow < rng^.Row1) then if (ARow < rng^.Row1) then
@ -6226,14 +5845,7 @@ begin
// The formerly first row is no longer merged --> un-tag its cells // The formerly first row is no longer merged --> un-tag its cells
for cell in Cells.GetRowEnumerator(rng^.Row1, rng^.Col1, rng^.Col2) do for cell in Cells.GetRowEnumerator(rng^.Row1, rng^.Col1, rng^.Col2) do
Exclude(cell^.Flags, cfMerged); Exclude(cell^.Flags, cfMerged);
{
for c := rng^.Col1 to rng^.Col2 do
begin
cell := FindCell(rng^.Row1, c);
if cell <> nil then
Exclude(cell^.Flags, cfMerged);
end;
}
// Shift merged block down // Shift merged block down
// (Don't call "MergeCells" here - this would add a new merged block // (Don't call "MergeCells" here - this would add a new merged block
// because of the new merge base! --> infinite loop!) // because of the new merge base! --> infinite loop!)
@ -6242,21 +5854,11 @@ begin
// The last row needs to be tagged // The last row needs to be tagged
for cell in Cells.GetRowEnumerator(rng^.Row2, rng^.Col1, rng^.Col2) do for cell in Cells.GetRowEnumerator(rng^.Row2, rng^.Col1, rng^.Col2) do
Include(cell^.Flags, cfMerged); Include(cell^.Flags, cfMerged);
{
for c := rng^.Col1 to rng^.Col2 do
begin
cell := FindCell(rng^.Row2, c);
if cell <> nil then
Include(cell^.Flags, cfMerged);
end;
}
end else end else
// The new row goes through this cell block --> Shift only the bottom row // The new row goes through this cell block --> Shift only the bottom row
// of the range down by 1 // of the range down by 1
if (ARow >= rng^.Row1) and (ARow <= rng^.Row2) then if (ARow >= rng^.Row1) and (ARow <= rng^.Row2) then
MergeCells(rng^.Row1, rng^.Col1, rng^.Row2+1, rng^.Col2); MergeCells(rng^.Row1, rng^.Col1, rng^.Row2+1, rng^.Col2);
// Continue with next block
// rng := PsCellRange(FMergedCells.GetNext);
end; end;
ChangedCell(ARow, 0); ChangedCell(ARow, 0);
@ -6472,9 +6074,6 @@ begin
// Updates fist/last column/row index // Updates fist/last column/row index
UpdateCaches; UpdateCaches;
// Shared formulas must contain at least two cells
FixSharedFormulas;
// Calculated formulas (if requested) // Calculated formulas (if requested)
if (boCalcBeforeSaving in FOptions) then if (boCalcBeforeSaving in FOptions) then
for sheet in FWorksheets do for sheet in FWorksheets do
@ -6717,22 +6316,6 @@ begin
raise Exception.Create(rsUnsupportedWriteFormat); raise Exception.Create(rsUnsupportedWriteFormat);
end; end;
{@@ ----------------------------------------------------------------------------
Shared formulas must contain at least two cells. If it's a single cell, then
the cell formula is converted to a standard one.
-------------------------------------------------------------------------------}
procedure TsWorkbook.FixSharedFormulas;
var
sheet: TsWorksheet;
i: Integer;
begin
for i := 0 to GetWorksheetCount-1 do
begin
sheet := GetWorksheetByIndex(i);
sheet.FixSharedFormulas
end;
end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Determines the maximum index of used columns and rows in all sheets of this Determines the maximum index of used columns and rows in all sheets of this
workbook. Respects VirtualMode. workbook. Respects VirtualMode.

View File

@ -2692,12 +2692,14 @@ begin
if ACell^.ContentType = cctError then if ACell^.ContentType = cctError then
AStrings.Add(Format('ErrorValue=%s', [GetEnumName(TypeInfo(TsErrorValue), ord(ACell^.ErrorValue))])); AStrings.Add(Format('ErrorValue=%s', [GetEnumName(TypeInfo(TsErrorValue), ord(ACell^.ErrorValue))]));
AStrings.Add(Format('FormulaValue=%s', [Worksheet.ReadFormulaAsString(ACell, true)])); AStrings.Add(Format('FormulaValue=%s', [Worksheet.ReadFormulaAsString(ACell, true)]));
{
if ACell^.SharedFormulaBase = nil then if ACell^.SharedFormulaBase = nil then
AStrings.Add('SharedFormulaBase=') AStrings.Add('SharedFormulaBase=')
else else
AStrings.Add(Format('SharedFormulaBase=%s', [GetCellString( AStrings.Add(Format('SharedFormulaBase=%s', [GetCellString(
ACell^.SharedFormulaBase^.Row, ACell^.SharedFormulaBase^.Col) ACell^.SharedFormulaBase^.Row, ACell^.SharedFormulaBase^.Col)
])); ]));
}
if (cfHyperlink in ACell^.Flags) then if (cfHyperlink in ACell^.Flags) then
begin begin
hyperlink := Worksheet.FindHyperlink(ACell); hyperlink := Worksheet.FindHyperlink(ACell);

View File

@ -548,13 +548,11 @@ type
{ Location of the cell } { Location of the cell }
Row: Cardinal; // zero-based Row: Cardinal; // zero-based
Col: Cardinal; // zero-based Col: Cardinal; // zero-based
Worksheet: Pointer; // Must be cast to TsWorksheet when used Worksheet: Pointer; // Must be cast to TsWorksheet when used (avoids circular unit reference)
{ Status flags } { Status flags }
Flags: TsCellFlags; Flags: TsCellFlags;
{ Index of format record in the workbook's FCellFormatList } { Index of format record in the workbook's FCellFormatList }
FormatIndex: Integer; FormatIndex: Integer;
{ Special information }
SharedFormulaBase: PCell; // Cell containing the shared formula
{ Cell content } { Cell content }
UTF8StringValue: String; // Strings cannot be part of a variant record UTF8StringValue: String; // Strings cannot be part of a variant record
FormulaValue: String; FormulaValue: String;

View File

@ -33,13 +33,17 @@ type
// Test reconstruction of formula strings // Test reconstruction of formula strings
procedure Test_Write_Read_FormulaStrings(AFormat: TsSpreadsheetFormat; procedure Test_Write_Read_FormulaStrings(AFormat: TsSpreadsheetFormat;
UseRPNFormula: Boolean); UseRPNFormula: Boolean);
{
// Test reconstruction of shared formula strings // Test reconstruction of shared formula strings
procedure Test_Write_Read_SharedFormulaStrings(AFormat: TsSpreadsheetFormat); procedure Test_Write_Read_SharedFormulaStrings(AFormat: TsSpreadsheetFormat);
// Test calculation of formulas // Test calculation of formulas
}
procedure Test_Write_Read_CalcFormulas(AFormat: TsSpreadsheetformat; procedure Test_Write_Read_CalcFormulas(AFormat: TsSpreadsheetformat;
UseRPNFormula: Boolean); UseRPNFormula: Boolean);
{
// Test calculation of shared formulas // Test calculation of shared formulas
procedure Test_Write_Read_CalcSharedFormulas(AFormat: TsSpreadsheetformat); procedure Test_Write_Read_CalcSharedFormulas(AFormat: TsSpreadsheetformat);
}
published published
// Writes out formulas & reads them back. // Writes out formulas & reads them back.
@ -54,6 +58,7 @@ type
{ ODS Tests } { ODS Tests }
procedure Test_Write_Read_FormulaStrings_ODS; procedure Test_Write_Read_FormulaStrings_ODS;
(*
// Writes out shared formulas & reads them back. // Writes out shared formulas & reads them back.
{ BIFF2 Tests } { BIFF2 Tests }
procedure Test_Write_Read_SharedFormulaStrings_BIFF2; procedure Test_Write_Read_SharedFormulaStrings_BIFF2;
@ -64,7 +69,7 @@ type
{ OOXML Tests } { OOXML Tests }
procedure Test_Write_Read_SharedFormulaStrings_OOXML; procedure Test_Write_Read_SharedFormulaStrings_OOXML;
{ ODS Tests } { ODS Tests }
procedure Test_Write_Read_SharedFormulaStrings_ODS; procedure Test_Write_Read_SharedFormulaStrings_ODS; *)
// Writes out and calculates rpn formulas, read back // Writes out and calculates rpn formulas, read back
{ BIFF2 Tests } { BIFF2 Tests }
@ -89,7 +94,7 @@ type
procedure Test_Write_Read_CalcStringFormula_OOXML; procedure Test_Write_Read_CalcStringFormula_OOXML;
{ ODS Tests } { ODS Tests }
procedure Test_Write_Read_CalcStringFormula_ODS; procedure Test_Write_Read_CalcStringFormula_ODS;
(*
// Writes out and calculates shared formulas, read back // Writes out and calculates shared formulas, read back
{ BIFF2 Tests } { BIFF2 Tests }
procedure Test_Write_Read_CalcSharedFormula_BIFF2; procedure Test_Write_Read_CalcSharedFormula_BIFF2;
@ -100,7 +105,7 @@ type
{ OOXML Tests } { OOXML Tests }
procedure Test_Write_Read_CalcSharedFormula_OOXML; procedure Test_Write_Read_CalcSharedFormula_OOXML;
{ ODS Tests } { ODS Tests }
procedure Test_Write_Read_CalcSharedFormula_ODS; procedure Test_Write_Read_CalcSharedFormula_ODS; *)
end; end;
@ -227,7 +232,7 @@ end;
{ Test writing and reading (i.e. reconstruction) of shared formula strings } { Test writing and reading (i.e. reconstruction) of shared formula strings }
(*
procedure TSpreadWriteReadFormulaTests.Test_Write_Read_SharedFormulaStrings( procedure TSpreadWriteReadFormulaTests.Test_Write_Read_SharedFormulaStrings(
AFormat: TsSpreadsheetFormat); AFormat: TsSpreadsheetFormat);
const const
@ -352,7 +357,7 @@ procedure TSpreadWriteReadFormulaTests.Test_Write_Read_SharedFormulaStrings_ODS;
begin begin
Test_Write_Read_SharedFormulaStrings(sfOpenDocument); Test_Write_Read_SharedFormulaStrings(sfOpenDocument);
end; end;
*)
{ Test calculation of formulas } { Test calculation of formulas }
procedure TSpreadWriteReadFormulaTests.Test_Write_Read_CalcFormulas( procedure TSpreadWriteReadFormulaTests.Test_Write_Read_CalcFormulas(
@ -550,6 +555,7 @@ end;
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Calculation of shared formulas // Calculation of shared formulas
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
(*
procedure TSpreadWriteReadFormulaTests.Test_Write_Read_CalcSharedFormulas( procedure TSpreadWriteReadFormulaTests.Test_Write_Read_CalcSharedFormulas(
AFormat: TsSpreadsheetFormat); AFormat: TsSpreadsheetFormat);
const const
@ -707,7 +713,7 @@ procedure TSpreadWriteReadFormulaTests.Test_Write_Read_CalcSharedFormula_ODS;
begin begin
Test_Write_Read_CalcSharedFormulas(sfOpenDocument); Test_Write_Read_CalcSharedFormulas(sfOpenDocument);
end; end;
*)
initialization initialization
// Register so these tests are included in a full run // Register so these tests are included in a full run

File diff suppressed because it is too large Load Diff

View File

@ -128,10 +128,12 @@ type
procedure WriteRPNFormula(AStream: TStream; const ARow, ACol: Cardinal; procedure WriteRPNFormula(AStream: TStream; const ARow, ACol: Cardinal;
const AFormula: TsRPNFormula; ACell: PCell); override; const AFormula: TsRPNFormula; ACell: PCell); override;
function WriteRPNFunc(AStream: TStream; AIdentifier: Word): Word; override; function WriteRPNFunc(AStream: TStream; AIdentifier: Word): Word; override;
{
procedure WriteRPNSharedFormulaLink(AStream: TStream; ACell: PCell; procedure WriteRPNSharedFormulaLink(AStream: TStream; ACell: PCell;
var RPNLength: Word); override; var RPNLength: Word); override;
}
procedure WriteRPNTokenArraySize(AStream: TStream; ASize: Word); override; procedure WriteRPNTokenArraySize(AStream: TStream; ASize: Word); override;
procedure WriteSharedFormula(AStream: TStream; ACell: PCell); override; // procedure WriteSharedFormula(AStream: TStream; ACell: PCell); override;
procedure WriteStringRecord(AStream: TStream; AString: String); override; procedure WriteStringRecord(AStream: TStream; AString: String); override;
procedure WriteWindow1(AStream: TStream); override; procedure WriteWindow1(AStream: TStream); override;
procedure WriteWindow2(AStream: TStream; ASheet: TsWorksheet); procedure WriteWindow2(AStream: TStream; ASheet: TsWorksheet);
@ -1670,9 +1672,10 @@ begin
AStream.WriteByte(1); AStream.WriteByte(1);
{ Formula data (RPN token array) } { Formula data (RPN token array) }
{
if ACell^.SharedFormulaBase <> nil then if ACell^.SharedFormulaBase <> nil then
WriteRPNSharedFormulaLink(AStream, ACell, RPNLength) WriteRPNSharedFormulaLink(AStream, ACell, RPNLength)
else else}
WriteRPNTokenArray(AStream, ACell, AFormula, false, RPNLength); WriteRPNTokenArray(AStream, ACell, AFormula, false, RPNLength);
{ Finally write sizes after we know them } { Finally write sizes after we know them }
@ -1683,7 +1686,7 @@ begin
{ Write following STRING record if formula result is a non-empty string } { Write following STRING record if formula result is a non-empty string }
if (ACell^.ContentType = cctUTF8String) and (ACell^.UTF8StringValue <> '') then if (ACell^.ContentType = cctUTF8String) and (ACell^.UTF8StringValue <> '') then
WriteStringRecord(AStream, ACell^.UTF8StringValue); WriteSTRINGRecord(AStream, ACell^.UTF8StringValue);
end; end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
@ -1696,7 +1699,7 @@ begin
AStream.WriteByte(Lo(AIdentifier)); AStream.WriteByte(Lo(AIdentifier));
Result := 1; Result := 1;
end; end;
(*
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
This method is intended to write a link to the cell containing the shared This method is intended to write a link to the cell containing the shared
formula used by the cell. But since BIFF2 does not support shared formulas formula used by the cell. But since BIFF2 does not support shared formulas
@ -1721,7 +1724,7 @@ begin
// Clean up // Clean up
SetLength(formula, 0); SetLength(formula, 0);
end; end;
*)
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Writes the size of the RPN token array. Called from WriteRPNFormula. Writes the size of the RPN token array. Called from WriteRPNFormula.
Overrides xlscommon. Overrides xlscommon.
@ -1731,7 +1734,7 @@ procedure TsSpreadBIFF2Writer.WriteRPNTokenArraySize(AStream: TStream;
begin begin
AStream.WriteByte(ASize); AStream.WriteByte(ASize);
end; end;
(*
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Is intended to write the token array of a shared formula stored in ACell. Is intended to write the token array of a shared formula stored in ACell.
But since BIFF2 does not support shared formulas this method must not do But since BIFF2 does not support shared formulas this method must not do
@ -1740,7 +1743,7 @@ end;
procedure TsSpreadBIFF2Writer.WriteSharedFormula(AStream: TStream; ACell: PCell); procedure TsSpreadBIFF2Writer.WriteSharedFormula(AStream: TStream; ACell: PCell);
begin begin
Unused(AStream, ACell); Unused(AStream, ACell);
end; end; *)
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Writes an Excel 2 STRING record which immediately follows a FORMULA record Writes an Excel 2 STRING record which immediately follows a FORMULA record

View File

@ -355,8 +355,8 @@ const
MASK_HLINK_ABSOLUTE = $00000002; MASK_HLINK_ABSOLUTE = $00000002;
MASK_HLINK_DESCRIPTION = $00000014; MASK_HLINK_DESCRIPTION = $00000014;
MASK_HLINK_TEXTMARK = $00000008; MASK_HLINK_TEXTMARK = $00000008;
MASK_HLINK_TARGETFRAME = $00000080; {%H-}MASK_HLINK_TARGETFRAME = $00000080;
MASK_HLINK_UNCPATH = $00000100; {%H-}MASK_HLINK_UNCPATH = $00000100;
SHAPEID_BASE = 1024; SHAPEID_BASE = 1024;
@ -1446,7 +1446,7 @@ begin
col2 := WordLEToN(AStream.ReadWord); col2 := WordLEToN(AStream.ReadWord);
{ GUID of standard link } { GUID of standard link }
AStream.ReadBuffer(guid, SizeOf(guid)); AStream.ReadBuffer(guid{%H-}, SizeOf(guid));
{ unknown DWord } { unknown DWord }
AStream.ReadDWord; AStream.ReadDWord;
@ -2648,7 +2648,7 @@ procedure TsSpreadBIFF8Writer.WriteMergedCells(AStream: TStream;
const const
MAX_PER_RECORD = 1026; MAX_PER_RECORD = 1026;
var var
n0, n, i: Integer; n0, n: Integer;
rng: PsCellRange; rng: PsCellRange;
newRecord: Boolean; newRecord: Boolean;
begin begin
@ -2678,31 +2678,6 @@ begin
n := Min(n0, MAX_PER_RECORD); n := Min(n0, MAX_PER_RECORD);
end; end;
end; end;
(*
while n0 > 0 do begin
n := Min(n0, MAX_PER_RECORD);
// at most 1026 merged ranges per BIFF record, the rest goes into a new record
{ BIFF record header }
WriteBIFFHeader(AStream, INT_EXCEL_ID_MERGEDCELLS, 2 + n*8);
{ Number of cell ranges in this record }
AStream.WriteWord(WordToLE(n));
{ Loop for writing the merged cell ranges }
rng := PsCellRange(AWorksheet.MergedCells.GetFirst);
while (n > 0) do begin
AStream.WriteWord(WordToLE(rng^.Row1));
AStream.WriteWord(WordToLE(rng^.Row2));
AStream.WriteWord(WordToLE(rng^.Col1));
AStream.WriteWord(WordToLE(rng^.Col2));
dec(n);
rng := PsCellRange(AWorksheet.MergedCells.GetNext);
end;
dec(n0, MAX_PER_RECORD);
end;
*)
end; end;
{@@----------------------------------------------------------------------------- {@@-----------------------------------------------------------------------------

View File

@ -313,7 +313,8 @@ type
out AFlags: TsRelFlags); virtual; out AFlags: TsRelFlags); virtual;
function ReadRPNFunc(AStream: TStream): Word; virtual; function ReadRPNFunc(AStream: TStream): Word; virtual;
procedure ReadRPNSharedFormulaBase(AStream: TStream; out ARow, ACol: Cardinal); virtual; procedure ReadRPNSharedFormulaBase(AStream: TStream; out ARow, ACol: Cardinal); virtual;
function ReadRPNTokenArray(AStream: TStream; ACell: PCell): Boolean; function ReadRPNTokenArray(AStream: TStream; ACell: PCell;
ASharedFormulaBase: PCell = nil): Boolean;
function ReadRPNTokenArraySize(AStream: TStream): word; virtual; function ReadRPNTokenArraySize(AStream: TStream): word; virtual;
procedure ReadSharedFormula(AStream: TStream); procedure ReadSharedFormula(AStream: TStream);
@ -404,8 +405,10 @@ type
const AFormula: TsRPNFormula; ACell: PCell); virtual; const AFormula: TsRPNFormula; ACell: PCell); virtual;
function WriteRPNFunc(AStream: TStream; AIdentifier: Word): Word; virtual; function WriteRPNFunc(AStream: TStream; AIdentifier: Word): Word; virtual;
procedure WriteRPNResult(AStream: TStream; ACell: PCell); procedure WriteRPNResult(AStream: TStream; ACell: PCell);
{
procedure WriteRPNSharedFormulaLink(AStream: TStream; ACell: PCell; procedure WriteRPNSharedFormulaLink(AStream: TStream; ACell: PCell;
var RPNLength: Word); virtual; var RPNLength: Word); virtual;
}
procedure WriteRPNTokenArray(AStream: TStream; ACell: PCell; procedure WriteRPNTokenArray(AStream: TStream; ACell: PCell;
const AFormula: TsRPNFormula; UseRelAddr: Boolean; var RPNLength: Word); const AFormula: TsRPNFormula; UseRelAddr: Boolean; var RPNLength: Word);
procedure WriteRPNTokenArraySize(AStream: TStream; ASize: Word); virtual; procedure WriteRPNTokenArraySize(AStream: TStream; ASize: Word); virtual;
@ -413,10 +416,12 @@ type
// Writes out a SELECTION record // Writes out a SELECTION record
procedure WriteSelection(AStream: TStream; ASheet: TsWorksheet; APane: Byte); procedure WriteSelection(AStream: TStream; ASheet: TsWorksheet; APane: Byte);
procedure WriteSelections(AStream: TStream; ASheet: TsWorksheet); procedure WriteSelections(AStream: TStream; ASheet: TsWorksheet);
(*
// Writes out a shared formula // Writes out a shared formula
procedure WriteSharedFormula(AStream: TStream; ACell: PCell); virtual; procedure WriteSharedFormula(AStream: TStream; ACell: PCell); virtual;
procedure WriteSharedFormulaRange(AStream: TStream; procedure WriteSharedFormulaRange(AStream: TStream;
AFirstRow, AFirstCol, ALastRow, ALastCol: Cardinal); virtual; AFirstRow, AFirstCol, ALastRow, ALastCol: Cardinal); virtual;
*)
procedure WriteSheetPR(AStream: TStream); procedure WriteSheetPR(AStream: TStream);
procedure WriteStringRecord(AStream: TStream; AString: String); virtual; procedure WriteStringRecord(AStream: TStream; AString: String); virtual;
// Writes cell content received by workbook in OnNeedCellData event // Writes cell content received by workbook in OnNeedCellData event
@ -1591,7 +1596,7 @@ end;
rpn formula, converts it to a string formula and stores it in the cell. rpn formula, converts it to a string formula and stores it in the cell.
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
function TsSpreadBIFFReader.ReadRPNTokenArray(AStream: TStream; function TsSpreadBIFFReader.ReadRPNTokenArray(AStream: TStream;
ACell: PCell): Boolean; ACell: PCell; ASharedFormulaBase: PCell = nil): Boolean;
var var
n: Word; n: Word;
p0: Int64; p0: Int64;
@ -1607,7 +1612,8 @@ var
funcCode: Word; funcCode: Word;
b: Byte; b: Byte;
found: Boolean; found: Boolean;
formula: TsRPNformula; rpnFormula: TsRPNformula;
strFormula: String;
begin begin
rpnItem := nil; rpnItem := nil;
n := ReadRPNTokenArraySize(AStream); n := ReadRPNTokenArraySize(AStream);
@ -1652,16 +1658,16 @@ begin
// For compatibility with other formats, convert offsets back to regular indexes. // For compatibility with other formats, convert offsets back to regular indexes.
if (rfRelRow in flags) if (rfRelRow in flags)
then r := LongInt(ACell^.Row) + dr then r := LongInt(ACell^.Row) + dr
else r := LongInt(ACell^.SharedFormulaBase^.Row) + dr; else r := LongInt(ASharedFormulaBase^.Row) + dr;
if (rfRelRow2 in flags) if (rfRelRow2 in flags)
then r2 := LongInt(ACell^.Row) + dr2 then r2 := LongInt(ACell^.Row) + dr2
else r2 := LongInt(ACell^.SharedFormulaBase^.Row) + dr2; else r2 := LongInt(ASharedFormulaBase^.Row) + dr2;
if (rfRelCol in flags) if (rfRelCol in flags)
then c := LongInt(ACell^.Col) + dc then c := LongInt(ACell^.Col) + dc
else c := LongInt(ACell^.SharedFormulaBase^.Col) + dc; else c := LongInt(ASharedFormulaBase^.Col) + dc;
if (rfRelCol2 in flags) if (rfRelCol2 in flags)
then c2 := LongInt(ACell^.Col) + dc2 then c2 := LongInt(ACell^.Col) + dc2
else c2 := LongInt(ACell^.SharedFormulaBase^.Col) + dc2; else c2 := LongInt(ASharedFormulaBase^.Col) + dc2;
rpnItem := RPNCellRange(r, c, r2, c2, flags, rpnItem); rpnItem := RPNCellRange(r, c, r2, c2, flags, rpnItem);
end; end;
INT_EXCEL_TOKEN_TMISSARG: INT_EXCEL_TOKEN_TMISSARG:
@ -1710,12 +1716,9 @@ begin
end; end;
INT_EXCEL_TOKEN_TEXP: INT_EXCEL_TOKEN_TEXP:
// Indicates that cell belongs to a shared or array formula. We determine // Indicates that cell belongs to a shared or array formula.
// the base cell of the shared formula and store it in the current cell. // This information is not needed any more.
begin
ReadRPNSharedFormulaBase(AStream, r, c); ReadRPNSharedFormulaBase(AStream, r, c);
ACell^.SharedFormulaBase := FWorksheet.FindCell(r, c);
end;
else else
found := false; found := false;
@ -1734,11 +1737,16 @@ begin
Result := false; Result := false;
end end
else begin else begin
formula := CreateRPNFormula(rpnItem, true); // true --> we have to flip the order of items! rpnFormula := CreateRPNFormula(rpnItem, true); // true --> we have to flip the order of items!
strFormula := FWorksheet.ConvertRPNFormulaToStringFormula(rpnFormula);
if strFormula <> '' then
ACell^.FormulaValue := strFormula;
{
if (ACell^.SharedFormulaBase = nil) or (ACell = ACell^.SharedFormulaBase) then if (ACell^.SharedFormulaBase = nil) or (ACell = ACell^.SharedFormulaBase) then
ACell^.FormulaValue := FWorksheet.ConvertRPNFormulaToStringFormula(formula) ACell^.FormulaValue := FWorksheet.ConvertRPNFormulaToStringFormula(formula)
else else
ACell^.FormulaValue := ''; ACell^.FormulaValue := '';
}
Result := true; Result := true;
end; end;
end; end;
@ -1760,7 +1768,7 @@ end;
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
procedure TsSpreadBIFFReader.ReadSharedFormula(AStream: TStream); procedure TsSpreadBIFFReader.ReadSharedFormula(AStream: TStream);
var var
r1, {%H-}r2, c1, {%H-}c2: Cardinal; r, r1, r2, c, c1, c2: Cardinal;
cell: PCell; cell: PCell;
begin begin
// Cell range in which the formula is valid // Cell range in which the formula is valid
@ -1769,7 +1777,7 @@ begin
c1 := AStream.ReadByte; // 8 bit, even for BIFF8 c1 := AStream.ReadByte; // 8 bit, even for BIFF8
c2 := AStream.ReadByte; c2 := AStream.ReadByte;
{ Create cell } { Create cell - this is the "base" of the shared formula }
if FIsVirtualMode then begin // "Virtual" cell if FIsVirtualMode then begin // "Virtual" cell
InitCell(r1, c1, FVirtualCell); InitCell(r1, c1, FVirtualCell);
cell := @FVirtualCell; cell := @FVirtualCell;
@ -1783,7 +1791,12 @@ begin
AStream.ReadByte; AStream.ReadByte;
// RPN formula tokens // RPN formula tokens
ReadRPNTokenArray(AStream, cell); ReadRPNTokenArray(AStream, cell, cell); //base);
// Copy shared formula to individual cells in the specified range
for r := r1 to r2 do
for c := c1 to c2 do
FWorksheet.CopyFormula(cell, r, c);
end; end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
@ -2534,8 +2547,12 @@ begin
if (ARow >= FLimitations.MaxRowCount) or (ACol >= FLimitations.MaxColCount) then if (ARow >= FLimitations.MaxRowCount) or (ACol >= FLimitations.MaxColCount) then
exit; exit;
if Length(AFormula) = 0 then
exit;
{
if not ((Length(AFormula) > 0) or (ACell^.SharedFormulaBase <> nil)) then if not ((Length(AFormula) > 0) or (ACell^.SharedFormulaBase <> nil)) then
exit; exit;
}
{ BIFF Record header } { BIFF Record header }
AStream.WriteWord(WordToLE(INT_EXCEL_ID_FORMULA)); AStream.WriteWord(WordToLE(INT_EXCEL_ID_FORMULA));
@ -2560,18 +2577,19 @@ begin
AStream.WriteDWord(0); AStream.WriteDWord(0);
{ Formula data (RPN token array) } { Formula data (RPN token array) }
{
if ACell^.SharedFormulaBase <> nil then if ACell^.SharedFormulaBase <> nil then
WriteRPNSharedFormulaLink(AStream, ACell, RPNLength) WriteRPNSharedFormulaLink(AStream, ACell, RPNLength)
else else
}
WriteRPNTokenArray(AStream, ACell, AFormula, false, RPNLength); WriteRPNTokenArray(AStream, ACell, AFormula, false, RPNLength);
{ Write sizes in the end, after we known them } { Write sizes in the end, after we known them }
FinalPos := AStream.Position; FinalPos := AStream.Position;
AStream.Position := RecordSizePos; AStream.Position := RecordSizePos;
AStream.WriteWord(WordToLE(FinalPos - StartPos)); AStream.WriteWord(WordToLE(FinalPos - StartPos));
// AStream.WriteWord(WordToLE(22 + RPNLength));
AStream.Position := FinalPos; AStream.Position := FinalPos;
(*
{ If the cell is the first cell of a range with a shared formula write the { If the cell is the first cell of a range with a shared formula write the
shared formula RECORD here. The shared formula RECORD must follow the shared formula RECORD here. The shared formula RECORD must follow the
first FORMULA record referring to the shared formula} first FORMULA record referring to the shared formula}
@ -2580,10 +2598,11 @@ begin
(ACol = ACell^.SharedFormulaBase^.Col) (ACol = ACell^.SharedFormulaBase^.Col)
then then
WriteSharedFormula(AStream, ACell^.SharedFormulaBase); WriteSharedFormula(AStream, ACell^.SharedFormulaBase);
*)
{ Write following STRING record if formula result is a non-empty string. } { Write following STRING record if formula result is a non-empty string. }
if (ACell^.ContentType = cctUTF8String) and (ACell^.UTF8StringValue <> '') then if (ACell^.ContentType = cctUTF8String) and (ACell^.UTF8StringValue <> '') then
WriteStringRecord(AStream, ACell^.UTF8StringValue); WriteSTRINGRecord(AStream, ACell^.UTF8StringValue);
end; end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
@ -2642,7 +2661,7 @@ begin
{ Write result of the formula, encoded above } { Write result of the formula, encoded above }
AStream.WriteBuffer(Data, 8); AStream.WriteBuffer(Data, 8);
end; end;
(*
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Is called from WriteRPNFormula in the case that the cell uses a shared Is called from WriteRPNFormula in the case that the cell uses a shared
formula and writes the token "array" pointing to the shared formula base. formula and writes the token "array" pointing to the shared formula base.
@ -2669,6 +2688,7 @@ begin
AStream.WriteBuffer(rec, SizeOf(rec)); AStream.WriteBuffer(rec, SizeOf(rec));
RPNLength := SizeOf(rec); RPNLength := SizeOf(rec);
end; end;
*)
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Writes the token array of the given RPN formula and returns its size Writes the token array of the given RPN formula and returns its size
@ -3014,7 +3034,7 @@ begin
end; end;
end; end;
end; end;
(*
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Writes the token array of a shared formula stored in ACell. Writes the token array of a shared formula stored in ACell.
Note: Relative cell addresses of a shared formula are defined by Note: Relative cell addresses of a shared formula are defined by
@ -3083,7 +3103,7 @@ begin
AStream.WriteByte(AFirstCol); AStream.WriteByte(AFirstCol);
// Index to last rcolumn // Index to last rcolumn
AStream.WriteByte(ALastCol); AStream.WriteByte(ALastCol);
end; end; *)
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Writes a SHEETPR Record. Writes a SHEETPR Record.

View File

@ -67,7 +67,7 @@ type
FBorderList: TFPList; FBorderList: TFPList;
FHyperlinkList: TFPList; FHyperlinkList: TFPList;
FThemeColors: array of TsColorValue; FThemeColors: array of TsColorValue;
FSharedFormulas: TStringList; // FSharedFormulas: TStringList;
FWrittenByFPS: Boolean; FWrittenByFPS: Boolean;
procedure ApplyCellFormatting(ACell: PCell; XfIndex: Integer); procedure ApplyCellFormatting(ACell: PCell; XfIndex: Integer);
procedure ApplyHyperlinks(AWorksheet: TsWorksheet); procedure ApplyHyperlinks(AWorksheet: TsWorksheet);
@ -467,7 +467,7 @@ begin
// Set up the default palette in order to have the default color names correct. // Set up the default palette in order to have the default color names correct.
Workbook.UseDefaultPalette; Workbook.UseDefaultPalette;
FSharedFormulas := TStringList.Create; // FSharedFormulas := TStringList.Create;
FSharedStrings := TStringList.Create; FSharedStrings := TStringList.Create;
FFillList := TFPList.Create; FFillList := TFPList.Create;
FBorderList := TFPList.Create; FBorderList := TFPList.Create;
@ -493,7 +493,7 @@ begin
FHyperlinkList.Free; FHyperlinkList.Free;
FSharedStrings.Free; FSharedStrings.Free;
FSharedFormulas.Free; // FSharedFormulas.Free;
// FCellFormatList is destroyed by ancestor // FCellFormatList is destroyed by ancestor
inherited Destroy; inherited Destroy;
@ -677,6 +677,8 @@ var
sstIndex: Integer; sstIndex: Integer;
number: Double; number: Double;
fmt: TsCellFormat; fmt: TsCellFormat;
rng: TsCellRange;
r,c: Cardinal;
begin begin
if ANode = nil then if ANode = nil then
exit; exit;
@ -720,14 +722,21 @@ begin
begin begin
// Shared formula // Shared formula
s := GetAttrValue(datanode, 'ref'); s := GetAttrValue(datanode, 'ref');
if (s <> '') then // This is the shared formula base if (s <> '') then // This is the shared formula range
begin begin
// Split shared formula into single-cell formulas
ParseCellRangeString(s, rng);
for r := rng.Row1 to rng.Row2 do
for c := rng.Col1 to rng.Col2 do
FWorksheet.CopyFormula(cell, r, c);
(*
s := GetAttrValue(datanode, 'si'); s := GetAttrValue(datanode, 'si');
if s <> '' then if s <> '' then
FSharedFormulas.AddObject(addr, {%H-}Pointer(PtrInt(StrToInt(s)))); FSharedFormulas.AddObject(addr, {%H-}Pointer(PtrInt(StrToInt(s))));
FWorksheet.WriteFormula(cell, formulaStr); FWorksheet.WriteFormula(cell, formulaStr);
cell^.SharedFormulaBase := cell; cell^.SharedFormulaBase := cell;
//AWorksheet.WriteSharedFormula(s, formulaStr); AWorksheet.WriteSharedFormula(s, formulaStr);
end else end else
begin begin
s := GetAttrValue(datanode, 'si'); s := GetAttrValue(datanode, 'si');
@ -736,6 +745,7 @@ begin
s := FSharedFormulas[FSharedFormulas.IndexOfObject({%H-}Pointer(PtrInt(StrToInt(s))))]; s := FSharedFormulas[FSharedFormulas.IndexOfObject({%H-}Pointer(PtrInt(StrToInt(s))))];
cell^.SharedFormulaBase := FWorksheet.FindCell(s); cell^.SharedFormulaBase := FWorksheet.FindCell(s);
end; end;
*)
end; end;
end end
else else
@ -3279,9 +3289,6 @@ procedure TsSpreadOOXMLWriter.WriteFormula(AStream: TStream;
var var
cellPosText: String; cellPosText: String;
lStyleIndex: Integer; lStyleIndex: Integer;
r, r1, r2: Cardinal;
c, c1, c2: Cardinal;
cell: PCell;
t, v: String; t, v: String;
begin begin
cellPosText := TsWorksheet.CellPosToText(ARow, ACol); cellPosText := TsWorksheet.CellPosToText(ARow, ACol);
@ -3323,61 +3330,6 @@ begin
end; end;
end; end;
// Cell uses a shared formula
if Assigned(ACell^.SharedFormulaBase) then begin
// Cell is base of the shared formula, i.e. contains the shared formula
if (ACell = ACell^.SharedFormulaBase) then
begin
// Find range of cells using this shared formula
// The base of the shared formula is the left/top edge of the range
r1 := ACell^.Row;
r2 := r1;
r := r1 + 1;
while r <= FWorksheet.GetLastRowIndex do
begin
cell := FWorksheet.FindCell(r, ACell^.Col);
if (cell <> nil) and (cell^.SharedFormulaBase = ACell^.SharedFormulaBase) then
r2 := r
else
break;
inc(r);
end;
c1 := ACell^.Col;
c2 := c1;
c := c1 + 1;
while c <= FWorksheet.GetLastColIndex do
begin
cell := FWorksheet.FindCell(ACell^.Row, c);
if (cell <> nil) and (cell^.SharedFormulaBase = ACell^.SharedFormulaBase) then
c2 := c
else
break;
inc(c);
end;
AppendToStream(AStream, Format(
'<c r="%s" s="%d"%s>' +
'<f t="shared" ref="%s" si="%d">%s</f>' +
'%s' +
'</c>', [
CellPosText, lStyleIndex, t,
GetCellRangeString(ACell^.Row, ACell^.Col, r2, c2),
{%H-}PtrInt(ACell), // Use the cell pointer as ID of the shared formula
PrepareFormula(ACell^.FormulaValue),
v
]));
end else
// Cell uses the shared formula
AppendToStream(AStream, Format(
'<c r="%s" s="%d"%s>' +
'<f t="shared" si="%d" />' +
'%s' +
'</c>', [
CellPosText, lStyleIndex, t,
{%H-}PtrInt(ACell^.SharedFormulaBase), // ID of the shared formula
v
]));
end else begin
// "normal" formula
AppendToStream(AStream, Format( AppendToStream(AStream, Format(
'<c r="%s" s="%d"%s>' + '<c r="%s" s="%d"%s>' +
'<f>%s</f>' + '<f>%s</f>' +
@ -3388,7 +3340,6 @@ begin
v v
])); ]));
end; end;
end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Writes a string to the stream Writes a string to the stream