From 394c8032df8116a289944cd2d428bf08a737a058 Mon Sep 17 00:00:00 2001 From: wp_xxyyzz Date: Mon, 4 Jun 2018 09:47:08 +0000 Subject: [PATCH] fpspreadsheet: Fix 3d formulas not changing when rows/cols are inserted/deleted in referenced sheets. git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@6454 8e941d3f-bd1b-0410-a28a-d453659cc2b4 --- .../source/common/fpsclasses.pas | 263 ++++++++++++------ .../source/common/fpsexprparser.pas | 79 +++--- .../source/common/fpspreadsheet.pas | 28 +- .../fpspreadsheet/source/common/xlsbiff8.pas | 9 +- .../fpspreadsheet/source/common/xlscommon.pas | 9 +- 5 files changed, 254 insertions(+), 134 deletions(-) diff --git a/components/fpspreadsheet/source/common/fpsclasses.pas b/components/fpspreadsheet/source/common/fpsclasses.pas index da11492c6..73835c36c 100644 --- a/components/fpspreadsheet/source/common/fpsclasses.pas +++ b/components/fpspreadsheet/source/common/fpsclasses.pas @@ -193,10 +193,10 @@ type AParsedFormula: TsExpressionParser = nil): PsFormula; procedure DeleteFormula(ACell: PCell); overload; procedure DeleteFormula(ARow, ACol: Cardinal); overload; - procedure DeleteRowOrCol(AIndex: Cardinal; IsRow: Boolean); function FindFormula(ACell: PCell): PsFormula; overload; function FindFormula(ARow, ACol: Cardinal): PsFormula; overload; - procedure InsertRowOrCol(AIndex: Cardinal; IsRow: Boolean); + procedure FixReferences(AIndex: Cardinal; IsRow, IsDeleting: Boolean; + InSheet: TsBasicWorksheet); // enumerators function GetEnumerator: TsFormulaEnumerator; end; @@ -252,140 +252,209 @@ end; { Call-back function for formulas when rows/cols are inserted/deleted } -procedure FixDeletedCol(AExprNode: TsExprNode; AData: Pointer; +procedure FixDeletedCol(AExprNode: TsExprNode; AData1, AData2: Pointer; var MustRebuildFormulas: Boolean); var colIndex: Cardinal; + referencedSheet, referencedSheet2: TsBasicWorksheet; + changedSheet: TsBasicWorksheet; rng: TsCellRange; begin - colIndex := PtrInt(AData); + colIndex := PtrInt(AData1); + changedSheet := TsBasicWorksheet(AData2); if AExprNode is TsCellExprNode then begin - if not TsCellExprNode(AExprNode).Has3dLink then - if TsCellExprNode(AExprNode).Col > colIndex then begin - TsCellExprNode(AExprNode).Col := TsCellexprNode(AExprNode).Col - 1; - MustRebuildFormulas := true; - end else - if TsCellExprNode(AExprNode).Col = colIndex then begin - TsCellExprNode(AExprNode).Error := errIllegalRef; - MustRebuildFormulas := true; - end; - end else + if TsCellExprNode(AExprNode).Error <> errOK then + exit; + referencedSheet := TsCellExprNode(AExprNode).GetSheet; + if TsCellExprNode(AExprNode).Has3dLink and (referencedSheet <> changedSheet) then + exit; + if TsCellExprNode(AExprNode).Col > colIndex then begin + TsCellExprNode(AExprNode).Col := TsCellExprNode(AExprNode).Col - 1; + MustRebuildFormulas := true; + end else + if TsCellExprNode(AExprNode).col = colIndex then begin + TsCellExprNode(AExprNode).Error := errIllegalRef; + MustRebuildFormulas := true; + end; + end + else if AExprNode is TsCellRangeExprNode then begin - if not TsCellRangeExprNode(AExprNode).Has3dLink then begin - rng := TsCellRangeExprNode(AExprNode).Range; - if (rng.Col1 = colIndex) and (rng.Col2 = colIndex) then begin - TsCellRangeExprNode(AExprNode).Error := errIllegalRef; + if TsCellRangeExprNode(AExprNode).Error <> errOK then + exit; + referencedSheet := TsCellRangeExprNode(AExprNode).GetSheet(1); + referencedSheet2 := TsCellRangeExprNode(AExprNode).GetSheet(2); + if TsCellRangeExprNode(AExprNode).Has3dLink and + (referencedSheet <> changedSheet) and + (referencedSheet2 <> changedSheet) + then + exit; + rng := TsCellRangeExprNode(AExprNode).Range; + if (rng.Col1 = colIndex) and (rng.Col2 = colIndex) then begin + TsCellRangeExprNode(AExprNode).Error := errIllegalRef; + MustRebuildFormulas := true; + end else begin + if rng.Col1 > colIndex then begin + dec(rng.Col1); MustRebuildFormulas := true; - end else begin - if rng.Col1 > colIndex then begin - dec(rng.Col1); - MustRebuildFormulas := true; - end; - if rng.Col2 >= colIndex then begin - dec(rng.Col2); - MustRebuildFormulas := true; - end; - TsCellRangeExprNode(AExprNode).Range := rng; end; + if rng.Col2 >= colIndex then begin + dec(rng.Col2); + MustRebuildFormulas := true; + end; + TsCellRangeExprNode(AExprNode).Range := rng; end; end; end; -procedure FixDeletedRow(AExprNode: TsExprNode; AData: Pointer; +procedure FixDeletedRow(AExprNode: TsExprNode; AData1, AData2: Pointer; var MustRebuildFormulas: Boolean); var rowIndex: Cardinal; rng: TsCellRange; + changedSheet: TsBasicWorksheet; + referencedSheet, referencedSheet2: TsBasicWorksheet; begin - rowIndex := PtrInt(AData); + rowIndex := PtrInt(AData1); + changedSheet := TsBasicWorksheet(AData2); + if AExprNode is TsCellExprNode then begin - if not TsCellExprNode(AExprNode).Has3dLink then - if TsCellExprNode(AExprNode).Row > rowIndex then begin - TsCellExprNode(AExprNode).Row := TsCellExprNode(AExprNode).Row - 1; - MustRebuildFormulas := true; - end else - if TsCellExprNode(AExprNode).Row = rowIndex then begin - TsCelLExprNode(AExprNode).Error := errIllegalRef; - MustRebuildFormulas := true; - end; - end else + if TsCellExprNode(AExprNode).Error <> errOK then + exit; + referencedSheet := TsCellExprNode(AExprNode).GetSheet; + if TsCellExprNode(AExprNode).Has3dLink and (referencedSheet <> changedSheet) then + exit; + if TsCellExprNode(AExprNode).Row > rowIndex then begin + TsCellExprNode(AExprNode).Row := TsCellExprNode(AExprNode).Row - 1; + MustRebuildFormulas := true; + end else + if TsCellExprNode(AExprNode).Row = rowIndex then begin + TsCellExprNode(AExprNode).Error := errIllegalRef; + MustRebuildFormulas := true; + end; + end + else if AExprNode is TsCellRangeExprNode then begin - if not TsCellRangeExprNode(AExprNode).Has3dLink then begin - rng := TsCellRangeExprNode(AExprNode).Range; - if (rng.Row1 = rowIndex) and (rng.Row2 = rowIndex) then begin - TsCellRangeExprNode(AExprNode).Error := errIllegalRef; + if TsCellRangeExprNode(AExprNode).Error <> errOK then + exit; + referencedSheet := TsCellRangeExprNode(AExprNode).GetSheet(1); + referencedSheet2 := TsCellRangeExprNode(AExprNode).GetSheet(2); + if TsCellRangeExprNode(AExprNode).Has3dLink and + (referencedSheet <> changedSheet) and + (referencedSheet2 <> changedSheet) + then + exit; + rng := TsCellRangeExprNode(AExprNode).Range; + if (rng.Row1 = rowIndex) and (rng.Row2 = rowIndex) then begin + TsCellRangeExprNode(AExprNode).Error := errIllegalRef; + MustRebuildFormulas := true; + end else + begin + if rng.Row1 > rowIndex then begin + dec(rng.Row1); MustRebuildFormulas := true; - end else - begin - if rng.Row1 > rowIndex then begin - dec(rng.Row1); - MustRebuildFormulas := true; - end; - if rng.Row2 >= rowIndex then begin - dec(rng.Row2); - MustRebuildFormulas := true; - end; - TsCellRangeExprNode(AExprNode).Range := rng; end; + if rng.Row2 >= rowIndex then begin + dec(rng.Row2); + MustRebuildFormulas := true; + end; + TsCellRangeExprNode(AExprNode).Range := rng; end; end; end; -procedure FixInsertedCol(AExprNode: TsExprNode; AData: Pointer; +procedure FixInsertedCol(AExprNode: TsExprNode; AData1, AData2: Pointer; var MustRebuildFormulas: Boolean); var colIndex: Cardinal; + changedSheet: TsBasicWorksheet; + referencedSheet, referencedSheet2: TsBasicWorksheet; rng: TsCellRange; begin - colIndex := PtrInt(AData); + colIndex := PtrInt(AData1); + changedSheet := TsBasicWorksheet(AData2); if AExprNode is TsCellExprNode then begin - if not TsCellExprNode(AExprNode).Has3dLink then - if TsCellExprNode(AExprNode).Col >= colIndex then begin - TsCellExprNode(AExprNode).Col := TsCellexprNode(AExprNode).Col + 1; - MustRebuildFormulas := true; - end; + if TsCellExprNode(AExprNode).Error <> errOK then + exit; + referencedSheet := TsCellExprNode(AExprNode).GetSheet; + if TsCellExprNode(AExprNode).Has3dLink and (referencedSheet <> changedSheet) then + exit; + if TsCellExprNode(AExprNode).Col >= colIndex then begin + TsCellExprNode(AExprNode).Col := TsCellExprNode(AExprNode).Col + 1; + MustRebuildFormulas := true; + end; end else if AExprNode is TsCellRangeExprNode then begin - if not TsCellRangeExprNode(AExprNode).Has3dLink then begin - rng := TsCellRangeExprNode(AExprNode).Range; - if rng.Col1 >= colIndex then inc(rng.Col1); - if rng.Col2 >= colIndex then inc(rng.Col2); - TsCellRangeExprNode(AExprNode).Range := rng; + if TsCellRangeExprNode(AExprNode).Error <> errOK then + exit; + referencedSheet := TsCellRangeExprNode(AExprNode).GetSheet(1); + referencedSheet2 := TsCellRangeExprNode(AExprNode).GetSheet(2); + if TsCellRangeExprNode(AExprNode).Has3dLink and + (referencedSheet <> changedSheet) and + (referencedSheet2 <> changedSheet) + then + exit; + rng := TsCellRangeExprNode(AExprNode).Range; + if rng.Col1 >= colIndex then begin + inc(rng.Col1); MustRebuildFormulas := true; end; + if rng.Col2 >= colIndex then begin + inc(rng.Col2); + MustRebuildFormulas := true; + end; + TsCellRangeExprNode(AExprNode).Range := rng; end; end; -procedure FixInsertedRow(AExprNode: TsExprNode; AData: Pointer; +procedure FixInsertedRow(AExprNode: TsExprNode; AData1, AData2: Pointer; var MustRebuildFormulas: Boolean); var rowIndex: Cardinal; + changedSheet: TsBasicWorksheet; + referencedSheet, referencedSheet2: TsBasicWorksheet; rng: TsCellRange; begin - rowIndex := PtrInt(AData); + rowIndex := PtrInt(AData1); + changedSheet := TsBasicWorksheet(AData2); if AExprNode is TsCellExprNode then begin - if not TsCellexprNode(AExprNode).Has3dLink then - if TsCellExprNode(AExprNode).Row >= rowIndex then begin - TsCellExprNode(AExprNode).Row := TsCellExprNode(AExprNode).Row + 1; - MustRebuildFormulas := true; - end; + if TsCellExprNode(AExprNode).Error <> errOK then + exit; + referencedSheet := TsCellExprNode(AExprNode).GetSheet; + if TsCellExprNode(AExprNode).Has3dLink and (referencedSheet <> changedSheet) then + exit; + if TsCellExprNode(AExprNode).Row >= rowIndex then begin + TsCellExprNode(AExprNode).Row := TsCellExprNode(AExprNode).Row + 1; + MustRebuildFormulas := true; + end; end else if AExprNode is TsCellRangeExprNode then begin - if not TsCellRangeExprNode(AExprNode).Has3dLink then begin - rng := TsCellRangeExprNode(AExprNode).Range; - if rng.Row1 >= rowIndex then inc(rng.Row1); - if rng.Row2 >= rowIndex then inc(rng.Row2); - TsCellRangeExprNode(AExprNode).Range := rng; + if TsCellRangeExprNode(AExprNode).Error <> errOK then + exit; + referencedSheet := TsCellRangeExprNode(AExprNode).GetSheet(1); + referencedSheet2 := TsCellRangeExprNode(AExprNode).GetSheet(2); + if TsCellRangeExprNode(AExprNode).Has3dLink and + (referencedSheet <> changedSheet) and + (referencedSheet2 <> changedSheet) + then + exit; + rng := TsCellRangeExprNode(AExprNode).Range; + if rng.Row1 >= rowIndex then begin + inc(rng.Row1); MustRebuildFormulas := true; end; + if rng.Row2 >= rowIndex then begin + inc(rng.Row2); + MustRebuildFormulas := true; + end; + TsCellRangeExprNode(AExprNode).Range := rng; end; end; @@ -1436,7 +1505,9 @@ begin Delete(ARow, ACol); // will release memory automatically end; -procedure TsFormulas.DeleteRowOrCol(AIndex: Cardinal; IsRow: Boolean); +(* +procedure TsFormulas.DeleteRowOrCol(AIndex: Cardinal; IsRow: Boolean; + InSheet: TsBasicWorksheet); var node, nextnode: TAvgLvlTreeNode; formula: PsFormula; @@ -1476,7 +1547,7 @@ begin formula^.Text := formula^.Parser.Expression; node := nextnode; end; -end; +end; *) procedure TsFormulas.DisposeData(var AData: Pointer); begin @@ -1501,12 +1572,38 @@ begin Result := PsFormula(FindByRowCol(ARow, ACol)); end; +{ In worksheet "InSheet" a row or column has been inserted or deleted at the + given index. "IsRow" tells whether it is a row or a column, and "IsDeleteing" + tells whether it has been deleted (true) or inserted (false). + This method must scan the formulas of the current worksheet whether there is + a reference to the modified sheet and must adapt the reference correspondingly +} +procedure TsFormulas.FixReferences(AIndex: Cardinal; IsRow, IsDeleting: Boolean; + InSheet: TsBasicWorksheet); +var + formula: PsFormula; + proc: TsExprNodeProc; +begin + if IsRow and IsDeleting then + proc := @FixDeletedRow + else if IsRow and not IsDeleting then + proc := @FixInsertedRow + else if not IsRow and IsDeleting then + proc := @FixDeletedCol + else + proc := @fixInsertedCol; + + for formula in self do + if formula^.Parser.IterateNodes(proc, Pointer(PtrInt(AIndex)), InSheet) then + formula^.Text := formula^.Parser.Expression; +end; + // Formula enumerators (use in "for ... in" syntax) function TsFormulas.GetEnumerator: TsFormulaEnumerator; begin Result := TsFormulaEnumerator.Create(self, 0, 0, $7FFFFFFF, $7FFFFFFF, false); end; - + (* procedure TsFormulas.InsertRowOrCol(AIndex: Cardinal; IsRow: Boolean); var node: TAvgLvlTreeNode; @@ -1529,7 +1626,7 @@ begin formula^.Text := formula^.Parser.Expression; node := FindSuccessor(node); end; -end; +end; *) function TsFormulas.NewData: Pointer; var diff --git a/components/fpspreadsheet/source/common/fpsexprparser.pas b/components/fpspreadsheet/source/common/fpsexprparser.pas index 609305b45..0e62b28bc 100644 --- a/components/fpspreadsheet/source/common/fpsexprparser.pas +++ b/components/fpspreadsheet/source/common/fpsexprparser.pas @@ -109,7 +109,7 @@ type { Proceudre executed when iterating through all nodes (Parser.IterateNodes). The procedure sets the parameter MustRebuildFormula to true if the text formula has to be rebuilt. } - TsExprNodeProc = procedure(ANode: TsExprNode; AData: Pointer; + TsExprNodeProc = procedure(ANode: TsExprNode; AData1, AData2: Pointer; var MustRebuildFormulas: Boolean); { TsExprNode } @@ -124,7 +124,7 @@ type function AsString: string; virtual; abstract; procedure Check; virtual; //abstract; function Has3DLink: Boolean; virtual; - procedure IterateNodes(AProc: TsExprNodeProc; AData: Pointer; + procedure IterateNodes(AProc: TsExprNodeProc; AData1, AData2: Pointer; var MustRebuildFormulas: Boolean); virtual; function NodeType: TsResultType; virtual; abstract; function NodeValue: TsExpressionResult; @@ -144,7 +144,7 @@ type constructor Create(AParser: TsExpressionParser; ALeft, ARight: TsExprNode); destructor Destroy; override; function Has3DLink: Boolean; override; - procedure IterateNodes(AProc: TsExprNodeProc; AData: Pointer; + procedure IterateNodes(AProc: TsExprNodeProc; AData1, AData2: Pointer; var MustRebuildFormulas: boolean); override; property Left: TsExprNode read FLeft; property Right: TsExprNode read FRight; @@ -567,7 +567,7 @@ type function AsString: String; override; procedure Check; override; function Has3DLink: Boolean; override; - procedure IterateNodes(AProc: TsExprNodeProc; AData: Pointer; + procedure IterateNodes(AProc: TsExprNodeProc; AData1, AData2: Pointer; var MustRebuildFormulas: Boolean); override; property ArgumentNodes: TsExprArgumentArray read FArgumentNodes; property ArgumentParams: TsExprParameterArray read FArgumentParams; @@ -612,7 +612,6 @@ type protected function GetCol: Cardinal; function GetRow: Cardinal; - function GetSheet: TsBasicWorksheet; procedure GetNodeValue(out AResult: TsExpressionResult); override; public constructor Create(AParser: TsExpressionParser; AWorksheet: TsBasicWorksheet; @@ -620,11 +619,12 @@ type function AsRPNItem(ANext: PRPNItem): PRPNItem; override; function AsString: string; override; procedure Check; override; + function GetSheet: TsBasicWorksheet; function GetSheetIndex: Integer; function GetSheetName: String; function GetWorkbook: TsBasicWorkbook; function Has3DLink: Boolean; override; - procedure IterateNodes(AProc: TsExprNodeProc; AData: Pointer; + procedure IterateNodes(AProc: TsExprNodeProc; AData1, AData2: Pointer; var MustRebuildFormulas: Boolean); override; function NodeType: TsResultType; override; procedure SetSheetIndex(AIndex: Integer); @@ -658,10 +658,11 @@ type function AsRPNItem(ANext: PRPNItem): PRPNItem; override; function AsString: String; override; procedure Check; override; + function GetSheet(AIndex: TsCellRangeIndex): TsBasicWorksheet; function GetSheetIndex(AIndex: TsCellRangeIndex): Integer; function GetWorkbook: TsBasicWorkbook; function Has3DLink: Boolean; override; - procedure IterateNodes(AProc: TsExprNodeProc; AData: Pointer; + procedure IterateNodes(AProc: TsExprNodeProc; AData1, AData2: Pointer; var MustRebuildFormulas: Boolean); override; function NodeType: TsResultType; override; procedure SetSheetIndex(AIndex: TsCellRangeIndex; AValue: Integer); @@ -788,7 +789,7 @@ type function Evaluate: TsExpressionResult; procedure EvaluateExpression(out AResult: TsExpressionResult); function Has3DLinks: Boolean; - function IterateNodes(AProc: TsExprNodeProc; AData: Pointer): boolean; + function IterateNodes(AProc: TsExprNodeProc; AData1, AData2: Pointer): boolean; procedure PrepareCopyMode(ASourceCell, ADestCell: PCell); function ResultType: TsResultType; @@ -1988,10 +1989,10 @@ begin end; function TsExpressionParser.IterateNodes(AProc: TsExprNodeProc; - AData: Pointer): Boolean; + AData1, AData2: Pointer): Boolean; begin Result := false; - FExprNode.IterateNodes(AProc, AData, Result); + FExprNode.IterateNodes(AProc, AData1, AData2, Result); end; procedure TsExpressionParser.SetDialect(const AValue: TsFormulaDialect); @@ -2776,10 +2777,11 @@ begin Result := false; end; -procedure TsExprNode.IterateNodes(AProc: TsExprNodeProc; AData: Pointer; +procedure TsExprNode.IterateNodes(AProc: TsExprNodeProc; AData1, AData2: Pointer; var MustRebuildFormulas: Boolean); begin - Unused(AProc, AData, MustRebuildFormulas); + Unused(AProc, AData1, AData2); + Unused(MustRebuildFormulas); // to be overridden by descendant classes end; @@ -2834,12 +2836,12 @@ begin end; procedure TsBinaryOperationExprNode.IterateNodes(AProc: TsExprNodeProc; - AData: Pointer; var MustRebuildFormulas: Boolean); + AData1, AData2: Pointer; var MustRebuildFormulas: Boolean); var rebuildLeft, rebuildRight: Boolean; begin - FLeft.IterateNodes(AProc, AData, rebuildLeft); - FRight.IterateNodes(AProc, AData, rebuildRight); + FLeft.IterateNodes(AProc, AData1, AData2, rebuildLeft); + FRight.IterateNodes(AProc, AData1, AData2, rebuildRight); MustRebuildFormulas := MustRebuildFormulas or rebuildLeft or rebuildRight; end; @@ -3795,12 +3797,12 @@ begin end; procedure TsFunctionExprNode.IterateNodes(AProc: TsExprNodeProc; - AData: Pointer; var MustRebuildFormulas: Boolean); + AData1, AData2: Pointer; var MustRebuildFormulas: Boolean); var i: Integer; begin for i:=0 to High(FArgumentParams) do - FArgumentNodes[i].IterateNodes(AProc, AData, MustRebuildFormulas); + FArgumentNodes[i].IterateNodes(AProc, AData1, AData2, MustRebuildFormulas); end; @@ -3948,11 +3950,6 @@ begin if FError <> errOK then begin AResult.ResultType := rtError; AResult.ResError := FError; - { - AResult.ResRow := GetRow; - AResult.ResCol := GetCol; - AResult.Worksheet := GetSheet; - } exit; end; @@ -4013,31 +4010,31 @@ end; function TsCellExprNode.GetSheet: TsBasicWorksheet; begin - if FSheetIndex = -1 then - Result := FWorksheet - else begin + if FHas3dLink then begin Result := (GetWorkbook as TsWorkbook).GetWorksheetByIndex(FSheetIndex); if Result = nil then FError := errIllegalREF; - end; + end else + Result := FWorksheet; end; function TsCellExprNode.GetSheetIndex: Integer; var book: TsWorkbook; begin - book := GetWorkbook as TsWorkbook; - if FSheetIndex = -1 then + if FHas3dLink then + Result := FSheetIndex + else begin + book := GetWorkbook as TsWorkbook; Result := book.GetWorksheetIndex(FWorksheet) - else - Result := FSheetIndex; + end; end; function TsCellExprNode.GetSheetName: String; begin - if FSheetIndex = -1 then - Result := FWorksheet.Name + if FHas3dLink then + Result := TsWorkbook(GetWorkbook).GetWorksheetByIndex(FSheetIndex).Name else - Result := TsWorkbook(GetWorkbook).GetWorksheetByIndex(FSheetIndex).Name; + Result := FWorksheet.Name; end; function TsCellExprNode.GetWorkbook: TsBasicWorkbook; @@ -4056,9 +4053,9 @@ begin end; procedure TsCellExprNode.IterateNodes(AProc: TsExprNodeProc; - AData: Pointer; var MustRebuildFormulas: Boolean); + AData1, AData2: Pointer; var MustRebuildFormulas: Boolean); begin - AProc(self, AData, MustRebuildFormulas); + AProc(self, AData1, AData2, MustRebuildFormulas); end; procedure TsCellExprNode.SetSheetIndex(AIndex: Integer); @@ -4301,6 +4298,14 @@ begin Result := (FWorksheet as TsWorksheet).Workbook; end; +function TsCellRangeExprNode.GetSheet(AIndex: TsCellRangeIndex): TsBasicWorksheet; +begin + if FError <> errOK then + Result := nil + else + Result := TsWorkbook(GetWorkbook).GetWorksheetByIndex(GetSheetIndex(AIndex)); +end; + function TsCellRangeExprNode.GetSheetIndex(AIndex: TsCellRangeIndex): Integer; begin Result := FSheetIndex[AIndex]; @@ -4312,9 +4317,9 @@ begin end; procedure TsCellRangeExprNode.IterateNodes(AProc: TsExprNodeProc; - AData: Pointer; var MustRebuildFormulas: Boolean); + AData1, AData2: Pointer; var MustRebuildFormulas: Boolean); begin - AProc(self, AData, MustRebuildFormulas); + AProc(self, AData1, AData2, MustRebuildFormulas); end; function TsCellRangeExprNode.NodeType: TsResultType; diff --git a/components/fpspreadsheet/source/common/fpspreadsheet.pas b/components/fpspreadsheet/source/common/fpspreadsheet.pas index be1f6292a..9c3ddea3e 100644 --- a/components/fpspreadsheet/source/common/fpspreadsheet.pas +++ b/components/fpspreadsheet/source/common/fpspreadsheet.pas @@ -7386,6 +7386,7 @@ var i: Integer; r: Cardinal; formula: PsFormula; + sheet: TsWorksheet; begin // Fix merged cells FMergedCells.DeleteRowOrCol(AIndex, IsRow); @@ -7396,8 +7397,14 @@ begin // Fix hyperlinks FHyperlinks.DeleteRowOrCol(AIndex, IsRow); - // Fix formulas + // Fix formulas: + // 1) Fix Row/Col index of in-sheet formulas FFormulas.DeleteRowOrCol(AIndex, IsRow); + // 2) Fix formula references to this sheet + for i := 0 to FWorkbook.GetWorksheetcount-1 do begin + sheet := FWorkbook.GetWorksheetByIndex(i); + sheet.Formulas.FixReferences(AIndex, IsRow, true, self); + end; // Delete cells FCells.DeleteRowOrCol(AIndex, IsRow); @@ -7483,6 +7490,7 @@ var col: PCol; i: Integer; rng: PsCellRange; + sheet: TsWorksheet; begin // Update row indexes of cell comments FComments.InsertRowOrCol(AIndex, IsRow); @@ -7490,8 +7498,14 @@ begin // Update row indexes of cell hyperlinks FHyperlinks.InsertRowOrCol(AIndex, IsRow); - // Update row indexes of cell formulas + // Fix formulas: + // 1) Update Row/Col index of in-sheet formulas FFormulas.InsertRowOrCol(AIndex, IsRow); + // 2) Fix formula references to this sheet + for i := 0 to FWorkbook.GetWorksheetcount-1 do begin + sheet := FWorkbook.GetWorksheetByIndex(i); + sheet.Formulas.FixReferences(AIndex, IsRow, false, self); + end; // Update cell indexes of cell records FCells.InsertRowOrCol(AIndex, IsRow); @@ -9600,7 +9614,7 @@ end; { AData points to the deleted worksheet } -procedure FixWorksheetDeletedCallback(ANode: TsExprNode; AData: Pointer; +procedure FixWorksheetDeletedCallback(ANode: TsExprNode; AData1, AData2: Pointer; var MustRebuildFormulas: Boolean); var deletedindex: Integer; @@ -9609,10 +9623,12 @@ var rngNode: TsCellRangeExprNode; index, index1, index2: Integer; begin + Unused(AData2); + if ANode is TsCellExprNode then begin cellNode := TsCellExprNode(ANode); - deletedSheet := TsWorksheet(AData); + deletedSheet := TsWorksheet(AData1); deletedindex := TsWorkbook(cellNode.GetWorkbook).GetWorksheetIndex(deletedSheet); index := cellNode.GetSheetIndex; if deletedindex < index then begin @@ -9627,7 +9643,7 @@ begin if ANode is TsCellRangeExprNode then begin rngNode := TsCellRangeExprNode(ANode); - deletedSheet := TsWorksheet(AData); + deletedSheet := TsWorksheet(AData1); deletedIndex := TsWorkbook(rngNode.GetWorkbook).GetWorksheetIndex(deletedSheet); index1 := rngNode.GetSheetIndex(1); index2 := rngNode.GetSheetIndex(2); @@ -9663,7 +9679,7 @@ begin fcWorksheetRenamed: Result := true; // Nothing to do, no sheet names in formula nodes fcWorksheetDeleted: - Result := AFormula^.Parser.IterateNodes(FixWorksheetDeletedCallback, AData); + Result := AFormula^.Parser.IterateNodes(FixWorksheetDeletedCallback, AData, nil); end; end; diff --git a/components/fpspreadsheet/source/common/xlsbiff8.pas b/components/fpspreadsheet/source/common/xlsbiff8.pas index b05175da8..3769b52d9 100644 --- a/components/fpspreadsheet/source/common/xlsbiff8.pas +++ b/components/fpspreadsheet/source/common/xlsbiff8.pas @@ -2559,14 +2559,15 @@ begin AStream.WriteWord(0); end; -procedure DoCollectSheetsWith3dRefs(ANode: TsExprNode; AData: Pointer; +procedure DoCollectSheetsWith3dRefs(ANode: TsExprNode; AData1, AData2: Pointer; var MustRebuildFormulas: Boolean); var sheetlist: TsBIFF8ExternSheetList; sheetIdx, sheetIdx1, sheetIdx2: Integer; begin - Unused(MustRebuildFormulas); - sheetlist := TsBIFF8ExternSheetList(AData); + Unused(AData2, MustRebuildFormulas); + + sheetlist := TsBIFF8ExternSheetList(AData1); if (ANode is TsCellExprNode) and TsCellExprNode(ANode).Has3DLink then begin sheetIdx := TsCellExprNode(ANode).GetSheetIndex; @@ -2593,7 +2594,7 @@ procedure TsSpreadBIFF8Writer.CollectExternData; formula: PsFormula; begin for formula in ASheet.Formulas do - formula^.Parser.IterateNodes(@DoCollectSheetsWith3dRefs, FBiff8ExternSheets); + formula^.Parser.IterateNodes(@DoCollectSheetsWith3dRefs, FBiff8ExternSheets, nil); end; var diff --git a/components/fpspreadsheet/source/common/xlscommon.pas b/components/fpspreadsheet/source/common/xlscommon.pas index 8b2d091e7..4c910f323 100644 --- a/components/fpspreadsheet/source/common/xlscommon.pas +++ b/components/fpspreadsheet/source/common/xlscommon.pas @@ -3463,7 +3463,7 @@ begin end; end; -procedure DoCollectSheetsWith3dRefs(ANode: TsExprNode; AData: Pointer; +procedure DoCollectSheetsWith3dRefs(ANode: TsExprNode; AData1, AData2: Pointer; var MustRebuildFormulas: Boolean); var sheetlist: TsBIFFExternSheetList; @@ -3471,8 +3471,9 @@ var workbook: TsWorkbook; sheetName: String; begin - Unused(MustRebuildFormulas); - sheetlist := TsBIFFExternSheetList(AData); + Unused(MustRebuildFormulas, AData2); + + sheetlist := TsBIFFExternSheetList(AData1); if (ANode is TsCellExprNode) and TsCellExprNode(ANode).Has3DLink then begin if (ANode as TsCellExprNode).Error <> errOK then @@ -3505,7 +3506,7 @@ function TsSpreadBIFFWriter.CollectExternData(AWorksheet: TsBasicWorksheet = nil formula: PsFormula; begin for formula in ASheet.Formulas do - formula^.Parser.IterateNodes(@DoCollectSheetsWith3dRefs, ASheetList); + formula^.Parser.IterateNodes(@DoCollectSheetsWith3dRefs, ASheetList, nil); end; var