fpspreadsheet: Some bug fixes related with inserting/deleting rows through merged cells/shared formula blocks. Still buggy...

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3578 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2014-09-18 16:01:45 +00:00
parent 4f941218df
commit abb2847886

View File

@ -1956,7 +1956,6 @@ procedure TsWorksheet.DeleteColCallback(data, arg: Pointer);
var var
cell: PCell; cell: PCell;
col: PtrInt; col: PtrInt;
fe: TsFormulaElement;
formula: TsRPNFormula; formula: TsRPNFormula;
i: Integer; i: Integer;
begin begin
@ -1977,26 +1976,25 @@ begin
// (2) update cell addresses affected by the deletion of the column // (2) update cell addresses affected by the deletion of the column
for i:=0 to High(formula) do for i:=0 to High(formula) do
begin begin
fe := formula[i]; // "fe" means "formula element" if (formula[i].ElementKind in [fekCell, fekCellRef, fekCellRange]) then
if (fe.ElementKind in [fekCell, fekCellRef, fekCellRange]) then
begin begin
if fe.Col = col then if formula[i].Col = col then
begin begin
fe.ElementKind := fekErr; formula[i].ElementKind := fekErr;
fe.IntValue := ord(errIllegalRef); formula[i].IntValue := ord(errIllegalRef);
end else end else
if fe.Col > col then if formula[i].Col > col then
dec(fe.Col); dec(formula[i].Col);
if (fe.ElementKind = fekCellRange) then if (formula[i].ElementKind = fekCellRange) then
begin begin
if (fe.Col2 = col) then if (formula[i].Col2 = col) then
begin begin
fe.ElementKind := fekErr; formula[i].ElementKind := fekErr;
fe.IntValue := ord(errIllegalRef); formula[i].IntValue := ord(errIllegalRef);
end end
else else
if (fe.Col2 > col) then if (formula[i].Col2 > col) then
dec(fe.Col2); dec(formula[i].Col2);
end; end;
end; end;
end; end;
@ -2011,7 +2009,6 @@ procedure TsWorksheet.DeleteRowCallback(data, arg: Pointer);
var var
cell: PCell; cell: PCell;
row: PtrInt; row: PtrInt;
fe: TsFormulaElement;
formula: TsRPNFormula; formula: TsRPNFormula;
i: Integer; i: Integer;
begin begin
@ -2032,26 +2029,25 @@ begin
// (2) update cell addresses affected by the deletion of the column // (2) update cell addresses affected by the deletion of the column
for i:=0 to High(formula) do for i:=0 to High(formula) do
begin begin
fe := formula[i]; // "fe" means "formula element" if (formula[i].ElementKind in [fekCell, fekCellRef, fekCellRange]) then
if (fe.ElementKind in [fekCell, fekCellRef, fekCellRange]) then
begin begin
if fe.Row = row then if formula[i].Row = row then
begin begin
fe.ElementKind := fekErr; formula[i].ElementKind := fekErr;
fe.IntValue := ord(errIllegalRef); formula[i].IntValue := ord(errIllegalRef);
end else end else
if fe.Row > row then if formula[i].Row > row then
dec(fe.Row); dec(formula[i].Row);
if (fe.ElementKind = fekCellRange) then if (formula[i].ElementKind = fekCellRange) then
begin begin
if (fe.Row2 = row) then if (formula[i].Row2 = row) then
begin begin
fe.ElementKind := fekErr; formula[i].ElementKind := fekErr;
fe.IntValue := ord(errIllegalRef); formula[i].IntValue := ord(errIllegalRef);
end end
else else
if (fe.Row2 > row) then if (formula[i].Row2 > row) then
dec(fe.Row2); dec(formula[i].Row2);
end; end;
end; end;
end; end;
@ -5022,7 +5018,6 @@ procedure TsWorksheet.InsertColCallback(data, arg: Pointer);
var var
cell: PCell; cell: PCell;
col: PtrInt; col: PtrInt;
fe: TsFormulaElement;
formula: TsRPNFormula; formula: TsRPNFormula;
i: Integer; i: Integer;
begin begin
@ -5038,33 +5033,23 @@ begin
// Update formulas // Update formulas
if HasFormula(cell) then if HasFormula(cell) then
begin begin
{ // (1) create an rpn formula
if cell^.SharedFormulaBase <> cell then formula := BuildRPNFormula(cell);
// (2) update cell addresses affected by the insertion of a column
for i:=0 to Length(formula)-1 do
begin begin
newCell := GetCell(cell^.Row, col); case formula[i].ElementKind of
newCell^.SharedFormulaBase := cell^.SharedFormulaBasse; fekCell, fekCellRef:
end else if formula[i].Col >= col then inc(formula[i].Col);
} fekCellRange:
begin begin
// (1) create an rpn formula if formula[i].Col >= col then inc(formula[i].Col);
formula := BuildRPNFormula(cell); if formula[i].Col2 >= col then inc(formula[i].Col2);
// (2) update cell addresses affected by the insertion of a column end;
for i:=0 to Length(formula)-1 do
begin
fe := Formula[i]; // "fe" means "formula element"
case fe.ElementKind of
fekCell, fekCellRef:
if fe.Col >= col then inc(fe.Col);
fekCellRange:
begin
if fe.Col >= col then inc(fe.Col);
if fe.Col2 >= col then inc(fe.Col2);
end;
end;
end; end;
// (3) convert rpn formula back to string formula
cell^.FormulaValue := ConvertRPNFormulaToStringFormula(formula);
end; end;
// (3) convert rpn formula back to string formula
cell^.FormulaValue := ConvertRPNFormulaToStringFormula(formula);
end; end;
end; end;
@ -5261,6 +5246,46 @@ begin
cellnode := FCells.FindSuccessor(cellnode); cellnode := FCells.FindSuccessor(cellnode);
end; end;
// Fix merged cells and shared formulas: If the inserted column runs through a
// block of merged cells or a shared formula the block is cut into two pieces.
// Here we fill the gap with dummy cells and set their MergeBase / SharedFormulaBase
// correctly.
if ACol > 0 then
begin
c := ACol - 1;
// Seek along the column immediately to the left of the inserted column
for r := 0 to GetLastOccupiedRowIndex do
begin
cell := FindCell(r, c);
if not Assigned(cell) then
continue;
// A merged cell block is found
if IsMerged(cell) then
begin
// Does it extend beyond the newly inserted column?
nextcell := FindCell(r, ACol+1);
if Assigned(nextcell) and (nextcell^.MergeBase = cell^.MergeBase) then
begin
// Yes: we add a cell into the gap row and merge it with the others.
gapcell := GetCell(r, ACol);
gapcell^.Mergebase := cell^.MergeBase;
end;
end else
// A shared formula block is found
if cell^.SharedFormulaBase <> nil then begin
// Does it extend beyond the newly inserted column?
nextcell := FindCell(r, ACol+1);
if Assigned(nextcell) and (nextcell^.SharedFormulaBase = cell^.SharedFormulaBase) then
begin
// Yes - we add a cell into the gap and share the formula of the base
gapcell := GetCell(r, ACol);
gapcell^.SharedFormulaBase := cell^.SharedFormulaBase;
end;
end;
end;
end;
(*
// Fix merged cells: If the inserted column runs through a block of merged // Fix merged cells: If the inserted column runs through a block of merged
// cells the block is cut into two pieces. Here we fill the gap with dummy // cells the block is cut into two pieces. Here we fill the gap with dummy
// cells and set their MergeBase correctly. // cells and set their MergeBase correctly.
@ -5280,6 +5305,7 @@ begin
end; end;
end; end;
end; end;
*)
// Update column index of column records // Update column index of column records
for i:=0 to FCols.Count-1 do begin for i:=0 to FCols.Count-1 do begin
@ -5298,7 +5324,6 @@ var
cell: PCell; cell: PCell;
row: PtrInt; row: PtrInt;
i: Integer; i: Integer;
fe: TsFormulaElement;
formula: TsRPNFormula; formula: TsRPNFormula;
begin begin
row := PtrInt(arg); row := PtrInt(arg);
@ -5315,14 +5340,13 @@ begin
formula := BuildRPNFormula(cell); formula := BuildRPNFormula(cell);
// (2) update cell addresses affected by the insertion of a column // (2) update cell addresses affected by the insertion of a column
for i:=0 to Length(formula)-1 do begin for i:=0 to Length(formula)-1 do begin
fe := formula[i]; // "fe" means "formula element" case formula[i].ElementKind of
case fe.ElementKind of
fekCell, fekCellRef: fekCell, fekCellRef:
if fe.Row >= row then inc(fe.Row); if formula[i].Row >= row then inc(formula[i].Row);
fekCellRange: fekCellRange:
begin begin
if fe.Row >= row then inc(fe.Row); if formula[i].Row >= row then inc(formula[i].Row);
if fe.Row2 >= row then inc(fe.Row2); if formula[i].Row2 >= row then inc(formula[i].Row2);
end; end;
end; end;
end; end;
@ -5343,7 +5367,7 @@ var
row: PRow; row: PRow;
cellnode: TAVLTreeNode; cellnode: TAVLTreeNode;
i: Integer; i: Integer;
r, c, r1, c1, r2, c2: Cardinal; r, c, cc, r1, c1, r2, c2: Cardinal;
cell, nextcell, gapcell: PCell; cell, nextcell, gapcell: PCell;
begin begin
// Update row index of cell records // Update row index of cell records
@ -5353,25 +5377,44 @@ begin
cellnode := FCells.FindSuccessor(cellnode); cellnode := FCells.FindSuccessor(cellnode);
end; end;
// Fix merged cells: If the inserted row runs through a block of merged // Fix merged cells and shared formulas: If the inserted row runs through a
// cells the block is cut into two pieces. Here we fill the gap with dummy // block of merged cells or a shared formula the block is cut into two pieces.
// cells and set their MergeBase correctly. // Here we fill the gap with dummy cells and set their MergeBase / SharedFormulaBase
for r := 0 to GetLastRowIndex do // correctly.
for c := 0 to GetLastColIndex do if ARow > 0 then
begin
r := ARow - 1;
// Seek along the row immediately above the inserted row
for c := 0 to GetLastOccupiedColIndex do
begin begin
cell := FindCell(r, c); cell := FindCell(r, c);
if IsMergeBase(cell) then begin if not Assigned(cell) then
FindMergedRange(cell, r1, c1, r2, c2); continue;
if ARow = r2 + 1 then begin // A merged cell block is found
nextcell := FindCell(ARow + 1, c); if IsMerged(cell) then
if Assigned(nextcell) and (nextcell^.MergeBase = cell) then begin
begin // Does it extend beyond the newly inserted row?
gapcell := GetCell(ARow, c); nextcell := FindCell(ARow+1, c);
gapcell^.MergeBase := cell; if Assigned(nextcell) and (nextcell^.MergeBase = cell^.MergeBase) then
end; begin
// Yes: we add a cell into the gap row and merge it with the others.
gapcell := GetCell(ARow, c);
gapcell^.Mergebase := cell^.MergeBase;
end;
end else
// A shared formula block is found
if cell^.SharedFormulaBase <> nil then begin
// Does it extend beyond the newly inserted row?
nextcell := FindCell(ARow+1, c);
if Assigned(nextcell) and (nextcell^.SharedFormulaBase = cell^.SharedFormulaBase) then
begin
// Yes - we add a cell into the gap and share the formula of the base
gapcell := GetCell(ARow, c);
gapcell^.SharedFormulaBase := cell^.SharedFormulaBase;
end; end;
end; end;
end; end;
end;
// Update row index of row records // Update row index of row records
for i:=0 to FRows.Count-1 do begin for i:=0 to FRows.Count-1 do begin