From e165ffb180e08da65ef4e5834311ff69a92e7675 Mon Sep 17 00:00:00 2001 From: wp_xxyyzz Date: Wed, 18 Mar 2015 18:11:52 +0000 Subject: [PATCH] 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 --- components/fpspreadsheet/fpsclasses.pas | 95 ++++++++++++++++------ components/fpspreadsheet/fpspreadsheet.pas | 12 +-- 2 files changed, 75 insertions(+), 32 deletions(-) diff --git a/components/fpspreadsheet/fpsclasses.pas b/components/fpspreadsheet/fpsclasses.pas index fee1d3165..89525d888 100644 --- a/components/fpspreadsheet/fpsclasses.pas +++ b/components/fpspreadsheet/fpsclasses.pas @@ -20,6 +20,7 @@ type { TsRowColEnumerator } TsRowColEnumerator = class + private protected FCurrentNode: TAVLTreeNode; FTree: TsRowColAVLTree; @@ -55,7 +56,7 @@ type procedure Delete(ARow, ACol: Cardinal); overload; procedure DeleteRowOrCol(AIndex: Cardinal; IsRow: Boolean); 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 GetFirst: PsRowCol; function GetLast: PsRowCol; @@ -197,6 +198,8 @@ end; constructor TsRowColEnumerator.Create(ATree: TsRowColAVLTree; AStartRow, AStartCol, AEndRow, AEndCol: Cardinal; AReverse: Boolean); +var + node: TAVLTreeNode; begin FTree := ATree; FReverse := AReverse; @@ -218,6 +221,11 @@ begin FStartCol := AEndCol; FEndCol := AStartCol; end; + if FEndRow = $7FFFFFFF then + begin + node := FTree.FindHighest; + FEndRow := PsRowCol(node.Data)^.Row; + end; end; function TsRowColEnumerator.GetCurrent: PsRowCol; @@ -236,6 +244,7 @@ end; function TsRowColEnumerator.MoveNext: Boolean; var curr: PsRowCol; + rc: TsRowCol; begin Result := false; if FCurrentNode <> nil then begin @@ -245,13 +254,23 @@ begin if FCurrentNode <> nil then begin curr := PsRowCol(FCurrentNode.Data); - if not InRange(curr^.Col, FStartCol, FEndCol) then - while (FCurrentNode <> nil) and - not InRange(curr^.Col, FStartCol, FEndCol) and (curr^.Row >= FStartRow) - do begin - FCurrentNode := FTree.FindPrecessor(FCurrentNode); - if FCurrentNode <> nil then curr := PsRowCol(FCurrentNode.Data); + 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 + not (InRange(curr^.Row, FStartRow, FEndRow) and InRange(curr^.Col, FStartCol, FEndCol)) + do begin + FCurrentNode := FTree.FindPrecessor(FCurrentNode); + if FCurrentNode <> nil then curr := PsRowCol(FCurrentNode.Data); + end; end; + end; end; end else begin @@ -259,13 +278,31 @@ begin if FCurrentNode <> nil then begin curr := PsRowCol(FCurrentNode.Data); - if not InRange(curr^.Col, FStartCol, FEndCol) then - while (FCurrentNode <> nil) and - not InRange(curr^.Col, FStartCol, FEndCol) and (curr^.Row <= FEndRow) - do begin - FCurrentNode := FTree.FindSuccessor(FCurrentNode); - if FCurrentNode <> nil then curr := PsRowCol(FCurrentNode.Data); + 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 + not (InRange(curr^.Row, FStartRow, FEndRow) and InRange(curr^.Col, FStartCol, FEndCol)) + do begin + FCurrentNode := FTree.FindSuccessor(FCurrentNode); + if FCurrentNode <> nil then curr := PsRowCol(FCurrentNode.Data); + end; end; + end; end; end; Result := (FCurrentNode <> nil) and @@ -275,8 +312,11 @@ begin begin if FReverse then begin - FCurrentNode := FTree.FindHighest; - curr := PsRowCol(FCurrentNode.Data); + rc.Row := FEndRow; + rc.Col := FEndCol; + FCurrentNode := FTree.FindNearest(@rc); + if FCurrentNode <> nil then + curr := PsRowCol(FCurrentNode.Data); while (FCurrentNode <> nil) and not (InRange(curr^.Row, FStartRow, FEndRow) and InRange(curr^.Col, FStartCol, FEndCol)) do begin @@ -285,8 +325,11 @@ begin end; end else begin - FCurrentNode := FTree.FindLowest; - curr := Current; + rc.Row := FStartRow; + rc.Col := FStartCol; + FCurrentNode := FTree.FindNearest(@rc); + if FCurrentNode <> nil then + curr := PsRowCol(FCurrentNode.Data); while (FCurrentNode <> nil) and not (InRange(curr^.Row, FStartRow, FEndRow) and InRange(curr^.Col, FStartCol, FEndCol)) do begin @@ -423,8 +466,8 @@ procedure TsRowColAVLTree.Exchange(ARow1, ACol1, ARow2, ACol2: Cardinal); var item1, item2: PsRowCol; begin - item1 := Find(ARow1, ACol1); - item2 := Find(ARow2, ACol2); + item1 := FindByRowCol(ARow1, ACol1); + item2 := FindByRowCol(ARow2, ACol2); // There are entries for both locations: Exchange row/col indexes if (item1 <> nil) and (item2 <> nil) then @@ -463,7 +506,7 @@ end; returns a pointer to the data record. Returns nil if such a node does not exist -------------------------------------------------------------------------------} -function TsRowColAVLTree.Find(ARow, ACol: Cardinal): PsRowCol; +function TsRowColAVLTree.FindByRowCol(ARow, ACol: Cardinal): PsRowCol; var data: TsRowCol; node: TAVLTreeNode; @@ -608,7 +651,7 @@ end; -------------------------------------------------------------------------------} function TsCells.FindCell(ARow, ACol: Cardinal): PCell; begin - Result := PCell(Find(ARow, ACol)); + Result := PCell(FindByRowCol(ARow, ACol)); end; {@@ ---------------------------------------------------------------------------- @@ -745,7 +788,7 @@ end; function TsComments.AddComment(ARow, ACol: Cardinal; AComment: String): PsComment; begin - Result := PsComment(Find(ARow, ACol)); + Result := PsComment(FindByRowCol(ARow, ACol)); if Result = nil then Result := PsComment(Add(ARow, ACol)); Result^.Text := AComment; @@ -825,7 +868,7 @@ end; function TsHyperlinks.AddHyperlink(ARow, ACol: Cardinal; ATarget: String; ATooltip: String = ''): PsHyperlink; begin - Result := PsHyperlink(Find(ARow, ACol)); + Result := PsHyperlink(FindByRowCol(ARow, ACol)); if Result = nil then Result := PsHyperlink(Add(ARow, ACol)); Result^.Target := ATarget; @@ -905,7 +948,7 @@ end; -------------------------------------------------------------------------------} function TsMergedCells.AddRange(ARow1, ACol1, ARow2, ACol2: Cardinal): PsCellRange; begin - Result := PsCellRange(Find(ARow1, ACol1)); + Result := PsCellRange(FindByRowCol(ARow1, ACol1)); if Result = nil then Result := PsCellRange(Add(ARow1, ACol1)); Result^.Row2 := ARow2; @@ -999,7 +1042,7 @@ var rng: PsCellrange; dr, dc: Cardinal; begin - rng := PsCellrange(Find(ARow1, ACol1)); + rng := PsCellrange(FindByRowCol(ARow1, ACol1)); if rng <> nil then begin dr := rng^.Row2 - rng^.Row1; @@ -1010,7 +1053,7 @@ begin rng^.Col2 := ACol2 + dc; end; - rng := PsCellRange(Find(ARow2, ACol2)); + rng := PsCellRange(FindByRowCol(ARow2, ACol2)); if rng <> nil then begin dr := rng^.Row2 - rng^.Row1; diff --git a/components/fpspreadsheet/fpspreadsheet.pas b/components/fpspreadsheet/fpspreadsheet.pas index 821ec8099..76d4b1357 100755 --- a/components/fpspreadsheet/fpspreadsheet.pas +++ b/components/fpspreadsheet/fpspreadsheet.pas @@ -1360,7 +1360,7 @@ end; function TsWorksheet.FindComment(ACell: PCell): PsComment; begin if HasComment(ACell) then - Result := PsComment(FComments.Find(ACell^.Row, ACell^.Col)) + Result := PsComment(FComments.FindByRowCol(ACell^.Row, ACell^.Col)) else Result := nil; end; @@ -1385,7 +1385,7 @@ var comment: PsComment; begin Result := ''; - comment := PsComment(FComments.Find(ARow, ACol)); + comment := PsComment(FComments.FindByRowCol(ARow, ACol)); if comment <> nil then Result := comment^.Text; end; @@ -1460,7 +1460,7 @@ end; function TsWorksheet.FindHyperlink(ACell: PCell): PsHyperlink; begin if HasHyperlink(ACell) then - Result := PsHyperlink(FHyperlinks.Find(ACell^.Row, ACell^.Col)) + Result := PsHyperlink(FHyperlinks.FindByRowCol(ACell^.Row, ACell^.Col)) else Result := nil; end; @@ -2074,7 +2074,7 @@ end; -------------------------------------------------------------------------------} function TsWorksheet.FindCell(ARow, ACol: Cardinal): PCell; begin - Result := PCell(FCells.Find(ARow, ACol)); + Result := PCell(FCells.FindByRowCol(ARow, ACol)); { if (ARow = FLastFoundRow) and (ACol = FLastFoundCol) then Result := FLastFoundCell @@ -3119,7 +3119,7 @@ begin exit; // 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 if rng = nil then FMergedCells.AddRange(ARow1, ACol1, ARow2, ACol2) @@ -3296,7 +3296,7 @@ end; -------------------------------------------------------------------------------} function TsWorksheet.RemoveCell(ARow, ACol: Cardinal): PCell; begin - Result := PCell(FCells.Find(ARow, ACol)); + Result := PCell(FCells.FindByRowCol(ARow, ACol)); if Result <> nil then FCells.Remove(Result); end;