fpspreadsheet: Improved AVLTree enumerators (no speed loss of column enumerator compared to for-to cell access)

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@4050 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2015-03-18 18:11:52 +00:00
parent b4799b072a
commit e165ffb180
2 changed files with 75 additions and 32 deletions

View File

@ -20,6 +20,7 @@ type
{ TsRowColEnumerator } { TsRowColEnumerator }
TsRowColEnumerator = class TsRowColEnumerator = class
private
protected protected
FCurrentNode: TAVLTreeNode; FCurrentNode: TAVLTreeNode;
FTree: TsRowColAVLTree; FTree: TsRowColAVLTree;
@ -55,7 +56,7 @@ type
procedure Delete(ARow, ACol: Cardinal); overload; procedure Delete(ARow, ACol: Cardinal); overload;
procedure DeleteRowOrCol(AIndex: Cardinal; IsRow: Boolean); virtual; procedure DeleteRowOrCol(AIndex: Cardinal; IsRow: Boolean); virtual;
procedure Exchange(ARow1, ACol1, ARow2, ACol2: Cardinal); virtual; procedure Exchange(ARow1, ACol1, ARow2, ACol2: Cardinal); virtual;
function Find(ARow, ACol: Cardinal): PsRowCol; overload; function FindByRowCol(ARow, ACol: Cardinal): PsRowCol; overload;
function GetData(ANode: TAVLTreeNode): PsRowCol; function GetData(ANode: TAVLTreeNode): PsRowCol;
function GetFirst: PsRowCol; function GetFirst: PsRowCol;
function GetLast: PsRowCol; function GetLast: PsRowCol;
@ -197,6 +198,8 @@ end;
constructor TsRowColEnumerator.Create(ATree: TsRowColAVLTree; constructor TsRowColEnumerator.Create(ATree: TsRowColAVLTree;
AStartRow, AStartCol, AEndRow, AEndCol: Cardinal; AReverse: Boolean); AStartRow, AStartCol, AEndRow, AEndCol: Cardinal; AReverse: Boolean);
var
node: TAVLTreeNode;
begin begin
FTree := ATree; FTree := ATree;
FReverse := AReverse; FReverse := AReverse;
@ -218,6 +221,11 @@ begin
FStartCol := AEndCol; FStartCol := AEndCol;
FEndCol := AStartCol; FEndCol := AStartCol;
end; end;
if FEndRow = $7FFFFFFF then
begin
node := FTree.FindHighest;
FEndRow := PsRowCol(node.Data)^.Row;
end;
end; end;
function TsRowColEnumerator.GetCurrent: PsRowCol; function TsRowColEnumerator.GetCurrent: PsRowCol;
@ -236,6 +244,7 @@ end;
function TsRowColEnumerator.MoveNext: Boolean; function TsRowColEnumerator.MoveNext: Boolean;
var var
curr: PsRowCol; curr: PsRowCol;
rc: TsRowCol;
begin begin
Result := false; Result := false;
if FCurrentNode <> nil then begin if FCurrentNode <> nil then begin
@ -245,29 +254,57 @@ begin
if FCurrentNode <> nil then if FCurrentNode <> nil then
begin begin
curr := PsRowCol(FCurrentNode.Data); curr := PsRowCol(FCurrentNode.Data);
if not InRange(curr^.Col, FStartCol, FEndCol) then if not InRange(LongInt(curr^.Col), FStartCol, FEndCol) then
begin
rc := curr^;
if LongInt(rc.Col) < FStartCol then
dec(rc.Row);
rc.Col := FEndCol;
FCurrentNode := FTree.FindNearest(@rc);
if FCurrentNode <> nil then begin
curr := PsRowCol(FCurrentNode.Data);
while (FCurrentNode <> nil) and while (FCurrentNode <> nil) and
not InRange(curr^.Col, FStartCol, FEndCol) and (curr^.Row >= FStartRow) not (InRange(curr^.Row, FStartRow, FEndRow) and InRange(curr^.Col, FStartCol, FEndCol))
do begin do begin
FCurrentNode := FTree.FindPrecessor(FCurrentNode); FCurrentNode := FTree.FindPrecessor(FCurrentNode);
if FCurrentNode <> nil then curr := PsRowCol(FCurrentNode.Data); if FCurrentNode <> nil then curr := PsRowCol(FCurrentNode.Data);
end; end;
end; end;
end;
end;
end else end else
begin begin
FCurrentNode := FTree.FindSuccessor(FCurrentNode); FCurrentNode := FTree.FindSuccessor(FCurrentNode);
if FCurrentNode <> nil then if FCurrentNode <> nil then
begin begin
curr := PsRowCol(FCurrentNode.Data); curr := PsRowCol(FCurrentNode.Data);
if not InRange(curr^.Col, FStartCol, FEndCol) then rc.Col := FStartCol;
if LongInt(rc.Col) > FEndCol then inc(rc.Row);
if not InRange(LongInt(curr^.Col), FStartCol, FEndCol) then
begin
rc := curr^;
if LongInt(rc.Col) > FEndCol then inc(rc.Row);
rc.Col := FStartCol;
FCurrentNode := FTree.FindNearest(@rc);
if FCurrentNode <> nil then
begin
curr := PsRowCol(FCurrentNode.Data);
if (curr^.Col < FStartCol) then
while (FCurrentNode <> nil) and not InRange(curr^.Col, FStartCol, FEndCol) do
begin
FCurrentNode := FTree.FindSuccessor(FCurrentNode);
if FCurrentNode <> nil then curr := PsRowCol(FCurrentNode.Data);
end;
while (FCurrentNode <> nil) and while (FCurrentNode <> nil) and
not InRange(curr^.Col, FStartCol, FEndCol) and (curr^.Row <= FEndRow) not (InRange(curr^.Row, FStartRow, FEndRow) and InRange(curr^.Col, FStartCol, FEndCol))
do begin do begin
FCurrentNode := FTree.FindSuccessor(FCurrentNode); FCurrentNode := FTree.FindSuccessor(FCurrentNode);
if FCurrentNode <> nil then curr := PsRowCol(FCurrentNode.Data); if FCurrentNode <> nil then curr := PsRowCol(FCurrentNode.Data);
end; end;
end; end;
end; end;
end;
end;
Result := (FCurrentNode <> nil) and Result := (FCurrentNode <> nil) and
InRange(curr^.Col, FStartCol, FEndCol) and InRange(curr^.Col, FStartCol, FEndCol) and
InRange(curr^.Row, FStartRow, FEndRow); InRange(curr^.Row, FStartRow, FEndRow);
@ -275,7 +312,10 @@ begin
begin begin
if FReverse then if FReverse then
begin begin
FCurrentNode := FTree.FindHighest; rc.Row := FEndRow;
rc.Col := FEndCol;
FCurrentNode := FTree.FindNearest(@rc);
if FCurrentNode <> nil then
curr := PsRowCol(FCurrentNode.Data); curr := PsRowCol(FCurrentNode.Data);
while (FCurrentNode <> nil) and while (FCurrentNode <> nil) and
not (InRange(curr^.Row, FStartRow, FEndRow) and InRange(curr^.Col, FStartCol, FEndCol)) not (InRange(curr^.Row, FStartRow, FEndRow) and InRange(curr^.Col, FStartCol, FEndCol))
@ -285,8 +325,11 @@ begin
end; end;
end else end else
begin begin
FCurrentNode := FTree.FindLowest; rc.Row := FStartRow;
curr := Current; rc.Col := FStartCol;
FCurrentNode := FTree.FindNearest(@rc);
if FCurrentNode <> nil then
curr := PsRowCol(FCurrentNode.Data);
while (FCurrentNode <> nil) and while (FCurrentNode <> nil) and
not (InRange(curr^.Row, FStartRow, FEndRow) and InRange(curr^.Col, FStartCol, FEndCol)) not (InRange(curr^.Row, FStartRow, FEndRow) and InRange(curr^.Col, FStartCol, FEndCol))
do begin do begin
@ -423,8 +466,8 @@ procedure TsRowColAVLTree.Exchange(ARow1, ACol1, ARow2, ACol2: Cardinal);
var var
item1, item2: PsRowCol; item1, item2: PsRowCol;
begin begin
item1 := Find(ARow1, ACol1); item1 := FindByRowCol(ARow1, ACol1);
item2 := Find(ARow2, ACol2); item2 := FindByRowCol(ARow2, ACol2);
// There are entries for both locations: Exchange row/col indexes // There are entries for both locations: Exchange row/col indexes
if (item1 <> nil) and (item2 <> nil) then if (item1 <> nil) and (item2 <> nil) then
@ -463,7 +506,7 @@ end;
returns a pointer to the data record. returns a pointer to the data record.
Returns nil if such a node does not exist Returns nil if such a node does not exist
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
function TsRowColAVLTree.Find(ARow, ACol: Cardinal): PsRowCol; function TsRowColAVLTree.FindByRowCol(ARow, ACol: Cardinal): PsRowCol;
var var
data: TsRowCol; data: TsRowCol;
node: TAVLTreeNode; node: TAVLTreeNode;
@ -608,7 +651,7 @@ end;
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
function TsCells.FindCell(ARow, ACol: Cardinal): PCell; function TsCells.FindCell(ARow, ACol: Cardinal): PCell;
begin begin
Result := PCell(Find(ARow, ACol)); Result := PCell(FindByRowCol(ARow, ACol));
end; end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
@ -745,7 +788,7 @@ end;
function TsComments.AddComment(ARow, ACol: Cardinal; function TsComments.AddComment(ARow, ACol: Cardinal;
AComment: String): PsComment; AComment: String): PsComment;
begin begin
Result := PsComment(Find(ARow, ACol)); Result := PsComment(FindByRowCol(ARow, ACol));
if Result = nil then if Result = nil then
Result := PsComment(Add(ARow, ACol)); Result := PsComment(Add(ARow, ACol));
Result^.Text := AComment; Result^.Text := AComment;
@ -825,7 +868,7 @@ end;
function TsHyperlinks.AddHyperlink(ARow, ACol: Cardinal; ATarget: String; function TsHyperlinks.AddHyperlink(ARow, ACol: Cardinal; ATarget: String;
ATooltip: String = ''): PsHyperlink; ATooltip: String = ''): PsHyperlink;
begin begin
Result := PsHyperlink(Find(ARow, ACol)); Result := PsHyperlink(FindByRowCol(ARow, ACol));
if Result = nil then if Result = nil then
Result := PsHyperlink(Add(ARow, ACol)); Result := PsHyperlink(Add(ARow, ACol));
Result^.Target := ATarget; Result^.Target := ATarget;
@ -905,7 +948,7 @@ end;
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
function TsMergedCells.AddRange(ARow1, ACol1, ARow2, ACol2: Cardinal): PsCellRange; function TsMergedCells.AddRange(ARow1, ACol1, ARow2, ACol2: Cardinal): PsCellRange;
begin begin
Result := PsCellRange(Find(ARow1, ACol1)); Result := PsCellRange(FindByRowCol(ARow1, ACol1));
if Result = nil then if Result = nil then
Result := PsCellRange(Add(ARow1, ACol1)); Result := PsCellRange(Add(ARow1, ACol1));
Result^.Row2 := ARow2; Result^.Row2 := ARow2;
@ -999,7 +1042,7 @@ var
rng: PsCellrange; rng: PsCellrange;
dr, dc: Cardinal; dr, dc: Cardinal;
begin begin
rng := PsCellrange(Find(ARow1, ACol1)); rng := PsCellrange(FindByRowCol(ARow1, ACol1));
if rng <> nil then if rng <> nil then
begin begin
dr := rng^.Row2 - rng^.Row1; dr := rng^.Row2 - rng^.Row1;
@ -1010,7 +1053,7 @@ begin
rng^.Col2 := ACol2 + dc; rng^.Col2 := ACol2 + dc;
end; end;
rng := PsCellRange(Find(ARow2, ACol2)); rng := PsCellRange(FindByRowCol(ARow2, ACol2));
if rng <> nil then if rng <> nil then
begin begin
dr := rng^.Row2 - rng^.Row1; dr := rng^.Row2 - rng^.Row1;

View File

@ -1360,7 +1360,7 @@ end;
function TsWorksheet.FindComment(ACell: PCell): PsComment; function TsWorksheet.FindComment(ACell: PCell): PsComment;
begin begin
if HasComment(ACell) then if HasComment(ACell) then
Result := PsComment(FComments.Find(ACell^.Row, ACell^.Col)) Result := PsComment(FComments.FindByRowCol(ACell^.Row, ACell^.Col))
else else
Result := nil; Result := nil;
end; end;
@ -1385,7 +1385,7 @@ var
comment: PsComment; comment: PsComment;
begin begin
Result := ''; Result := '';
comment := PsComment(FComments.Find(ARow, ACol)); comment := PsComment(FComments.FindByRowCol(ARow, ACol));
if comment <> nil then if comment <> nil then
Result := comment^.Text; Result := comment^.Text;
end; end;
@ -1460,7 +1460,7 @@ end;
function TsWorksheet.FindHyperlink(ACell: PCell): PsHyperlink; function TsWorksheet.FindHyperlink(ACell: PCell): PsHyperlink;
begin begin
if HasHyperlink(ACell) then if HasHyperlink(ACell) then
Result := PsHyperlink(FHyperlinks.Find(ACell^.Row, ACell^.Col)) Result := PsHyperlink(FHyperlinks.FindByRowCol(ACell^.Row, ACell^.Col))
else else
Result := nil; Result := nil;
end; end;
@ -2074,7 +2074,7 @@ end;
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
function TsWorksheet.FindCell(ARow, ACol: Cardinal): PCell; function TsWorksheet.FindCell(ARow, ACol: Cardinal): PCell;
begin begin
Result := PCell(FCells.Find(ARow, ACol)); Result := PCell(FCells.FindByRowCol(ARow, ACol));
{ {
if (ARow = FLastFoundRow) and (ACol = FLastFoundCol) then if (ARow = FLastFoundRow) and (ACol = FLastFoundCol) then
Result := FLastFoundCell Result := FLastFoundCell
@ -3119,7 +3119,7 @@ begin
exit; exit;
// Is cell ARow1/ACol1 already the base of a merged range? ... // Is cell ARow1/ACol1 already the base of a merged range? ...
rng := PsCellRange(FMergedCells.Find(ARow1, ACol1)); rng := PsCellRange(FMergedCells.FindByRowCol(ARow1, ACol1));
// ... no: --> Add a new merged range // ... no: --> Add a new merged range
if rng = nil then if rng = nil then
FMergedCells.AddRange(ARow1, ACol1, ARow2, ACol2) FMergedCells.AddRange(ARow1, ACol1, ARow2, ACol2)
@ -3296,7 +3296,7 @@ end;
-------------------------------------------------------------------------------} -------------------------------------------------------------------------------}
function TsWorksheet.RemoveCell(ARow, ACol: Cardinal): PCell; function TsWorksheet.RemoveCell(ARow, ACol: Cardinal): PCell;
begin begin
Result := PCell(FCells.Find(ARow, ACol)); Result := PCell(FCells.FindByRowCol(ARow, ACol));
if Result <> nil then FCells.Remove(Result); if Result <> nil then FCells.Remove(Result);
end; end;