diff --git a/components/fpspreadsheet/fpsopendocument.pas b/components/fpspreadsheet/fpsopendocument.pas index 4dc69b4f2..244f1c05d 100755 --- a/components/fpspreadsheet/fpsopendocument.pas +++ b/components/fpspreadsheet/fpsopendocument.pas @@ -30,7 +30,7 @@ uses Classes, SysUtils, fpszipper, {NOTE: fpszipper is the latest zipper.pp Change to standard zipper when FPC 2.4 is released. Changed by JLJR} fpspreadsheet, - xmlread, DOM; + xmlread, DOM, AVL_Tree; type @@ -404,6 +404,8 @@ var CurCell: PCell; CurRow: array of PCell; LastColNum: Cardinal; + ACell: PCell; + AVLNode: TAVLTreeNode; begin LastColNum := CurSheet.GetLastColNumber; @@ -413,41 +415,30 @@ begin ' ' + LineEnding; + ACell := GetMem(SizeOf(TCell)); + // The cells need to be written in order, row by row, cell by cell for j := 0 to CurSheet.GetLastRowNumber do begin FContent := FContent + ' ' + LineEnding; - // First make an array with the cells of this row in their respective order - // nil pointers indicate empty cells, so it's necessary to initialize the array - SetLength(CurRow, LastColNum + 1); - for k := 0 to LastColNum do CurRow[k] := nil; - - // Now fill the array with the cells in their proper place - for k := 0 to CurSheet.FCells.Count - 1 do - begin - CurCell := CurSheet.FCells.Items[k]; - if CurCell^.Row = j then CurRow[CurCell^.Col] := CurCell; - end; - - // And now write all cells from this row + // Write cells from this row. for k := 0 to LastColNum do begin - CurCell := CurRow[k]; - - if CurCell = nil then - FContent := FContent + '' + LineEnding - else WriteCellCallback(CurCell, nil); + ACell^.Row := j; + ACell^.Col := k; + AVLNode := CurSheet.Cells.Find(ACell); + if Assigned(AVLNode) then + WriteCellCallback(PCell(AVLNode.Data), nil) + else + FContent := FContent + '' + LineEnding; end; FContent := FContent + ' ' + LineEnding; end; - // Clean up - SetLength(CurRow, 0); - // Footer FContent := FContent + ' ' + LineEnding; diff --git a/components/fpspreadsheet/fpspreadsheet.pas b/components/fpspreadsheet/fpspreadsheet.pas index 83278799c..abd0cbb61 100755 --- a/components/fpspreadsheet/fpspreadsheet.pas +++ b/components/fpspreadsheet/fpspreadsheet.pas @@ -14,7 +14,7 @@ unit fpspreadsheet; interface uses - Classes, SysUtils; + Classes, SysUtils, AVL_Tree; type TsSpreadsheetFormat = (sfExcel2, sfExcel3, sfExcel4, sfExcel5, sfExcel8, @@ -98,9 +98,9 @@ type TsWorksheet = class private + FCells: TAvlTree; procedure RemoveCallback(data, arg: pointer); public - FCells: TFPList; Name: string; { Base methods } constructor Create; @@ -109,7 +109,6 @@ type function FindCell(ARow, ACol: Cardinal): PCell; function GetCell(ARow, ACol: Cardinal): PCell; function GetCellCount: Cardinal; - function GetCellByIndex(AIndex: Cardinal): PCell; function GetLastColNumber: Cardinal; function GetLastRowNumber: Cardinal; function ReadAsUTF8Text(ARow, ACol: Cardinal): ansistring; @@ -118,6 +117,7 @@ type procedure WriteNumber(ARow, ACol: Cardinal; ANumber: double); procedure WriteFormula(ARow, ACol: Cardinal; AFormula: TsFormula); procedure WriteRPNFormula(ARow, ACol: Cardinal; AFormula: TsRPNFormula); + property Cells: TAVLTree read FCells; end; { TsWorkbook } @@ -178,7 +178,7 @@ type function ExpandFormula(AFormula: TsFormula): TsExpandedFormula; { General writing methods } procedure WriteCellCallback(data, arg: pointer); - procedure WriteCellsToStream(AStream: TStream; ACells: TFPList); + procedure WriteCellsToStream(AStream: TStream; ACells: TAVLTree); procedure WriteToFile(AFileName: string; AData: TsWorkbook); virtual; procedure WriteToStream(AStream: TStream; AData: TsWorkbook); virtual; { Record writing methods } @@ -206,6 +206,9 @@ procedure RegisterSpreadFormat( implementation +uses + Math; + var { Translatable strings } lpUnsupportedReadFormat, lpUnsupportedWriteFormat: string; @@ -241,6 +244,13 @@ begin FreeMem(data); end; +function CompareCells(Item1, Item2: Pointer): Integer; +begin + result := PCell(Item1).Row - PCell(Item2).Row; + if Result = 0 then + Result := PCell(Item1).Col - PCell(Item2).Col; +end; + {@@ Constructor. } @@ -248,7 +258,7 @@ constructor TsWorksheet.Create; begin inherited Create; - FCells := TFPList.Create; + FCells := TAVLTree.Create(@CompareCells); end; {@@ @@ -277,24 +287,16 @@ end; } function TsWorksheet.FindCell(ARow, ACol: Cardinal): PCell; var - i: Integer; - ACell: PCell; + LCell: TCell; + AVLNode: TAVLTreeNode; begin - i := 0; Result := nil; - - while (i < FCells.Count) do - begin - ACell := PCell(FCells.Items[i]); - - if (ACell^.Row = ARow) and (ACell^.Col = ACol) then - begin - Result := ACell; - Exit; - end; - - Inc(i); - end; + + LCell.Row := ARow; + LCell.Col := ACol; + AVLNode := Cells.Find(@LCell); + if Assigned(AVLNode) then + result := PCell(AVLNode.Data); end; {@@ @@ -326,7 +328,7 @@ begin Result^.Row := ARow; Result^.Col := ACol; - FCells.Add(Result); + Cells.Add(Result); end; end; @@ -343,28 +345,7 @@ end; } function TsWorksheet.GetCellCount: Cardinal; begin - Result := FCells.Count; -end; - -{@@ - Obtains the cell with a specific index in the internal list of cells. - - The index goes from 0 to GetCellCount - 1. - - This routine is used together with GetCellCount to - iterate througth all cells in a worksheet efficiently. - - @param AIndex The index of the cell to be obtained - - @return A pointer to the cell, or nil if it doesn't exist - - @see TCell - @see GetCellCount -} -function TsWorksheet.GetCellByIndex(AIndex: Cardinal): PCell; -begin - if FCells.Count > AIndex then Result := PCell(FCells.Items[AIndex]) - else Result := nil; + Result := Cells.Count; end; {@@ @@ -379,19 +360,18 @@ end; } function TsWorksheet.GetLastColNumber: Cardinal; var - i: Integer; - ACell: PCell; + AVLNode: TAVLTreeNode; begin - i := 0; Result := 0; - while (i < FCells.Count) do + // Traverse the tree from lowest to highest. + // Since tree primary sort order is on Row + // highest Col could exist anywhere. + AVLNode := Cells.FindLowest; + While Assigned(AVLNode) do begin - ACell := PCell(FCells.Items[i]); - - if ACell^.Col > Result then Result := ACell^.Col; - - Inc(i); + Result := Math.Max(Result, PCell(AVLNode.Data)^.Col); + AVLNode := Cells.FindSuccessor(AVLNode); end; end; @@ -407,20 +387,13 @@ end; } function TsWorksheet.GetLastRowNumber: Cardinal; var - i: Integer; - ACell: PCell; + AVLNode: TAVLTreeNode; begin - i := 0; Result := 0; - while (i < FCells.Count) do - begin - ACell := PCell(FCells.Items[i]); - - if ACell^.Row > Result then Result := ACell^.Row; - - Inc(i); - end; + AVLNode := FCells.FindHighest; + if Assigned(AVLNode) then + Result := PCell(AVLNode.Data).Row; end; {@@ @@ -460,8 +433,16 @@ end; Clears the list of Cells and releases their memory. } procedure TsWorksheet.RemoveAllCells; +var + Node: TAVLTreeNode; begin - FCells.ForEachCall(RemoveCallback, nil); + Node:=FCells.FindLowest; + while Assigned(Node) do begin + RemoveCallback(Node.Data,nil); + Node.Data:=nil; + Node:=FCells.FindSuccessor(Node); + end; + FCells.Clear; end; {@@ @@ -725,7 +706,7 @@ end; } function TsWorkbook.GetWorksheetByIndex(AIndex: Cardinal): TsWorksheet; begin - if AIndex < FWorksheets.Count then Result := TsWorksheet(FWorksheets.Items[AIndex]) + if (integer(AIndex) < FWorksheets.Count) and (integer(AIndex)>=0) then Result := TsWorksheet(FWorksheets.Items[AIndex]) else Result := nil; end; @@ -854,9 +835,16 @@ end; @param AStream The output stream. @param ACells List of cells to be writeen } -procedure TsCustomSpreadWriter.WriteCellsToStream(AStream: TStream; ACells: TFPList); +procedure TsCustomSpreadWriter.WriteCellsToStream(AStream: TStream; ACells: TAVLTree); +var + AVLNode: TAVLTreeNode; begin - ACells.ForEachCall(WriteCellCallback, Pointer(AStream)); + AVLNode := ACells.FindLowest; + While Assigned(AVLNode) do + begin + WriteCellCallback(AVLNode.Data, Pointer(AStream)); + AVLNode := ACells.FindSuccessor(AVLNode); + end; end; {@@ diff --git a/components/fpspreadsheet/xlsbiff2.pas b/components/fpspreadsheet/xlsbiff2.pas index 3347042a5..544d0e012 100755 --- a/components/fpspreadsheet/xlsbiff2.pas +++ b/components/fpspreadsheet/xlsbiff2.pas @@ -130,7 +130,7 @@ procedure TsSpreadBIFF2Writer.WriteToStream(AStream: TStream; AData: TsWorkbook) begin WriteBOF(AStream); - WriteCellsToStream(AStream, AData.GetFirstWorksheet.FCells); + WriteCellsToStream(AStream, AData.GetFirstWorksheet.Cells); WriteEOF(AStream); end; diff --git a/components/fpspreadsheet/xlsbiff5.pas b/components/fpspreadsheet/xlsbiff5.pas index 6f94c8a53..e8306a540 100755 --- a/components/fpspreadsheet/xlsbiff5.pas +++ b/components/fpspreadsheet/xlsbiff5.pas @@ -403,7 +403,7 @@ begin WriteWindow2(AStream, True); - WriteCellsToStream(AStream, AData.GetWorksheetByIndex(i).FCells); + WriteCellsToStream(AStream, AData.GetWorksheetByIndex(i).Cells); WriteEOF(AStream); end; diff --git a/components/fpspreadsheet/xlsbiff8.pas b/components/fpspreadsheet/xlsbiff8.pas index dfa35b323..2af8dd5af 100755 --- a/components/fpspreadsheet/xlsbiff8.pas +++ b/components/fpspreadsheet/xlsbiff8.pas @@ -370,7 +370,7 @@ begin WriteWindow2(AStream, True); - WriteCellsToStream(AStream, AData.GetWorksheetByIndex(i).FCells); + WriteCellsToStream(AStream, AData.GetWorksheetByIndex(i).Cells); WriteEOF(AStream); end;