fpspreadsheet: MoveCell completely overwrites destination cell. MoveCell with sourcecell=nil erases destination cell. Related unit test cases.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@8270 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2022-04-28 10:08:39 +00:00
parent 16d631d0cf
commit 870e46847a
2 changed files with 154 additions and 39 deletions

View File

@@ -1614,6 +1614,7 @@ begin
// Short-cut for source and destination worksheets // Short-cut for source and destination worksheets
srcSheet := TsWorksheet(AFromcell^.Worksheet); srcSheet := TsWorksheet(AFromcell^.Worksheet);
destSheet := TsWorksheet(AToCell^.Worksheet); destSheet := TsWorksheet(AToCell^.Worksheet);
if destSheet = nil then destSheet := srcSheet;
// Remember the row and column indexes of the destination cell. // Remember the row and column indexes of the destination cell.
toRow := AToCell^.Row; toRow := AToCell^.Row;
@@ -2279,20 +2280,28 @@ var
i: Integer; i: Integer;
formula: PsFormula; formula: PsFormula;
formulaStr: String; formulaStr: String;
srcHasFormula: Boolean;
begin begin
if ACell = nil then destCell := FindCell(AToRow, AToCol);
exit;
// Avoid misplaced notifications during the copy operations when things could // Moving an empty cell should clear the destination cell.
// not yet be in place. if ACell = nil then
begin
DeleteCell(destCell);
exit;
end;
// Avoid misplaced notifications during the copy operations when things are
// not in place, yet.
FWorkbook.DisableNotifications; FWorkbook.DisableNotifications;
// Store old location // Store old location
fromRow := ACell^.Row; fromRow := ACell^.Row;
fromCol := ACell^.Col; fromCol := ACell^.Col;
// Copy cell to new location // Clear destination cell
EraseCell(destCell);
// Copy cell to new location (taking care of comments, hyperlinks etc)
// Note: In Excel the formula in a moved cell still points to the initial // Note: In Excel the formula in a moved cell still points to the initial
// location. This is different from copying a formula. // location. This is different from copying a formula.
// --> We must prevent CopyCell from adjusting the formula // --> We must prevent CopyCell from adjusting the formula
@@ -2304,18 +2313,17 @@ begin
if formulaStr <> '' then if formulaStr <> '' then
WriteFormula(AToRow, AToCol, formulaStr); WriteFormula(AToRow, AToCol, formulaStr);
// Fix formula references to this cell // Fix formula references to the source cell (ACell)
for i := 0 to FWorkbook.GetWorksheetcount-1 do begin for i := 0 to FWorkbook.GetWorksheetcount-1 do begin
sheet := FWorkbook.GetWorksheetByIndex(i); sheet := FWorkbook.GetWorksheetByIndex(i);
sheet.Formulas.FixReferenceToMovedCell(ACell, AToRow, AToCol, self); sheet.Formulas.FixReferenceToMovedCell(ACell, AToRow, AToCol, self);
end; end;
// Mark destination cell to contain a formula (if applicable). // Mark destination cell to contain a formula (if applicable).
destCell := FindCell(AToRow, AToCol);
formula := Formulas.FindFormula(destCell); formula := Formulas.FindFormula(destCell);
UseFormulaInCell(destCell, formula); UseFormulaInCell(destCell, formula);
// Delete cell at old location // Delete source cell at old location
DeleteCell(ACell); DeleteCell(ACell);
FWorkbook.EnableNotifications; FWorkbook.EnableNotifications;

View File

@@ -22,7 +22,6 @@ type
protected protected
procedure Test_MoveCell(ATestKind: Integer); procedure Test_MoveCell(ATestKind: Integer);
procedure Test_MoveCell_CircRef(ATestKind: Integer);
published published
procedure Test_MoveCell_Value; procedure Test_MoveCell_Value;
@@ -34,8 +33,10 @@ type
procedure Test_MoveCell_FormulaRef_REL; procedure Test_MoveCell_FormulaRef_REL;
procedure Test_MoveCell_FormulaRef_ABS; procedure Test_MoveCell_FormulaRef_ABS;
procedure Test_MoveCell_FormulaToValue; procedure Test_MoveCell_CircRef;
procedure Test_MoveCell_ValueToFormula; procedure Test_MoveCell_OverwriteFormula;
procedure Test_MoveCell_EmptyToValue;
procedure Test_MoveCell_EmptyToFormula;
end; end;
implementation implementation
@@ -191,20 +192,18 @@ end;
{==============================================================================} {==============================================================================}
{ In the following test an occupied cell is moved to a different location { In the following test an occupied cell with a formula is moved to a location
such that a circular reference is created. referenced by the formula.
ATestKind = 1: value cell is moved to formula cell which points to value cell This must result in a circular reference error. }
2: formula cell is moved to cell to which it points. } procedure TSpreadMoveTests.Test_MoveCell_CircRef;
procedure TSpreadMoveTests.Test_MoveCell_CircRef(ATestKind: Integer);
const const
VALUE_CELL_ROW = 0; VALUE_CELL_ROW = 0; // A1
VALUE_CELL_COL = 0; VALUE_CELL_COL = 0;
FORMULA_CELL_ROW = 11; FORMULA_CELL_ROW = 11; // F10
FORMULA_CELL_COL = 6; FORMULA_CELL_COL = 6;
var var
worksheet: TsWorksheet; worksheet: TsWorksheet;
workbook: TsWorkbook; workbook: TsWorkbook;
value_cell: PCell = nil;
formula_cell: PCell = nil; formula_cell: PCell = nil;
dest_cell: PCell = nil; dest_cell: PCell = nil;
begin begin
@@ -215,26 +214,18 @@ begin
worksheet := workBook.AddWorksheet(MoveTestSheet); worksheet := workBook.AddWorksheet(MoveTestSheet);
// Prepare the worksheet in which a cell is moved. // Prepare the worksheet in which a cell is moved.
// The value cell is A1, the formula cell is B2 and it points to A1 // The value cell is A1, the formula cell is F10 and it points to A1
value_cell := worksheet.WriteText(VALUE_CELL_ROW, VALUE_CELL_COL, 'abc'); // A1 worksheet.WriteText(VALUE_CELL_ROW, VALUE_CELL_COL, 'abc'); // A1
formula_cell := worksheet.WriteFormula(FORMULA_CELL_ROW, FORMULA_CELL_COL, 'A1'); formula_cell := worksheet.WriteFormula(FORMULA_CELL_ROW, FORMULA_CELL_COL, 'A1');
// Move the cell // Move the formula cell to overwrite the value cell
try try
case ATestKind of
1: begin
worksheet.MoveCell(value_cell, FORMULA_CELL_ROW, FORMULA_CELL_COL);
dest_cell := worksheet.FindCell(FORMULA_CELL_ROW, FORMULA_CELL_COL);
end;
2: begin
worksheet.MoveCell(formula_cell, VALUE_CELL_ROW, VALUE_CELL_COL); worksheet.MoveCell(formula_cell, VALUE_CELL_ROW, VALUE_CELL_COL);
dest_cell := worksheet.FindCell(VALUE_CELL_ROW, VALUE_CELL_COL); dest_cell := worksheet.FindCell(VALUE_CELL_ROW, VALUE_CELL_COL);
end;
end;
except except
end; end;
// In each case, the destination cell should contain a #REF! error // The destination cell should contain a #REF! error
CheckEquals(true, dest_cell^.ErrorValue = errIllegalRef, 'Circular reference not detected.'); CheckEquals(true, dest_cell^.ErrorValue = errIllegalRef, 'Circular reference not detected.');
finally finally
@@ -242,14 +233,130 @@ begin
end; end;
end; end;
procedure TSpreadMoveTests.Test_MoveCell_FormulaToValue; { In the following test an occupied cell with a value formula is moved to
a location with a formula cell pointing to the moved value cell.
This operation must delete the formula after moving. }
procedure TSpreadMoveTests.Test_MoveCell_OverwriteFormula;
const
VALUE_CELL_ROW = 0; // A1
VALUE_CELL_COL = 0;
FORMULA_CELL_ROW = 11; // F10
FORMULA_CELL_COL = 6;
var
worksheet: TsWorksheet;
workbook: TsWorkbook;
value_cell: PCell = nil;
dest_cell: PCell = nil;
begin begin
Test_MoveCell_CircRef(1); workbook := TsWorkbook.Create;
try
workbook.Options := workbook.Options + [boAutoCalc];
worksheet := workBook.AddWorksheet(MoveTestSheet);
// Prepare the worksheet in which a cell is moved.
// The value cell is A1, the formula cell is F10 and it points to A1
value_cell := worksheet.WriteText(VALUE_CELL_ROW, VALUE_CELL_COL, 'abc'); // A1
worksheet.WriteFormula(FORMULA_CELL_ROW, FORMULA_CELL_COL, 'A1');
// Move the value cell to overwrite the formula cell
try
worksheet.MoveCell(value_cell, FORMULA_CELL_ROW, FORMULA_CELL_COL);
dest_cell := worksheet.FindCell(FORMULA_CELL_ROW, FORMULA_CELL_COL);
except
end; end;
procedure TSpreadMoveTests.Test_MoveCell_ValueToFormula; // The destination cell should not contain a formula any more.
CheckEquals(false, HasFormula(dest_cell), 'Formula has not been removed.');
// Check value at destination after moving
CheckEquals('abc', worksheet.ReadAsText(dest_cell), 'Moved value mismatch.');
// Check value at source after moving
CheckEquals('', worksheet.ReadAsText(value_cell), 'Source value mismatch after moving.');
finally
workbook.Free;
end;
end;
{ In the following test an empty cell is moved to a location with a value cell.
This operation must delete the value in the destination cell after moving. }
procedure TSpreadMoveTests.Test_MoveCell_EmptyToValue;
const
SOURCE_CELL_ROW = 0; // A1
SOURCE_CELL_COL = 0;
DEST_CELL_ROW = 2; // C3
DEST_CELL_COL = 2;
var
worksheet: TsWorksheet;
workbook: TsWorkbook;
src_cell: PCell = nil;
dest_cell: PCell = nil;
begin begin
Test_MoveCell_CircRef(2); workbook := TsWorkbook.Create;
try
workbook.Options := workbook.Options + [boAutoCalc];
worksheet := workBook.AddWorksheet(MoveTestSheet);
// Prepare the worksheet in which an empty cell is moved.
src_cell := nil; // A1
dest_cell := worksheet.WriteText(DEST_CELL_ROW, DEST_CELL_COL, 'abc'); // C3
// Move the source cell to overwrite the value cell
try
worksheet.MoveCell(src_cell, DEST_CELL_ROW, DEST_CELL_COL);
dest_cell := worksheet.FindCell(DEST_CELL_ROW, DEST_CELL_COL);
except
end;
// The destination cell should be empty.
CheckEquals(true, dest_cell = nil, 'Destination cell nas not been deleted.');
finally
workbook.Free;
end;
end;
{ In the following test an empty cell is moved to a location with a formula cell.
This operation must delete the destination cell after moving. In particular,
there must not be a formula any more. }
procedure TSpreadMoveTests.Test_MoveCell_EmptyToFormula;
const
SOURCE_CELL_ROW = 0; // A1
SOURCE_CELL_COL = 0;
DEST_CELL_ROW = 2; // C3
DEST_CELL_COL = 2;
var
worksheet: TsWorksheet;
workbook: TsWorkbook;
src_cell: PCell = nil;
dest_cell: PCell = nil;
begin
workbook := TsWorkbook.Create;
try
workbook.Options := workbook.Options + [boAutoCalc];
worksheet := workBook.AddWorksheet(MoveTestSheet);
// Prepare the worksheet in which a cell is moved.
// The value cell is A1, the formula cell is B2 and it points to A1
src_cell := nil; // A1
dest_cell := worksheet.WriteFormula(DEST_CELL_ROW, DEST_CELL_COL, 'PI()'); // C3
// Move the empty source cell to overwrite the formula cell
try
worksheet.MoveCell(src_cell, DEST_CELL_ROW, DEST_CELL_COL);
dest_cell := worksheet.FindCell(DEST_CELL_ROW, DEST_CELL_COL);
except
end;
// The destination cell should be empty.
CheckEquals(false, HasFormula(dest_cell), 'Destination cell still contains a formula.');
CheckEquals(true, dest_cell = nil, 'Destination cell has not been deleted.');
finally
workbook.Free;
end;
end; end;
initialization initialization