From 02bd21629c408b262f0db676b0710d9a4f0b362d Mon Sep 17 00:00:00 2001 From: wp_xxyyzz Date: Fri, 22 Apr 2022 20:12:08 +0000 Subject: [PATCH] fpspreadsheet: Add method TsWorksheet.MoveCell (forum https://forum.lazarus.freepascal.org/index.php/topic,59137). git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@8260 8e941d3f-bd1b-0410-a28a-d453659cc2b4 --- .../source/common/fpsclasses.pas | 40 +++++++++++++ .../source/common/fpspreadsheet.pas | 58 +++++++++++++++---- 2 files changed, 86 insertions(+), 12 deletions(-) diff --git a/components/fpspreadsheet/source/common/fpsclasses.pas b/components/fpspreadsheet/source/common/fpsclasses.pas index 945ada323..0eeb4d7a3 100644 --- a/components/fpspreadsheet/source/common/fpsclasses.pas +++ b/components/fpspreadsheet/source/common/fpsclasses.pas @@ -197,6 +197,8 @@ type function FindFormula(ARow, ACol: Cardinal): PsFormula; overload; procedure FixReferences(AIndex: Cardinal; IsRow, IsDeleting: Boolean; InSheet: TsBasicWorksheet); + procedure FixReferenceToMovedCell(ACell: PCell; AToRow, AToCol: Cardinal; + ASheet: TsBasicWorksheet); // enumerators function GetEnumerator: TsFormulaEnumerator; end; @@ -474,6 +476,26 @@ begin end; end; +procedure FixFormulaToMovedCell(AExprNode: TsExprNode; AData1, AData2: Pointer; + var MustRebuildFormulas: Boolean); +var + oldCell, newCell: PCell; +begin + oldCell := PCell(AData1); + newCell := PCell(AData2); + if AExprNode is TsCellExprNode then + begin + if (oldCell^.Worksheet = TsCellExprNode(AExprNode).GetSheet) and + (oldCell^.Row = TsCellExprNode(AExprNode).Row) and + (oldCell^.Col = TsCellExprNode(AExprNode).Col) then + begin + TsCellExprNode(AExprNode).Row := newCell^.Row; + TsCellExprNode(AExprNode).Col := newCell^.Col; + MustRebuildFormulas := true; + end; + end; +end; + {==============================================================================} { TsRowColEnumerator } @@ -1571,6 +1593,24 @@ begin formula^.Text := formula^.Parser.Expression[fdExcelA1]; end; +{ A cell in the specified sheet has been moved from its old location at AFromRow/ + AFromCol to a new location at AToRow/AToCol. If the cell is referenced by + a formula the formula must be adjusted such it points to the new location. + However, this occurs only when the reference is a single cell, not a cell range.} +procedure TsFormulas.FixReferenceToMovedCell( + ACell: PCell; AToRow, AToCol: Cardinal; ASheet: TsBasicWorksheet); +var + formula: PsFormula; + newCell: TCell; +begin + newCell := ACell^; + newCell.Row := AToRow; + newCell.Col := AToCol; + for formula in self do + if formula^.Parser.IterateNodes(@FixFormulaToMovedCell, ACell, @newCell) then + formula^.Text := formula^.Parser.Expression[fdExcelA1]; +end; + // Formula enumerators (use in "for ... in" syntax) function TsFormulas.GetEnumerator: TsFormulaEnumerator; begin diff --git a/components/fpspreadsheet/source/common/fpspreadsheet.pas b/components/fpspreadsheet/source/common/fpspreadsheet.pas index eabf1d896..5812b434f 100644 --- a/components/fpspreadsheet/source/common/fpspreadsheet.pas +++ b/components/fpspreadsheet/source/common/fpspreadsheet.pas @@ -458,6 +458,8 @@ type function GetCell(AddressStr: String): PCell; overload; function GetCellCount: Cardinal; + procedure MoveCell(ACell: PCell; AToRow, AToCol: Cardinal); + function FindNextCellInCol(ARow, ACol: Cardinal): PCell; function FindNextCellInRow(ARow, ACol: Cardinal): PCell; function FindPrevCellInCol(ARow, ACol: Cardinal): PCell; @@ -2257,6 +2259,50 @@ begin Result := FCells.Count; end; +{@@ ---------------------------------------------------------------------------- + Moves a cell to a different location. + + All additional data (comments, format, hyperlinks, formulas) are moved to the + new location. If the old location was referenced by a formula then the formula + is adjusted so that it points to the new location, however, only if the + formula references a single cell, not a cell range. + + @param ACell Cell to be moved + @param AToRow Row index of the new location + @param AToCol Column index of the new location +-------------------------------------------------------------------------------} +procedure TsWorksheet.MoveCell(ACell: PCell; AToRow, AToCol: Cardinal); +var + fromRow, fromCol: Cardinal; + sheet: TsWorksheet; + i: Integer; +begin + if ACell = nil then + exit; + + // Store old location + fromRow := ACell^.Row; + fromCol := ACell^.Col; + + // Copy cell to new location + CopyCell(fromRow, fromCol, AToRow, AToCol); + + // Fix formula references to this cell + for i := 0 to FWorkbook.GetWorksheetcount-1 do begin + sheet := FWorkbook.GetWorksheetByIndex(i); + sheet.Formulas.FixReferenceToMovedCell(ACell, AToRow, AToCol, self); + end; + + // Delete cell at old location + DeleteCell(ACell); + + // Notify visual controls of changes + ChangedCell(AToRow, AToCol); + + // Notify visual controls of possibly changed row heights. + ChangedFont(AToRow, AToCol); +end; + {@@ ---------------------------------------------------------------------------- Determines the number of decimals displayed for the number in the cell @@ -5585,12 +5631,7 @@ var i: Integer; rng: PsCellRange; sheet: TsWorksheet; - wasAutoCalculating: Boolean; begin - // Turn off auto-calculation of formulas - wasAutoCalculating := (boAutoCalc in Workbook.Options); - //Workbook.Options := Workbook.Options - [boAutoCalc]; - // Update row indexes of cell comments FComments.InsertRowOrCol(AIndex, IsRow); @@ -5685,13 +5726,6 @@ begin ChangedCell(0, AIndex); end; - - // Calculate formulas - if wasAutoCalculating then - begin - //Workbook.Options := Workbook.Options + [boAutoCalc]; - //CalcFormulas; - end; end; {@@ ----------------------------------------------------------------------------