fpspreadsheet: Replace for-to loops by for-in loops if possible. Update example demos.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3991 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz 2015-03-05 10:35:32 +00:00
parent 75b09e07c3
commit c5f6cbe82d
19 changed files with 582 additions and 491 deletions

View File

@ -9,7 +9,7 @@ program myexcel2read;
{$mode delphi}{$H+}
uses
Classes, SysUtils, fpstypes, fpspreadsheet, fpscsv;
Classes, SysUtils, LazUTF8, fpstypes, fpspreadsheet, fpscsv;
var
MyWorkbook: TsWorkbook;
@ -45,17 +45,16 @@ begin
WriteLn('Contents of the first worksheet of the file:');
WriteLn('');
CurCell := MyWorkSheet.GetFirstCell();
for i := 0 to MyWorksheet.GetCellCount - 1 do
for CurCell in MyWorksheet.Cells do
begin
if HasFormula(CurCell) then
WriteLn('Row: ', CurCell^.Row, ' Col: ', CurCell^.Col, ' Formula: ', MyWorksheet.ReadFormulaAsString(CurCell))
else
WriteLn('Row: ', CurCell^.Row,
WriteLn(
'Row: ', CurCell^.Row,
' Col: ', CurCell^.Col,
' Value: ', UTF8ToAnsi(MyWorkSheet.ReadAsUTF8Text(CurCell^.Row, CurCell^.Col))
' Value: ', UTF8ToConsole(MyWorkSheet.ReadAsUTF8Text(CurCell^.Row, CurCell^.Col))
);
CurCell := MyWorkSheet.GetNextCell();
end;
// Finalization

View File

@ -10,7 +10,7 @@ program excel2read;
{$mode delphi}{$H+}
uses
Classes, SysUtils, fpsTypes, fpspreadsheet, xlsbiff2;
Classes, SysUtils, LazUTF8, fpsTypes, fpspreadsheet, xlsbiff2;
var
MyWorkbook: TsWorkbook;
@ -19,6 +19,7 @@ var
MyDir: string;
i: Integer;
CurCell: PCell;
begin
// Open the input file
MyDir := ExtractFilePath(ParamStr(0));
@ -43,16 +44,14 @@ begin
WriteLn('Contents of the first worksheet of the file:');
WriteLn('');
CurCell := MyWorkSheet.GetFirstCell();
for i := 0 to MyWorksheet.GetCellCount - 1 do
for CurCell in MyWorksheet.Cells do
begin
Write('Row: ', CurCell^.Row, ' Col: ', CurCell^.Col, ' Value: ',
UTF8ToAnsi(MyWorkSheet.ReadAsUTF8Text(CurCell^.Row, CurCell^.Col))
UTF8ToConsole(MyWorkSheet.ReadAsUTF8Text(CurCell^.Row, CurCell^.Col))
);
if HasFormula(CurCell) then
Write(' (Formula ', CurCell^.FormulaValue, ')');
WriteLn;
CurCell := MyWorkSheet.GetNextCell();
end;
// Finalization

View File

@ -10,7 +10,7 @@ program excel5read;
{$mode delphi}{$H+}
uses
Classes, SysUtils, fpsTypes, fpspreadsheet, xlsbiff5;
Classes, SysUtils, LazUTF8, fpsTypes, fpspreadsheet, xlsbiff5;
var
MyWorkbook: TsWorkbook;
@ -41,16 +41,14 @@ begin
WriteLn('Contents of the first worksheet of the file:');
WriteLn('');
CurCell := MyWorkSheet.GetFirstCell();
for i := 0 to MyWorksheet.GetCellCount - 1 do
for CurCell in MyWorksheet.Cells do
begin
Write('Row: ', CurCell^.Row,
' Col: ', CurCell^.Col, ' Value: ',
UTF8ToAnsi(MyWorkSheet.ReadAsUTF8Text(CurCell^.Row, CurCell^.Col)));
UTF8ToConsole(MyWorkSheet.ReadAsUTF8Text(CurCell^.Row, CurCell^.Col)));
if HasFormula(CurCell) then
Write(' - Formula: ', CurCell^.FormulaValue);
WriteLn;
CurCell := MyWorkSheet.GetNextCell();
end;
// Finalization

View File

@ -10,7 +10,7 @@ program excel8read;
{$mode delphi}{$H+}
uses
Classes, SysUtils, fpsTypes, fpspreadsheet, xlsbiff8,
Classes, SysUtils, LazUTF8, fpsTypes, fpspreadsheet, xlsbiff8,
fpsutils;
var
@ -47,19 +47,17 @@ begin
WriteLn('Contents of the first worksheet of the file:');
WriteLn('');
CurCell := MyWorkSheet.GetFirstCell();
for i := 0 to MyWorksheet.GetCellCount - 1 do
for CurCell in MyWorksheet.Cells do
begin
Write('Row: ', CurCell^.Row,
' Col: ', CurCell^.Col, ' Value: ',
UTF8ToAnsi(MyWorkSheet.ReadAsUTF8Text(CurCell^.Row,
UTF8ToConsole(MyWorkSheet.ReadAsUTF8Text(CurCell^.Row,
CurCell^.Col))
);
if HasFormula(CurCell) then
WriteLn(' Formula: ', MyWorkSheet.ReadFormulaAsString(CurCell))
else
WriteLn;
CurCell := MyWorkSheet.GetNextCell();
end;
// Finalization

View File

@ -69,7 +69,6 @@
<ComponentName Value="MainForm"/>
<HasResources Value="True"/>
<ResourceBaseClass Value="Form"/>
<UnitName Value="main"/>
</Unit1>
</Units>
</ProjectOptions>

View File

@ -7,7 +7,7 @@ uses
cthreads,
{$ENDIF}{$ENDIF}
Interfaces, // this includes the LCL widgetset
Forms, mainfrm, fpsCell
Forms, mainfrm
{ you can add units after this };
{$R *.res}

View File

@ -10,7 +10,7 @@ program ooxmlread;
{$mode delphi}{$H+}
uses
Classes, SysUtils, fpstypes, fpspreadsheet, xlsxooxml; //fpsallformats;
Classes, SysUtils, LazUTF8, fpstypes, fpspreadsheet, xlsxooxml; //fpsallformats;
var
MyWorkbook: TsWorkbook;
@ -45,14 +45,12 @@ begin
WriteLn('Contents of the first worksheet of the file:');
WriteLn('');
cell := MyWorkSheet.GetFirstCell();
for i := 0 to MyWorksheet.GetCellCount - 1 do begin
WriteLn('Row: ', cell^.Row,
' Col: ', cell^.Col, ' Value: ',
UTF8ToAnsi(MyWorkSheet.ReadAsUTF8Text(cell^.Row, cell^.Col))
for cell in MyWorksheet.Cells do
WriteLn(
'Row: ', cell^.Row,
' Col: ', cell^.Col,
' Value: ', UTF8ToConsole(MyWorkSheet.ReadAsUTF8Text(cell^.Row, cell^.Col))
);
cell := MyWorkSheet.GetNextCell();
end;
// Finalization
MyWorkbook.Free;

View File

@ -11,7 +11,7 @@ program opendocread;
{$mode delphi}{$H+}
uses
Classes, SysUtils, fpstypes, fpspreadsheet, fpsallformats;
Classes, SysUtils, LazUTF8, fpstypes, fpspreadsheet, fpsallformats;
var
MyWorkbook: TsWorkbook;
@ -45,14 +45,12 @@ begin
WriteLn('Contents of the first worksheet of the file:');
WriteLn('');
cell := MyWorkSheet.GetFirstCell();
for i := 0 to MyWorksheet.GetCellCount - 1 do begin
WriteLn('Row: ', cell^.Row,
' Col: ', cell^.Col, ' Value: ',
UTF8ToAnsi(MyWorkSheet.ReadAsUTF8Text(cell^.Row, cell^.Col))
for cell in MyWorksheet.Cells do
WriteLn(
'Row: ', cell^.Row,
' Col: ', cell^.Col,
' Value: ', UTF8ToConsole(MyWorkSheet.ReadAsUTF8Text(cell^.Row, cell^.Col))
);
cell := MyWorkSheet.GetNextCell();
end;
// Finalization
MyWorkbook.Free;

View File

@ -10,7 +10,7 @@ program wikitableread;
{$mode delphi}{$H+}
uses
Classes, SysUtils,
Classes, SysUtils, LazUTF8,
fpstypes, fpspreadsheet, wikitable, fpsutils;
var
@ -46,19 +46,16 @@ begin
WriteLn('Contents of the first worksheet of the file:');
WriteLn('');
CurCell := MyWorkSheet.GetFirstCell();
for i := 0 to MyWorksheet.GetCellCount - 1 do
for CurCell in MyWorkSheet.Cells do
begin
Write('Row: ', CurCell^.Row,
' Col: ', CurCell^.Col, ' Value: ',
UTF8ToAnsi(MyWorkSheet.ReadAsUTF8Text(CurCell^.Row,
CurCell^.Col))
);
' Col: ', CurCell^.Col, ' Value: ',
UTF8ToConsole(MyWorkSheet.ReadAsUTF8Text(CurCell^.Row, CurCell^.Col))
);
if HasFormula(CurCell) then
WriteLn(' Formula: ', CurCell^.FormulaValue)
else
WriteLn;
CurCell := MyWorkSheet.GetNextCell();
end;
// Finalization

View File

@ -14,65 +14,52 @@ type
{ TsRowCol }
TsRowCol = record
Row, Col: LongInt;
Row, Col: Cardinal;
end;
PsRowCol = ^TsRowCol;
{ TAVLTreeNodeStack }
TAVLTreeNodeStack = class(TFPList)
public
procedure Push(ANode: TAVLTreeNode);
function Pop: TAVLTreeNode;
end;
{ TsRowColEnumerator }
TsRowColEnumerator = class
protected
FCurrentNode: TAVLTreeNode;
FTree: TsRowColAVLTree;
FStartRow, FEndRow, FStartCol, FEndCol: LongInt;
FStartRow, FEndRow, FStartCol, FEndCol: Cardinal;
FReverse: Boolean;
function GetCurrent: PsRowCol;
public
constructor Create(ATree: TsRowColAVLTree;
AStartRow, AStartCol, AEndRow, AEndCol: LongInt; AReverse: Boolean);
AStartRow, AStartCol, AEndRow, AEndCol: Cardinal; AReverse: Boolean);
function GetEnumerator: TsRowColEnumerator; inline;
function MoveNext: Boolean;
property Current: PsRowCol read GetCurrent;
property StartRow: LongInt read FStartRow;
property EndRow: LongInt read FEndRow;
property StartCol: LongInt read FStartCol;
property EndCol: LongInt read FEndCol;
property StartRow: Cardinal read FStartRow;
property EndRow: Cardinal read FEndRow;
property StartCol: Cardinal read FStartCol;
property EndCol: Cardinal read FEndCol;
end;
{ TsRowColAVLTree }
TsRowColAVLTree = class(TAVLTree)
private
FOwnsData: Boolean;
FCurrentNode: TAVLTreeNode;
FCurrentNodeStack: TAVLTreeNodeStack;
protected
procedure DisposeData(var AData: Pointer); virtual; abstract;
function NewData: Pointer; virtual; abstract;
public
constructor Create(AOwnsData: Boolean = true);
destructor Destroy; override;
function Add(ARow, ACol: LongInt): PsRowCol;
function Add(ARow, ACol: Cardinal): PsRowCol; overload;
procedure Clear;
procedure Delete(ANode: TAVLTreeNode); overload;
procedure Delete(ARow, ACol: LongInt); overload;
procedure DeleteRowOrCol(AIndex: LongInt; IsRow: Boolean); virtual;
procedure Exchange(ARow1, ACol1, ARow2, ACol2: LongInt); virtual;
function Find(ARow, ACol: LongInt): PsRowCol; overload;
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 GetData(ANode: TAVLTreeNode): PsRowCol;
function GetFirst: PsRowCol;
function GetLast: PsRowCol;
function GetNext: PsRowCol;
function GetPrev: PsRowCol;
procedure InsertRowOrCol(AIndex: LongInt; IsRow: Boolean);
procedure Remove(ARow, ACol: LongInt); overload;
procedure PushCurrent;
procedure PopCurrent;
procedure InsertRowOrCol(AIndex: Cardinal; IsRow: Boolean);
procedure Remove(ARow, ACol: Cardinal); overload;
end;
{ TsCells }
@ -92,28 +79,28 @@ type
function NewData: Pointer; override;
public
constructor Create(AWorksheet: Pointer; AOwnsData: Boolean = true);
function AddCell(ARow, ACol: LongInt): PCell;
procedure DeleteCell(ARow, ACol: LongInt);
function FindCell(ARow, ACol: LongInt): PCell;
function AddCell(ARow, ACol: Cardinal): PCell;
procedure DeleteCell(ARow, ACol: Cardinal);
function FindCell(ARow, ACol: Cardinal): PCell;
function GetFirstCell: PCell;
function GetFirstCellOfRow(ARow: Cardinal): PCell;
function GetLastCell: PCell;
function GetNextCell: PCell;
function GetPrevCell: PCell;
function GetLastCellOfRow(ARow: Cardinal): PCell;
// enumerators
function GetEnumerator: TsCellEnumerator;
function GetReverseEnumerator: TsCellEnumerator;
function GetColEnumerator(ACol: LongInt; AStartRow: Longint = 0;
AEndRow: Longint = $7FFFFFFF): TsCellEnumerator;
function GetColEnumerator(ACol: Cardinal; AStartRow: Cardinal = 0;
AEndRow: Cardinal = $7FFFFFFF): TsCellEnumerator;
function GetRangeEnumerator(AStartRow, AStartCol,
AEndRow, AEndCol: Longint): TsCellEnumerator;
function GetRowEnumerator(ARow: LongInt; AStartCol:LongInt = 0;
AEndCol: Longint = $7FFFFFFF): TsCellEnumerator;
function GetReverseColEnumerator(ACol: LongInt; AStartRow: Longint = 0;
AEndRow: Longint = $7FFFFFFF): TsCellEnumerator;
AEndRow, AEndCol: Cardinal): TsCellEnumerator;
function GetRowEnumerator(ARow: Cardinal; AStartCol:Cardinal = 0;
AEndCol: Cardinal = $7FFFFFFF): TsCellEnumerator;
function GetReverseColEnumerator(ACol: Cardinal; AStartRow: Cardinal = 0;
AEndRow: Cardinal = $7FFFFFFF): TsCellEnumerator;
function GetReverseRangeEnumerator(AStartRow, AStartCol,
AEndRow, AEndCol: Longint): TsCellEnumerator;
function GetReverseRowEnumerator(ARow: LongInt; AStartCol:LongInt = 0;
AEndCol: Longint = $7FFFFFFF): TsCellEnumerator;
AEndRow, AEndCol: Cardinal): TsCellEnumerator;
function GetReverseRowEnumerator(ARow: Cardinal; AStartCol:Cardinal = 0;
AEndCol: Cardinal = $7FFFFFFF): TsCellEnumerator;
end;
{ TsComments }
@ -135,7 +122,7 @@ type
// enumerators
function GetEnumerator: TsCommentEnumerator;
function GetRangeEnumerator(AStartRow, AStartCol,
AEndRow, AEndCol: Longint): TsCommentEnumerator;
AEndRow, AEndCol: Cardinal): TsCommentEnumerator;
end;
{ TsHyperlinks }
@ -152,50 +139,52 @@ type
procedure DisposeData(var AData: Pointer); override;
function NewData: Pointer; override;
public
function AddHyperlink(ARow, ACol: Longint; ATarget: String;
function AddHyperlink(ARow, ACol: Cardinal; ATarget: String;
ATooltip: String = ''): PsHyperlink;
procedure DeleteHyperlink(ARow, ACol: Longint);
procedure DeleteHyperlink(ARow, ACol: Cardinal);
// enumerators
function GetEnumerator: TsHyperlinkEnumerator;
function GetRangeEnumerator(AStartRow, AStartCol,
AEndRow, AEndCol: Longint): TsHyperlinkEnumerator;
AEndRow, AEndCol: Cardinal): TsHyperlinkEnumerator;
end;
{ TsMergedCells }
TsCellRangeEnumerator = class(TsRowColEnumerator)
protected
function GetCurrent: PsCellRange;
public
function GetEnumerator: TsCellRangeEnumerator; inline;
property Current: PsCellRange read GetCurrent;
end;
TsMergedCells = class(TsRowColAVLTree)
protected
procedure DisposeData(var AData: Pointer); override;
function NewData: Pointer; override;
public
function AddRange(ARow1, ACol1, ARow2, ACol2: Longint): PsCellRange;
procedure DeleteRange(ARow, ACol: Longint);
procedure DeleteRowOrCol(AIndex: Longint; IsRow: Boolean); override;
procedure Exchange(ARow1, ACol1, ARow2, ACol2: Longint); override;
function FindRangeWithCell(ARow, ACol: Longint): PsCellRange;
function AddRange(ARow1, ACol1, ARow2, ACol2: Cardinal): PsCellRange;
procedure DeleteRange(ARow, ACol: Cardinal);
procedure DeleteRowOrCol(AIndex: Cardinal; IsRow: Boolean); override;
procedure Exchange(ARow1, ACol1, ARow2, ACol2: Cardinal); override;
function FindRangeWithCell(ARow, ACol: Cardinal): PsCellRange;
// enumerators
function GetEnumerator: TsCellRangeEnumerator;
end;
implementation
uses
Math, fpsUtils;
{ Helper function for sorting }
function CompareRowCol(Item1, Item2: Pointer): Integer;
begin
Result := LongInt(PsRowCol(Item1)^.Row) - PsRowCol(Item2)^.Row;
Result := Longint(PsRowCol(Item1)^.Row) - PsRowCol(Item2)^.Row;
if Result = 0 then
Result := LongInt(PsRowCol(Item1)^.Col) - PsRowCol(Item2)^.Col;
end;
function TAVLTreeNodeStack.Pop: TAVLTreeNode;
begin
Result := TAVLTreeNode(Items[Count-1]);
Delete(Count-1);
end;
procedure TAVLTreeNodeStack.Push(ANode: TAVLTreeNode);
begin
Add(ANode);
Result := Longint(PsRowCol(Item1)^.Col) - PsRowCol(Item2)^.Col;
end;
@ -205,7 +194,7 @@ end;
{******************************************************************************}
constructor TsRowColEnumerator.Create(ATree: TsRowColAVLTree;
AStartRow, AStartCol, AEndRow, AEndCol: LongInt; AReverse: Boolean);
AStartRow, AStartCol, AEndRow, AEndCol: Cardinal; AReverse: Boolean);
begin
FTree := ATree;
FReverse := AReverse;
@ -245,7 +234,7 @@ end;
function TsRowColEnumerator.MoveNext: Boolean;
var
r1,c1,r2,c2: LongInt;
r1,c1,r2,c2: Cardinal;
item: TsRowCol;
begin
if FCurrentNode <> nil then begin
@ -268,16 +257,22 @@ begin
end;
end else
begin
if FReverse and (FStartRow = $7FFFFFFF) and (FStartCol = $7FFFFFFF) then
FCurrentNode := FTree.FindHighest
else
if not FReverse and (FStartRow = 0) and (FStartCol = 0) then
FCurrentNode := FTree.FindLowest
else
if FReverse then
begin
item.Row := FStartRow;
item.Col := FStartCol;
FCurrentNode := FTree.Find(@item);
FCurrentNode := FTree.FindHighest;
while (FCurrentNode <> nil) and
( (Current^.Row < FEndRow) or (Current^.Row > FStartRow) or
(Current^.Col < FEndCol) or (Current^.Col > FStartCol) )
do
FCurrentNode := FTree.FindPrecessor(FCurrentNode);
end else
begin
FCurrentNode := FTree.FindLowest;
while (FCurrentNode <> nil) and
( (Current^.Row < FStartRow) or (Current^.Row > FEndRow) or
(Current^.Col < FStartCol) or (Current^.Col > FEndCol) )
do
FCurrentNode := FTree.FindSuccessor(FCurrentNode);
end;
end;
Result := FCurrentNode <> nil;
@ -298,7 +293,6 @@ constructor TsRowColAVLTree.Create(AOwnsData: Boolean = true);
begin
inherited Create(@CompareRowCol);
FOwnsData := AOwnsData;
FCurrentNodeStack := TAVLTreeNodeStack.Create;
end;
{@@ ----------------------------------------------------------------------------
@ -307,7 +301,6 @@ end;
-------------------------------------------------------------------------------}
destructor TsRowColAVLTree.Destroy;
begin
FCurrentNodeStack.Free;
Clear;
inherited;
end;
@ -316,7 +309,7 @@ end;
Adds a new node to the tree identified by the specified row and column
indexes.
-------------------------------------------------------------------------------}
function TsRowColAVLTree.Add(ARow, ACol: LongInt): PsRowCol;
function TsRowColAVLTree.Add(ARow, ACol: Cardinal): PsRowCol;
begin
Result := NewData;
Result^.Row := ARow;
@ -351,7 +344,7 @@ begin
inherited Delete(ANode);
end;
procedure TsRowColAVLTree.Delete(ARow, ACol: LongInt);
procedure TsRowColAVLTree.Delete(ARow, ACol: Cardinal);
var
node: TAVLTreeNode;
cell: TCell;
@ -371,7 +364,7 @@ end;
to be deleted
@param IsRow Identifies whether AIndex refers to a row or column index
-------------------------------------------------------------------------------}
procedure TsRowColAVLTree.DeleteRowOrCol(AIndex: LongInt; IsRow: Boolean);
procedure TsRowColAVLTree.DeleteRowOrCol(AIndex: Cardinal; IsRow: Boolean);
var
node, nextnode: TAVLTreeNode;
item: PsRowCol;
@ -386,7 +379,7 @@ begin
if item^.Row > AIndex then
dec(item^.Row)
else
// Remove the RowCol record if it is in the deleted row
// Remove and destroy the RowCol record if it is in the deleted row
if item^.Row = AIndex then
Delete(node);
end else
@ -406,7 +399,7 @@ end;
{@@ ----------------------------------------------------------------------------
Exchanges two nodes
-------------------------------------------------------------------------------}
procedure TsRowColAVLTree.Exchange(ARow1, ACol1, ARow2, ACol2: LongInt);
procedure TsRowColAVLTree.Exchange(ARow1, ACol1, ARow2, ACol2: Cardinal);
var
item1, item2: PsRowCol;
begin
@ -422,11 +415,11 @@ begin
item1^.Col := ACol2;
item2^.Row := ARow1;
item2^.Col := ACol1;
inherited Add(item1);
inherited Add(item2);
inherited Add(item1); // The items are sorted to the correct position
inherited Add(item2); // when they are added to the tree
end else
// Only the 1tst item exists --> give it the row/col indexes of the 2nd item
// Only the 1st item exists --> give it the row/col indexes of the 2nd item
if (item1 <> nil) then
begin
Remove(item1);
@ -450,7 +443,7 @@ end;
returns a pointer to the data record.
Returns nil if such a node does not exist
-------------------------------------------------------------------------------}
function TsRowColAVLTree.Find(ARow, ACol: LongInt): PsRowCol;
function TsRowColAVLTree.Find(ARow, ACol: Cardinal): PsRowCol;
var
data: TsRowCol;
node: TAVLTreeNode;
@ -476,22 +469,6 @@ begin
else
Result := nil;
end;
(*
function TsRowColAVLTree.GetEnumerator: TsRowColEnumerator;
begin
Result := TsRowColEnumerator.Create(self);
end;
function TsRowColAVLTree.GetColEnumerator(ACol: LongInt): TsRowColEnumerator;
begin
Result := TsRowColEnumerator.Create(self, -1, ACol, -1, ACol);
end;
function TsRowColAVLTree.GetRowEnumerator(ARow: LongInt): TsRowColEnumerator;
begin
Result := TsRowColEnumerator.Create(self, ARow, -1, ARow, -1);
end;
*)
{@@ ----------------------------------------------------------------------------
The combination of the methods GetFirst and GetNext allow a fast iteration
@ -499,28 +476,12 @@ end;
-------------------------------------------------------------------------------}
function TsRowColAVLTree.GetFirst: PsRowCol;
begin
FCurrentNode := FindLowest;
Result := GetData(FCurrentNode);
Result := GetData(FindLowest);
end;
function TsRowColAVLTree.GetLast: PsRowCol;
begin
FCurrentNode := FindHighest;
Result := GetData(FCurrentNode);
end;
function TsRowColAVLTree.GetNext: PsRowCol;
begin
if FCurrentNode <> nil then
FCurrentNode := FindSuccessor(FCurrentNode);
Result := GetData(FCurrentNode);
end;
function TsRowColAVLTree.GetPrev: PsRowCol;
begin
if FCurrentNode <> nil then
FCurrentNode := FindPrecessor(FCurrentNode);
Result := GetData(FCurrentNode);
Result := GetData(FindHighest);
end;
{@@ ----------------------------------------------------------------------------
@ -531,7 +492,7 @@ end;
to be inserted
@param IsRow Identifies whether AIndex refers to a row or column index
-------------------------------------------------------------------------------}
procedure TsRowColAVLTree.InsertRowOrCol(AIndex: LongInt; IsRow: Boolean);
procedure TsRowColAVLTree.InsertRowOrCol(AIndex: Cardinal; IsRow: Boolean);
var
node: TAVLTreeNode;
item: PsRowCol;
@ -553,7 +514,7 @@ end;
{@@ ----------------------------------------------------------------------------
Removes the node, but does NOT destroy the associated data reocrd
-------------------------------------------------------------------------------}
procedure TsRowColAVLTree.Remove(ARow, ACol: LongInt);
procedure TsRowColAVLTree.Remove(ARow, ACol: Cardinal);
var
node: TAVLTreeNode;
item: TsRowCol;
@ -565,16 +526,6 @@ begin
// Delete(node);
end;
procedure TsRowColAVLTree.PopCurrent;
begin
FCurrentNode := FCurrentNodeStack.Pop;
end;
procedure TsRowColAVLTree.PushCurrent;
begin
FCurrentNodeStack.Push(FCurrentNode);
end;
{******************************************************************************}
{ TsCellEnumerator: enumerator for the TsCells AVLTree }
@ -607,7 +558,7 @@ end;
NOTE: It must be checked first that there ia no other record at the same
col/row. (Check omitted for better performance).
-------------------------------------------------------------------------------}
function TsCells.AddCell(ARow, ACol: LongInt): PCell;
function TsCells.AddCell(ARow, ACol: Cardinal): PCell;
begin
Result := PCell(Add(ARow, ACol));
end;
@ -616,7 +567,7 @@ end;
Deletes the node for the specified row and column index along with the
associated cell data record.
-------------------------------------------------------------------------------}
procedure TsCells.DeleteCell(ARow, ACol: LongInt);
procedure TsCells.DeleteCell(ARow, ACol: Cardinal);
begin
Delete(ARow, ACol);
end;
@ -635,7 +586,7 @@ end;
{@@ ----------------------------------------------------------------------------
Checks whether a specific cell already exists
-------------------------------------------------------------------------------}
function TsCells.FindCell(ARow, ACol: Longint): PCell;
function TsCells.FindCell(ARow, ACol: Cardinal): PCell;
begin
Result := PCell(Find(ARow, ACol));
end;
@ -648,28 +599,28 @@ begin
Result := TsCellEnumerator.Create(self, 0, 0, $7FFFFFFF, $7FFFFFFF, false);
end;
function TsCells.GetColEnumerator(ACol: LongInt; AStartRow: Longint = 0;
AEndRow: Longint = $7FFFFFFF): TsCellEnumerator;
function TsCells.GetColEnumerator(ACol: Cardinal; AStartRow: Cardinal = 0;
AEndRow: Cardinal = $7FFFFFFF): TsCellEnumerator;
begin
Result := TsCellEnumerator.Create(Self, AStartRow, ACol, AEndRow, ACol, false);
end;
function TsCells.GetRangeEnumerator(AStartRow, AStartCol,
AEndRow, AEndCol: Longint): TsCellEnumerator;
AEndRow, AEndCol: Cardinal): TsCellEnumerator;
begin
Result := TsCellEnumerator.Create(Self,
AStartRow, AStartCol, AEndRow, AEndCol, false);
end;
function TsCells.GetRowEnumerator(ARow: LongInt; AStartCol: Longint = 0;
AEndCol: LongInt = $7FFFFFFF): TsCellEnumerator;
function TsCells.GetRowEnumerator(ARow: Cardinal; AStartCol: Cardinal = 0;
AEndCol: Cardinal = $7FFFFFFF): TsCellEnumerator;
begin
Result := TsCellEnumerator.Create(Self,
ARow, AStartCol, ARow, AEndCol, false);
end;
function TsCells.GetReverseColEnumerator(ACol: LongInt; AStartRow: Longint = 0;
AEndRow: Longint = $7FFFFFFF): TsCellEnumerator;
function TsCells.GetReverseColEnumerator(ACol: Cardinal; AStartRow: Cardinal = 0;
AEndRow: Cardinal = $7FFFFFFF): TsCellEnumerator;
begin
Result := TsCellEnumerator.Create(Self,
AStartRow, ACol, AEndRow, ACol, true);
@ -681,14 +632,14 @@ begin
end;
function TsCells.GetReverseRangeEnumerator(AStartRow, AStartCol,
AEndRow, AEndCol: Longint): TsCellEnumerator;
AEndRow, AEndCol: Cardinal): TsCellEnumerator;
begin
Result := TsCellEnumerator.Create(Self,
AStartRow, AStartCol, AEndRow, AEndCol, true);
end;
function TsCells.GetReverseRowEnumerator(ARow: LongInt; AStartCol: Longint = 0;
AEndCol: LongInt = $7FFFFFFF): TsCellEnumerator;
function TsCells.GetReverseRowEnumerator(ARow: Cardinal; AStartCol: Cardinal = 0;
AEndCol: Cardinal = $7FFFFFFF): TsCellEnumerator;
begin
Result := TsCellEnumerator.Create(Self,
ARow, AStartCol, ARow, AEndCol, true);
@ -697,9 +648,6 @@ end;
{@@ ----------------------------------------------------------------------------
Returns a pointer to the first cell of the tree.
Should always be followed by GetNextCell.
Use to iterate through all cells efficiently.
-------------------------------------------------------------------------------}
function TsCells.GetFirstCell: PCell;
begin
@ -707,10 +655,18 @@ begin
end;
{@@ ----------------------------------------------------------------------------
Returns a pointer to the last cell of the tree.
Returns a pointer to the first cell in a specified row
-------------------------------------------------------------------------------}
function TsCells.GetFirstCellOfRow(ARow: Cardinal): PCell;
begin
Result := nil;
// Creating the row enumerator automatically finds the first cell of the row
for Result in GetRowEnumerator(ARow) do
exit;
end;
Needed for efficient iteration through all nodes in reverse direction by
calling GetPrev.
{@@ ----------------------------------------------------------------------------
Returns a pointer to the last cell of the tree.
-------------------------------------------------------------------------------}
function TsCells.GetLastCell: PCell;
begin
@ -718,25 +674,14 @@ begin
end;
{@@ ----------------------------------------------------------------------------
After beginning an iteration through all cells with GetFirstCell, the next
available cell can be found by calling GetNextCell.
Use to iterate througt all cells efficiently.
Returns a pointer to the last cell of a specified row
-------------------------------------------------------------------------------}
function TsCells.GetNextCell: PCell;
function TsCells.GetLastCellOfRow(ARow: Cardinal): PCell;
begin
Result := PCell(GetNext);
end;
{@@ ----------------------------------------------------------------------------
After beginning a reverse iteration through all cells with GetLastCell,
the next available cell can be found by calling GetPrevCell.
Use to iterate througt all cells efficiently in reverse order.
-------------------------------------------------------------------------------}
function TsCells.GetPrevCell: PCell;
begin
Result := PCell(GetPrev);
Result := nil;
// Creating the reverse row enumerator finds the last cell of the row
for Result in GetReverseRowEnumerator(ARow) do
exit;
end;
{@@ ----------------------------------------------------------------------------
@ -826,7 +771,7 @@ begin
end;
function TsComments.GetRangeEnumerator(AStartRow, AStartCol,
AEndRow, AEndCol: Longint): TsCommentEnumerator;
AEndRow, AEndCol: Cardinal): TsCommentEnumerator;
begin
Result := TsCommentEnumerator.Create(Self,
AStartRow, AStartCol, AEndRow, AEndCol, false);
@ -847,6 +792,7 @@ begin
Result := PsHyperlink(inherited GetCurrent);
end;
{******************************************************************************}
{ TsHyperlinks: an AVLTree to store hyperlink records for cells }
{******************************************************************************}
@ -856,7 +802,7 @@ end;
exists then its data will be replaced by the specified ones.
Returns a pointer to the hyperlink record.
-------------------------------------------------------------------------------}
function TsHyperlinks.AddHyperlink(ARow, ACol: Longint; ATarget: String;
function TsHyperlinks.AddHyperlink(ARow, ACol: Cardinal; ATarget: String;
ATooltip: String = ''): PsHyperlink;
begin
Result := PsHyperlink(Find(ARow, ACol));
@ -870,7 +816,7 @@ end;
Deletes the node for the specified row and column index along with the
associated hyperlink data record.
-------------------------------------------------------------------------------}
procedure TsHyperlinks.DeleteHyperlink(ARow, ACol: Longint);
procedure TsHyperlinks.DeleteHyperlink(ARow, ACol: Cardinal);
begin
Delete(ARow, ACol);
end;
@ -895,7 +841,7 @@ begin
end;
function TsHyperlinks.GetRangeEnumerator(AStartRow, AStartCol,
AEndRow, AEndCol: Longint): TsHyperlinkEnumerator;
AEndRow, AEndCol: Cardinal): TsHyperlinkEnumerator;
begin
Result := TsHyperlinkEnumerator.Create(Self,
AStartRow, AStartCol, AEndRow, AEndCol, false);
@ -914,7 +860,22 @@ end;
{******************************************************************************}
{ TsMergedCell: a AVLTree to store merged cell range records for cells }
{ TsCellRangeEnumerator: enumerator for the cell range records }
{******************************************************************************}
function TsCellRangeEnumerator.GetEnumerator: TsCellRangeEnumerator;
begin
Result := self;
end;
function TsCellRangeEnumerator.GetCurrent: PsCellRange;
begin
Result := PsCellRange(inherited GetCurrent);
end;
{******************************************************************************}
{ TsMergedCells: a AVLTree to store merged cell range records for cells }
{******************************************************************************}
{@@ ----------------------------------------------------------------------------
@ -922,7 +883,7 @@ end;
exists then its data will be replaced by the specified ones.
Returns a pointer to the cell range record.
-------------------------------------------------------------------------------}
function TsMergedCells.AddRange(ARow1, ACol1, ARow2, ACol2: Longint): PsCellRange;
function TsMergedCells.AddRange(ARow1, ACol1, ARow2, ACol2: Cardinal): PsCellRange;
begin
Result := PsCellRange(Find(ARow1, ACol1));
if Result = nil then
@ -935,7 +896,7 @@ end;
Deletes the node for which the top/left corner of the cell range matches the
specified parameters. There is only a single range fulfilling this criterion.
-------------------------------------------------------------------------------}
procedure TsMergedCells.DeleteRange(ARow, ACol: Longint);
procedure TsMergedCells.DeleteRange(ARow, ACol: Cardinal);
begin
Delete(ARow, ACol);
end;
@ -948,21 +909,25 @@ end;
to be deleted
@param IsRow Identifies whether AIndex refers to a row or column index
-------------------------------------------------------------------------------}
procedure TsMergedCells.DeleteRowOrCol(AIndex: Longint; IsRow: Boolean);
procedure TsMergedCells.DeleteRowOrCol(AIndex: Cardinal; IsRow: Boolean);
var
rng, nextrng: PsCellRange;
rng: PsCellRange;
R: TsCellRange;
node, nextnode: TAVLTreeNode;
begin
rng := PsCellRange(GetFirst);
while Assigned(rng) do begin
nextrng := PsCellRange(GetNext);
node := FindLowest;
while Assigned(node) do begin
rng := PsCellRange(node.Data);
nextnode := FindSuccessor(node);
if IsRow then
begin
// Deleted row is above the merged range --> Shift entire range up by 1
// NOTE:
// The "merged" flags do not have to be changed, they move with the cells.
if (AIndex < rng^.Row1) then begin
dec(rng^.Row1);
dec(rng^.Row2);
R := rng^; // Store range parameters
Delete(node); // Delete node from tree, adapt the row indexes, ...
AddRange(R.Row1-1, R.Col1, R.Row2-1, R.Col2); // ... and re-insert to get it sorted correctly
end else
// Single-row merged block coincides with row to be deleted
if (AIndex = rng^.Row1) and (rng^.Row1 = rng^.Row2) then
@ -971,7 +936,7 @@ begin
// Deleted row runs through the merged block --> Shift bottom row up by 1
// NOTE: The "merged" flags disappear with the deleted cells
if (AIndex >= rng^.Row1) and (AIndex <= rng^.Row2) then
dec(rng^.Row2);
dec(rng^.Row2); // no need to remove & re-insert because Row1 does not change
end else
begin
// Deleted column is at the left of the merged range
@ -979,8 +944,9 @@ begin
// NOTE:
// The "merged" flags do not have to be changed, they move with the cells.
if (AIndex < rng^.Col1) then begin
dec(rng^.Col1);
dec(rng^.Col2);
R := rng^;
Delete(node);
AddRange(R.Row1, R.Col1-1, R.Row2, R.Col2-1);
end else
// Single-column block coincides with the column to be deleted
// NOTE: The "merged" flags disappear with the deleted cells
@ -993,7 +959,7 @@ begin
dec(rng^.Col2);
end;
// Proceed with next merged range
rng := nextrng;
node := nextnode;
end;
end;
@ -1008,10 +974,10 @@ begin
AData := nil;
end;
procedure TsMergedCells.Exchange(ARow1, ACol1, ARow2, ACol2: Longint);
procedure TsMergedCells.Exchange(ARow1, ACol1, ARow2, ACol2: Cardinal);
var
rng: PsCellrange;
dr, dc: LongInt;
dr, dc: Cardinal;
begin
rng := PsCellrange(Find(ARow1, ACol1));
if rng <> nil then
@ -1042,7 +1008,7 @@ end;
Finds the cell range which contains the cell specified by its row and column
index
-------------------------------------------------------------------------------}
function TsMergedCells.FindRangeWithCell(ARow, ACol: Longint): PsCellRange;
function TsMergedCells.FindRangeWithCell(ARow, ACol: Cardinal): PsCellRange;
var
node: TAVLTreeNode;
begin
@ -1056,6 +1022,14 @@ begin
end;
end;
{@@ ----------------------------------------------------------------------------
Cell range enumerator (use in "for ... in" syntax)
-------------------------------------------------------------------------------}
function TsMergedCells.GetEnumerator: TsCellRangeEnumerator;
begin
Result := TsCellRangeEnumerator.Create(self, 0, 0, $7FFFFFFF, $7FFFFFFF, false);
end;
{@@ ----------------------------------------------------------------------------
Alloates memory of a merged cell range data record.
-------------------------------------------------------------------------------}

View File

@ -604,7 +604,7 @@ procedure TsCSVWriter.WriteSheet(AStream: TStream; AWorksheet: TsWorksheet);
var
r, c: Cardinal;
LastRow, LastCol: Cardinal;
Cell: PCell;
cell: PCell;
begin
FWorksheet := AWorksheet;
@ -618,6 +618,12 @@ begin
LastRow := FWorksheet.GetLastOccupiedRowIndex;
LastCol := FWorksheet.GetLastOccupiedColIndex;
for r := 0 to LastRow do
begin
for cell in FWorksheet.Cells.GetRowEnumerator(r) do
WriteCellToStream(AStream, cell);
FCSVBuilder.AppendRow;
end;
{
for c := 0 to LastCol do
begin
Cell := FWorksheet.FindCell(r, c);
@ -626,6 +632,7 @@ begin
if c = LastCol then
FCSVBuilder.AppendRow;
end;
}
finally
FreeAndNil(FCSVBuilder);
end;

View File

@ -3226,12 +3226,12 @@ begin
end
else
// Look for empty rows with the same style, they need the "number-rows-repeated" element.
if (ASheet.GetFirstCellOfRow(r) = nil) then
if (ASheet.Cells.GetFirstCellOfRow(r) = nil) then
begin
rr := r + 1;
while (rr <= lastRow) do
begin
if ASheet.GetFirstCellOfRow(rr) <> nil then
if ASheet.Cells.GetFirstCellOfRow(rr) <> nil then
break;
h1 := ASheet.GetRowHeight(rr);
if not SameValue(h, h1, ROWHEIGHT_EPS) then
@ -3295,7 +3295,8 @@ begin
AppendToStream(AStream, Format(
'<table:table-cell %s/>', [colsRepeatedStr]));
end else
WriteCellCallback(cell, AStream);
WriteCellToStream(AStream, cell);
// WriteCellCallback(cell, AStream);
inc(c, colsRepeated);
end;
@ -3980,7 +3981,8 @@ begin
lCell.BoolValue := value <> 0;
end else
lCell.ContentType := cctEmpty;
WriteCellCallback(@lCell, AStream);
WriteCellToStream(AStream, @lCell);
// WriteCellCallback(@lCell, AStream);
end;
inc(c, colsRepeated);
end;

View File

@ -363,8 +363,8 @@ type
function GetCellCount: Cardinal;
function GetFirstCellOfRow(ARow: Cardinal): PCell;
function GetLastCellOfRow(ARow: Cardinal): PCell;
// function GetFirstCellOfRow(ARow: Cardinal): PCell;
// function GetLastCellOfRow(ARow: Cardinal): PCell;
function GetFirstColIndex(AForceCalculation: Boolean = false): Cardinal;
function GetLastColIndex(AForceCalculation: Boolean = false): Cardinal;
function GetLastColNumber: Cardinal; deprecated 'Use GetLastColIndex';
@ -1284,38 +1284,33 @@ var
i: Integer;
rpnFormula: TsRPNFormula;
begin
FCells.PushCurrent;
try
cell := FCells.GetFirstCell;
while Assigned(cell) do begin
if HasFormula(cell) then begin
rpnFormula := BuildRPNFormula(cell);
for i := 0 to Length(rpnFormula)-1 do
begin
fe := rpnFormula[i];
case fe.ElementKind of
fekCell, fekCellRef:
if (fe.Row = ARow) and (fe.Col = ACol) then
begin
Result := true;
exit;
end;
fekCellRange:
if (fe.Row <= ARow) and (ARow <= fe.Row2) and
(fe.Col <= ACol) and (ACol <= fe.Col2) then
begin
Result := true;
exit;
end;
end;
for cell in FCells do
begin
if HasFormula(cell) then begin
rpnFormula := BuildRPNFormula(cell);
for i := 0 to Length(rpnFormula)-1 do
begin
fe := rpnFormula[i];
case fe.ElementKind of
fekCell, fekCellRef:
if (fe.Row = ARow) and (fe.Col = ACol) then
begin
Result := true;
exit;
end;
fekCellRange:
if (fe.Row <= ARow) and (ARow <= fe.Row2) and
(fe.Col <= ACol) and (ACol <= fe.Col2) then
begin
Result := true;
exit;
end;
end;
end;
cell := FCells.GetNextCell;
end;
finally
FCells.PopCurrent;
end;
SetLength(rpnFormula, 0);
Result := false;
end;
{@@ ----------------------------------------------------------------------------
@ -2184,25 +2179,9 @@ var
begin
if AForceCalculation then
begin
Result := $FFFFFFFF;
Result := Cardinal(-1);
for cell in FCells do
Result := Math.Min(Result, cell^.Col);
(*
// Traverse the tree from lowest to highest.
// Since tree primary sort order is on row lowest col could exist anywhere.
FCells.PushCurrent;
try
cell := FCells.GetFirstCell;
while Assigned(cell) do
begin
Result := Math.Min(Result, cell^.Col);
cell := FCells.GetNextCell;
end;
finally
FCells.PopCurrent;
end;
*)
// In addition, there may be column records defining the column width even
// without content
for i:=0 to FCols.Count-1 do
@ -2214,14 +2193,14 @@ begin
else
begin
Result := FFirstColIndex;
if Result = $FFFFFFFF then
if Result = cardinal(-1) then
Result := GetFirstColIndex(true);
end;
end;
{@@ ----------------------------------------------------------------------------
Returns the 0-based index of the last column with a cell with contents or
with a column record.
Returns the 0-based index of the last column containing a cell with a
column record (due to content or formatting), or containing a Col record.
If no cells have contents or there are no column records, zero will be
returned, which is also a valid value.
@ -2243,11 +2222,10 @@ begin
if AForceCalculation then
begin
// Traverse the tree from lowest to highest.
// Since tree primary sort order is on row
// highest col could exist anywhere.
// Since tree primary sort order is on row highest col could exist anywhere.
Result := GetLastOccupiedColIndex;
// In addition, there may be column records defining the column width even
// without content
// without cells
for i:=0 to FCols.Count-1 do
if FCols[i] <> nil then
Result := Math.Max(Result, PCol(FCols[i])^.Col);
@ -2287,21 +2265,8 @@ begin
// Since tree's primary sort order is on row, highest col could exist anywhere.
for cell in FCells do
Result := Math.Max(Result, cell^.Col);
{
FCells.PushCurrent;
try
cell := FCells.GetFirstCell;
while Assigned(cell) do
begin
Result := Math.Max(Result, cell^.Col);
cell := FCells.GetNextCell;
end;
finally
FCells.PopCurrent;
end;
}
end;
(*
{@@ ----------------------------------------------------------------------------
Finds the first cell with contents in a given row
@ -2309,17 +2274,8 @@ end;
@return Pointer to the first cell in this row, or nil if the row is empty.
-------------------------------------------------------------------------------}
function TsWorksheet.GetFirstCellOfRow(ARow: Cardinal): PCell;
var
c, n: Cardinal;
begin
n := GetLastColIndex;
c := 0;
Result := FindCell(ARow, c);
while (result = nil) and (c < n) do
begin
inc(c);
result := FindCell(ARow, c);
end;
Result := FCells.GetFirstCellOfRow(ARow);
end;
{@@ ----------------------------------------------------------------------------
@ -2329,19 +2285,10 @@ end;
@return Pointer to the last cell in this row, or nil if the row is empty.
-------------------------------------------------------------------------------}
function TsWorksheet.GetLastCellOfRow(ARow: Cardinal): PCell;
var
c, n: Cardinal;
begin
n := GetLastColIndex;
c := n;
Result := FindCell(ARow, c);
while (Result = nil) and (c > 0) do
begin
dec(c);
Result := FindCell(ARow, c);
end;
Result := FCells.GetLastCellOfRow(ARow);
end;
*)
{@@ ----------------------------------------------------------------------------
Returns the 0-based index of the first row with a cell with data or formatting.
If no cells have contents, -1 will be returned.
@ -2360,14 +2307,8 @@ begin
if AForceCalculation then
begin
Result := $FFFFFFFF;
FCells.PushCurrent;
try
cell := FCells.GetFirstCell;
finally
FCells.PopCurrent;
end;
if Assigned(cell) then
Result := cell^.Row;
cell := FCells.GetFirstCell;
if cell <> nil then Result := cell^.Row;
// In addition, there may be row records even for rows without cells.
for i:=0 to FRows.Count-1 do
if FRows[i] <> nil then
@ -2378,13 +2319,14 @@ begin
else
begin
Result := FFirstRowIndex;
if Result = $FFFFFFFF then
if Result = Cardinal(-1) then
Result := GetFirstRowIndex(true);
end;
end;
{@@ ----------------------------------------------------------------------------
Returns the 0-based index of the last row with a cell with contents.
Returns the 0-based index of the last row with a cell with contents or with
a ROW record.
If no cells have contents, zero will be returned, which is also a valid value.
@ -3125,13 +3067,8 @@ begin
// ... yes: --> modify the merged range accordingly
begin
// unmark previously merged range
for r := rng^.Row1 to rng^.Row2 do
for c := rng^.Col1 to rng^.Col2 do
begin
cell := FindCell(r, c);
if cell <> nil then // nil happens when col/row is inserted...
Exclude(cell^.Flags, cfMerged);
end;
for cell in Cells.GetRangeEnumerator(rng^.Row1, rng^.Col1, rng^.Row2, rng^.Col2) do
Exclude(cell^.Flags, cfMerged);
// Define new limits of merged range
rng^.Row2 := ARow2;
rng^.Col2 := ACol2;
@ -3180,13 +3117,10 @@ begin
if rng <> nil then
begin
// Remove the "merged" flag from the cells in the merged range to make them
// isolated again.
for r := rng^.Row1 to rng^.Row2 do
for c := rng^.Col1 to rng^.Col2 do
begin
cell := FindCell(r, c);
Exclude(cell^.Flags, cfMerged);
end;
// isolated again...
for cell in Cells.GetRangeEnumerator(rng^.Row1, rng^.Col1, rng^.Row2, rng^.Col2) do
Exclude(cell^.Flags, cfMerged);
// ... and delete the range
FMergedCells.DeleteRange(rng^.Row1, rng^.Col1);
end;
@ -3363,8 +3297,12 @@ procedure TsWorksheet.FixSharedFormulas;
var
r,c, r1,c1, r2,c2: Cardinal;
cell: PCell;
firstRow, firstCol, lastRow, lastCol: Cardinal;
// firstRow, firstCol, lastRow, lastCol: Cardinal;
begin
for cell in Cells do
if FindSharedFormulaRange(cell, r1, c1, r2, c2) and (r1 = r2) and (c1 = c2) then
cell^.SharedFormulaBase := nil;
{
firstRow := GetFirstRowIndex;
firstCol := GetFirstColIndex;
lastRow := GetLastOccupiedRowIndex;
@ -3376,6 +3314,7 @@ begin
if FindSharedFormulaRange(cell, r1, c1, r2, c2) and (r1 = r2) and (c1 = c2) then
cell^.SharedFormulaBase := nil;
end;
}
end;
{@@ ----------------------------------------------------------------------------
@ -3678,10 +3617,14 @@ procedure TsWorksheet.Sort(const ASortParams: TsSortParams;
function ContainsMergedCells: boolean;
var
r,c: Cardinal;
//r,c: Cardinal;
cell: PCell;
begin
result := false;
for cell in Cells.GetRangeEnumerator(ARowFrom, AColFrom, ARowTo, AColTo) do
if IsMerged(cell) then
exit(true);
{
for r := ARowFrom to ARowTo do
for c := AColFrom to AColTo do
begin
@ -3689,6 +3632,7 @@ procedure TsWorksheet.Sort(const ASortParams: TsSortParams;
if IsMerged(cell) then
exit(true);
end;
}
end;
begin
@ -5749,11 +5693,15 @@ var
begin
Result := 0;
h0 := Workbook.GetDefaultFontSize;
for cell in Cells.GetRowEnumerator(ARow) do
Result := Max(Result, ReadCellFont(cell).Size / h0);
{
for col := GetFirstColIndex to GetLastColIndex do begin
cell := FindCell(ARow, col);
if cell <> nil then
Result := Max(Result, ReadCellFont(cell).Size / h0);
end;
}
end;
{@@ ----------------------------------------------------------------------------
@ -5993,16 +5941,8 @@ begin
RemoveAndFreeCell(r, ACol);
// Update column index of cell records
FCells.PushCurrent;
try
cell := FCells.GetFirstCell;
while Assigned(cell) do begin
DeleteColCallback(cell, {%H-}pointer(PtrInt(ACol)));
cell := FCells.GetNextCell;
end;
finally
FCells.PopCurrent;
end;
for cell in FCells do
DeleteColCallback(cell, {%H-}pointer(PtrInt(ACol)));
// Update column index of col records
for i:=FCols.Count-1 downto 0 do begin
@ -6080,11 +6020,8 @@ begin
RemoveAndFreeCell(ARow, c);
// Update row index of cell records
cell := FCells.GetFirstCell;
while Assigned(cell) do begin
for cell in FCells do
DeleteRowCallback(cell, {%H-}pointer(PtrInt(ARow)));
cell := FCells.GetNextCell;
end;
// Update row index of row records
for i:=FRows.Count-1 downto 0 do
@ -6119,11 +6056,8 @@ var
begin
// Handling of shared formula references is too complicated for me...
// Split them into isolated cell formulas
cell := FCells.GetFirstCell;
while Assigned(cell) do begin
for cell in FCells do
SplitSharedFormula(cell);
cell := FCells.GetNextCell;
end;
// Update column index of comments
FComments.InsertRowOrCol(ACol, false);
@ -6132,11 +6066,8 @@ begin
FHyperlinks.InsertRowOrCol(ACol, false);
// Update column index of cell records
cell := FCells.GetFirstCell;
while Assigned(cell) do begin
for cell in FCells do
InsertColCallback(cell, {%H-}pointer(PtrInt(ACol)));
cell := FCells.GetNextCell;
end;
// Update column index of column records
for i:=0 to FCols.Count-1 do begin
@ -6148,39 +6079,48 @@ begin
UpdateCaches;
// Fix merged cells
rng := PsCellRange(FMergedCells.GetFirst);
while rng <> nil do
for rng in FMergedCells do
// rng := PsCellRange(FMergedCells.GetFirst);
// while rng <> nil do
begin
// The new column is at the LEFT of the merged block
// --> Shift entire range to the right by 1 column
if (ACol < rng^.Col1) then
begin
// The former first column is no longer marged --> un-tag its cells
for cell in Cells.GetColEnumerator(rng^.Col1, rng^.Row1, rng^.Row2) do
Exclude(cell^.Flags, cfMerged);
{
for r := rng^.Row1 to rng^.Row2 do
begin
cell := FindCell(r, rng^.Col1);
if cell <> nil then
Exclude(cell^.Flags, cfMerged);
end;
}
// Shift merged block to the right
// Don't call "MergeCells" here - this would add a new merged block
// because of the new merge base! --> infinite loop!
inc(rng^.Col1);
inc(rng^.Col2);
// The right column needs to be tagged
for cell in Cells.GetColEnumerator(rng^.Col2, rng^.Row1, rng^.Row2) do
Include(cell^.Flags, cfMerged);
{
for r := rng^.Row1 to rng^.Row2 do
begin
cell := FindCell(R, rng^.Col2);
if cell <> nil then
Include(cell^.Flags, cfMerged);
end;
}
end else
// The new column goes through this cell block --> Shift only the right
// column of the range to the right by 1
if (ACol >= rng^.Col1) and (ACol <= rng^.Col2) then
MergeCells(rng^.Row1, rng^.Col1, rng^.Row2, rng^.Col2+1);
// Continue with next merged block
rng := PsCellRange(FMergedCells.GetNext);
// rng := PsCellRange(FMergedCells.GetNext);
end;
ChangedCell(0, ACol);
@ -6242,11 +6182,8 @@ var
begin
// Handling of shared formula references is too complicated for me...
// Splits them into isolated cell formulas
cell := FCells.GetFirstCell;
while Assigned(cell) do begin
for cell in FCells do
SplitSharedFormula(cell);
cell := FCells.GetNextCell;
end;
// Update row index of cell comments
FComments.InsertRowOrCol(ARow, true);
@ -6255,11 +6192,8 @@ begin
FHyperlinks.InsertRowOrCol(ARow, true);
// Update row index of cell records
cell := FCells.GetFirstCell;
while Assigned(cell) do begin
for cell in FCells do
InsertRowCallback(cell, {%H-}pointer(PtrInt(ARow)));
cell := FCells.GetNextCell;
end;
// Update row index of row records
for i:=0 to FRows.Count-1 do begin
@ -6271,38 +6205,47 @@ begin
UpdateCaches;
// Fix merged cells
rng := PsCellRange(FMergedCells.GetFirst);
while rng <> nil do
for rng in FMergedCells do
// rng := PsCellRange(FMergedCells.GetFirst);
// while rng <> nil do
begin
// The new row is ABOVE the merged block --> Shift entire range down by 1 row
if (ARow < rng^.Row1) then
begin
// The formerly first row is no longer merged --> un-tag its cells
for cell in Cells.GetRowEnumerator(rng^.Row1, rng^.Col1, rng^.Col2) do
Exclude(cell^.Flags, cfMerged);
{
for c := rng^.Col1 to rng^.Col2 do
begin
cell := FindCell(rng^.Row1, c);
if cell <> nil then
Exclude(cell^.Flags, cfMerged);
end;
}
// Shift merged block down
// (Don't call "MergeCells" here - this would add a new merged block
// because of the new merge base! --> infinite loop!)
inc(rng^.Row1);
inc(rng^.Row2);
// The last row needs to be tagged
for cell in Cells.GetRowEnumerator(rng^.Row2, rng^.Col1, rng^.Col2) do
Include(cell^.Flags, cfMerged);
{
for c := rng^.Col1 to rng^.Col2 do
begin
cell := FindCell(rng^.Row2, c);
if cell <> nil then
Include(cell^.Flags, cfMerged);
end;
}
end else
// The new row goes through this cell block --> Shift only the bottom row
// of the range down by 1
if (ARow >= rng^.Row1) and (ARow <= rng^.Row2) then
MergeCells(rng^.Row1, rng^.Col1, rng^.Row2+1, rng^.Col2);
// Continue with next block
rng := PsCellRange(FMergedCells.GetNext);
// rng := PsCellRange(FMergedCells.GetNext);
end;
ChangedCell(ARow, 0);
@ -8080,8 +8023,7 @@ begin
for i:=0 to GetWorksheetCount-1 do
begin
sheet := GetWorksheetByIndex(i);
cell := sheet.Cells.GetFirstCell;
while Assigned(cell) do
for cell in sheet.Cells do
begin
fmt := GetPointerToCellFormat(cell^.FormatIndex);
if (uffBackground in fmt^.UsedFormattingFields) then
@ -8099,7 +8041,6 @@ begin
if fnt.Color = AColorIndex then
exit;
end;
cell := sheet.Cells.GetNextCell;
end;
end;
Result := false;

View File

@ -75,9 +75,9 @@ type
function CalcRowHeight(AHeight: Single): Integer;
procedure ChangedCellHandler(ASender: TObject; ARow, ACol: Cardinal);
procedure ChangedFontHandler(ASender: TObject; ARow, ACol: Cardinal);
procedure FixNeighborCellBorders(ACol, ARow: Integer);
procedure FixNeighborCellBorders(ACell: PCell);
function GetBorderStyle(ACol, ARow, ADeltaCol, ADeltaRow: Integer;
out ABorderStyle: TsCellBorderStyle): Boolean;
ACell: PCell; out ABorderStyle: TsCellBorderStyle): Boolean;
// Setter/Getter
function GetBackgroundColor(ACol, ARow: Integer): TsColor;
@ -155,7 +155,7 @@ type
procedure DoPrepareCanvas(ACol, ARow: Integer; AState: TGridDrawState); override;
procedure DrawAllRows; override;
procedure DrawCellBorders; overload;
procedure DrawCellBorders(ACol, ARow: Integer; ARect: TRect); overload;
procedure DrawCellBorders(ACol, ARow: Integer; ARect: TRect; ACell: PCell); overload;
procedure DrawCellGrid(ACol,ARow: Integer; ARect: TRect; AState: TGridDrawState); override;
procedure DrawCommentMarker(ARect: TRect);
procedure DrawFocusRect(aCol,aRow:Integer; ARect:TRect); override;
@ -1502,7 +1502,7 @@ begin
c := GetGridCol(cell^.Col);
r := GetGridRow(cell^.Row);
rect := CellRect(c, r);
DrawCellBorders(c, r, rect);
DrawCellBorders(c, r, rect, cell);
end;
end;
end;
@ -1517,7 +1517,8 @@ end;
@param ARow Row index
@param ARect Rectangle in pixels occupied by the cell.
-------------------------------------------------------------------------------}
procedure TsCustomWorksheetGrid.DrawCellBorders(ACol, ARow: Integer; ARect: TRect);
procedure TsCustomWorksheetGrid.DrawCellBorders(ACol, ARow: Integer;
ARect: TRect; ACell: PCell);
const
drawHor = 0;
drawVert = 1;
@ -1635,26 +1636,24 @@ const
var
bs: TsCellBorderStyle;
cell: PCell;
fmt: PsCellFormat;
begin
if Assigned(Worksheet) then begin
// Left border
if GetBorderStyle(ACol, ARow, -1, 0, bs) then
if GetBorderStyle(ACol, ARow, -1, 0, ACell, bs) then
DrawBorderLine(ARect.Left-1, ARect, drawVert, bs);
// Right border
if GetBorderStyle(ACol, ARow, +1, 0, bs) then
if GetBorderStyle(ACol, ARow, +1, 0, ACell, bs) then
DrawBorderLine(ARect.Right-1, ARect, drawVert, bs);
// Top border
if GetBorderstyle(ACol, ARow, 0, -1, bs) then
if GetBorderstyle(ACol, ARow, 0, -1, ACell, bs) then
DrawBorderLine(ARect.Top-1, ARect, drawHor, bs);
// Bottom border
if GetBorderStyle(ACol, ARow, 0, +1, bs) then
if GetBorderStyle(ACol, ARow, 0, +1, ACell, bs) then
DrawBorderLine(ARect.Bottom-1, ARect, drawHor, bs);
cell := Worksheet.FindCell(ARow-FHeaderCount, ACol-FHeaderCount);
if cell <> nil then begin
fmt := Workbook.GetPointerToCellFormat(cell^.FormatIndex);
if ACell <> nil then begin
fmt := Workbook.GetPointerToCellFormat(ACell^.FormatIndex);
// Diagonal up
if cbDiagUp in fmt^.Border then begin
bs := fmt^.Borderstyles[cbDiagUp];
@ -2208,12 +2207,12 @@ end;
Copies the borders of a cell to its neighbors. This avoids the nightmare of
changing borders due to border conflicts of adjacent cells.
@param ACol Grid column index of the cell
@param ARow Grid row index of the cell
@param ACell Pointer to the cell
-------------------------------------------------------------------------------}
procedure TsCustomWorksheetGrid.FixNeighborCellBorders(ACol, ARow: Integer);
procedure TsCustomWorksheetGrid.FixNeighborCellBorders(ACell: PCell);
//Col, ARow: Integer);
procedure SetNeighborBorder(NewRow, NewCol: Integer;
procedure SetNeighborBorder(NewRow, NewCol: Cardinal;
ANewBorder: TsCellBorder; const ANewBorderStyle: TsCellBorderStyle;
AInclude: Boolean);
var
@ -2235,17 +2234,16 @@ procedure TsCustomWorksheetGrid.FixNeighborCellBorders(ACol, ARow: Integer);
end;
var
cell: PCell;
fmt: PsCellFormat;
begin
if Worksheet = nil then
exit;
cell := Worksheet.FindCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
if (Worksheet <> nil) and (cell <> nil) then
with cell^ do
// cell := Worksheet.FindCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
if (Worksheet <> nil) and (ACell <> nil) then
with ACell^ do
begin
fmt := Workbook.GetPointerToCellFormat(cell^.FormatIndex);
fmt := Workbook.GetPointerToCellFormat(ACell^.FormatIndex);
SetNeighborBorder(Row, Col-1, cbEast, fmt^.BorderStyles[cbWest], cbWest in fmt^.Border);
SetNeighborBorder(Row, Col+1, cbWest, fmt^.BorderStyles[cbEast], cbEast in fmt^.Border);
SetNeighborBorder(Row-1, Col, cbSouth, fmt^.BorderStyles[cbNorth], cbNorth in fmt^.Border);
@ -2541,12 +2539,30 @@ end;
-------------------------------------------------------------------------------}
function TsCustomWorksheetGrid.GetCellFonts(ARect: TGridRect): TFont;
var
c, r: Integer;
// c, r: Integer;
r1,c1,r2,c2: Cardinal;
sFont, sDefFont: TsFont;
cell: PCell;
begin
Result := GetCellFont(ARect.Left, ARect.Top);
sDefFont := Workbook.GetDefaultFont; // Default font
r1 := GetWorksheetRow(ARect.Top);
c1 := GetWorksheetCol(ARect.Left);
r2 := GetWorksheetRow(ARect.Bottom);
c2 := GetWorksheetRow(ARect.Right);
for cell in Worksheet.Cells.GetRangeEnumerator(r1, c1, r2, c2) do
begin
sFont := Worksheet.ReadCellFont(cell);
if (sFont.FontName <> sDefFont.FontName) and (sFont.Size <> sDefFont.Size)
and (sFont.Style <> sDefFont.Style) and (sFont.Color <> sDefFont.Color)
then
begin
Convert_sFont_to_Font(sDefFont, FCellFont);
Result := FCellFont;
exit;
end;
end;
{
for c := ARect.Left to ARect.Right do
for r := ARect.Top to ARect.Bottom do
begin
@ -2564,6 +2580,7 @@ begin
end;
end;
end;
}
end;
{@@ ----------------------------------------------------------------------------
@ -2798,11 +2815,12 @@ end;
Result is FALSE if there is no border line.
-------------------------------------------------------------------------------}
function TsCustomWorksheetGrid.GetBorderStyle(ACol, ARow, ADeltaCol, ADeltaRow: Integer;
out ABorderStyle: TsCellBorderStyle): Boolean;
ACell: PCell; out ABorderStyle: TsCellBorderStyle): Boolean;
var
cell, neighborcell: PCell;
//cell,
neighborcell: PCell;
border, neighborborder: TsCellBorder;
r, c: Cardinal;
// r, c: Cardinal;
begin
Result := true;
if (ADeltaCol = -1) and (ADeltaRow = 0) then
@ -2827,42 +2845,42 @@ begin
end else
raise Exception.Create('[TsCustomWorksheetGrid] Incorrect col/row for GetBorderStyle.');
r := GetWorksheetRow(ARow);
c := GetWorksheetCol(ACol);
cell := Worksheet.FindCell(r, c);
// r := GetWorksheetRow(ARow);
// c := GetWorksheetCol(ACol);
//cell := Worksheet.FindCell(r, c);
if (ARow - FHeaderCount + ADeltaRow < 0) or (ACol - FHeaderCount + ADeltaCol < 0) then
neighborcell := nil
else
neighborcell := Worksheet.FindCell(ARow - FHeaderCount + ADeltaRow, ACol - FHeaderCount + ADeltaCol);
// Only cell has border, but neighbor has not
if HasBorder(cell, border) and not HasBorder(neighborCell, neighborBorder) then
if HasBorder(ACell, border) and not HasBorder(neighborCell, neighborBorder) then
begin
if Worksheet.InSameMergedRange(cell, neighborcell) then
if Worksheet.InSameMergedRange(ACell, neighborcell) then
result := false
else
ABorderStyle := Worksheet.ReadCellBorderStyle(cell, border)
ABorderStyle := Worksheet.ReadCellBorderStyle(ACell, border)
end
else
// Only neighbor has border, cell has not
if not HasBorder(cell, border) and HasBorder(neighborCell, neighborBorder) then
if not HasBorder(ACell, border) and HasBorder(neighborCell, neighborBorder) then
begin
if Worksheet.InSameMergedRange(cell, neighborcell) then
if Worksheet.InSameMergedRange(ACell, neighborcell) then
result := false
else
ABorderStyle := Worksheet.ReadCellBorderStyle(neighborcell, neighborborder);
end
else
// Both cells have shared border -> use top or left border
if HasBorder(cell, border) and HasBorder(neighborCell, neighborBorder) then
if HasBorder(ACell, border) and HasBorder(neighborCell, neighborBorder) then
begin
if Worksheet.InSameMergedRange(cell, neighborcell) then
if Worksheet.InSameMergedRange(ACell, neighborcell) then
result := false
else
if (border in [cbNorth, cbWest]) then
ABorderStyle := Worksheet.ReadCellBorderStyle(neighborcell, neighborborder)
else
ABorderStyle := Worksheet.ReadCellBorderStyle(cell, border); //cell^.BorderStyles[border];
ABorderStyle := Worksheet.ReadCellBorderStyle(ACell, border);
end else
Result := false;
end;
@ -4279,7 +4297,7 @@ begin
try
cell := Worksheet.GetCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
Worksheet.WriteBorders(cell, AValue);
FixNeighborCellBorders(ACol, ARow);
FixNeighborCellBorders(cell);
finally
EndUpdate;
end;
@ -4311,7 +4329,7 @@ begin
try
cell := Worksheet.GetCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol));
Worksheet.WriteBorderStyle(cell, ABorder, AValue);
FixNeighborCellBorders(ACol, ARow);
FixNeighborCellBorders(cell);
finally
EndUpdate;
end;

View File

@ -100,7 +100,7 @@ type
procedure ListAllNumFormats; virtual;
{ Helpers for writing }
procedure WriteCellCallback(ACell: PCell; AStream: TStream);
procedure WriteCellToStream(AStream: TStream; ACell: PCell);
procedure WriteCellsToStream(AStream: TStream; ACells: TsCells);
{ Record writing methods }
@ -124,12 +124,14 @@ type
constructor Create(AWorkbook: TsWorkbook); override;
destructor Destroy; override;
{ General writing methods }
{
procedure IterateThroughCells(AStream: TStream; ACells: TsCells;
ACallback: TCellsCallback);
procedure IterateThroughComments(AStream: TStream; AComments: TsComments;
ACallback: TCommentsCallback);
procedure IterateThroughHyperlinks(AStream: TStream; AHyperlinks: TsHyperlinks;
ACallback: THyperlinksCallback);
}
procedure WriteToFile(const AFileName: string;
const AOverwriteExisting: Boolean = False); override;
procedure WriteToStream(AStream: TStream); override;
@ -460,7 +462,7 @@ begin
if ALastRow >= Limitations.MaxRowCount then
ALastRow := Limitations.MaxRowCount-1;
end;
(*
{@@ ----------------------------------------------------------------------------
A generic method to iterate through all cells in a worksheet and call a callback
routine for each cell.
@ -512,17 +514,10 @@ var
comment: PsComment;
begin
index := 0;
AComments.PushCurrent;
try
comment := PsComment(AComments.GetFirst);
while Assigned(comment) do
begin
ACallback(comment, index, AStream);
comment := PsComment(AComments.GetNext);
inc(index);
end;
finally
AComments.PopCurrent;
for comment in AComments do
begin
ACallback(comment, index, AStream);
inc(index);
end;
end;
@ -552,7 +547,7 @@ begin
AHyperlinks.PopCurrent;
end;
end;
*)
{@@ ----------------------------------------------------------------------------
Iterates through all cells and collects the number formats in
FNumFormatList (without duplicates).
@ -577,12 +572,12 @@ end;
stream. Calls the WriteNumber method of the worksheet for writing a number,
the WriteDateTime method for writing a date/time etc.
@param ACell Pointer to the worksheet cell being written
@param AStream Stream to which data are written
@param ACell Pointer to the worksheet cell being written
@param AStream Stream to which data are written
@see TsCustomSpreadWriter.WriteCellsToStream
-------------------------------------------------------------------------------}
procedure TsCustomSpreadWriter.WriteCellCallback(ACell: PCell; AStream: TStream);
procedure TsCustomSpreadWriter.WriteCellToStream(AStream: TStream; ACell: PCell);
begin
if HasFormula(ACell) then
WriteFormula(AStream, ACell^.Row, ACell^.Col, ACell)
@ -616,8 +611,12 @@ end;
-------------------------------------------------------------------------------}
procedure TsCustomSpreadWriter.WriteCellsToStream(AStream: TStream;
ACells: TsCells);
var
cell: PCell;
begin
IterateThroughCells(AStream, ACells, WriteCellCallback);
for cell in ACells do
WriteCellToStream(AStream, cell);
// IterateThroughCells(AStream, ACells, WriteCellCallback);
end;
{@@ ----------------------------------------------------------------------------

View File

@ -48,6 +48,7 @@
<Unit1>
<Filename Value="datetests.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="datetests"/>
</Unit1>
<Unit2>
<Filename Value="stringtests.pas"/>
@ -72,6 +73,7 @@
<Unit7>
<Filename Value="formattests.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="formattests"/>
</Unit7>
<Unit8>
<Filename Value="colortests.pas"/>

View File

@ -120,10 +120,6 @@ type
TsSpreadBIFF8Writer = class(TsSpreadBIFFWriter)
private
procedure WriteCommentsEscherCallback(AComment: PsComment;
ACommentIndex: Integer; AStream: TStream);
procedure WriteCommentsNoteCallback(AComment: PsComment;
ACommentIndex: Integer; AStream: TStream);
procedure WriteHyperlinksCallback(AHyperlink: PsHyperlink; AStream: TStream);
protected
@ -132,6 +128,12 @@ type
function WriteBoundsheet(AStream: TStream; ASheetName: string): Int64;
procedure WriteComment(AStream: TStream; ACell: PCell); override;
procedure WriteComments(AStream: TStream; AWorksheet: TsWorksheet);
{
procedure WriteCommentEscher(AStream: TStream; AComment: PsComment;
ACommentIndex: Integer);
procedure WriteCommentNote(AStream: TStream; AComment: PsComment;
ACommentIndex: Integer);
}
procedure WriteDimensions(AStream: TStream; AWorksheet: TsWorksheet);
procedure WriteEOF(AStream: TStream);
procedure WriteFont(AStream: TStream; AFont: TsFont);
@ -1563,7 +1565,8 @@ procedure TsSpreadBIFF8Reader.ReadHyperlinkToolTip(AStream: TStream);
var
txt: String;
widestr: widestring;
row, col, row1, col1, row2, col2: Word;
//row, col,
row1, col1, row2, col2: Word;
hyperlink: PsHyperlink;
numbytes: Integer;
begin
@ -1584,6 +1587,10 @@ begin
txt := UTF8Encode(wideStr);
{ Add tooltip to hyperlinks }
for hyperlink in FWorksheet.Hyperlinks.GetRangeEnumerator(row1, col1, row2, col2) do
hyperlink^.ToolTip := txt;
{
for row := row1 to row2 do
for col := col1 to col2 do
begin
@ -1591,6 +1598,7 @@ begin
if hyperlink <> nil then
hyperlink^.ToolTip := txt;
end;
}
end;
@ -1810,24 +1818,47 @@ end;
-------------------------------------------------------------------------------}
procedure TsSpreadBIFF8Writer.WriteComments(AStream: TStream;
AWorksheet: TsWorksheet);
var
index: Integer;
comment: PsComment;
begin
exit; // Remove after comments can be written correctly
{$warning TODO: Fix writing of cell comments in BIFF8 (file is readable by OpenOffice, but not by Excel)}
{ At first we have to write all Escher-related records for all comments. }
IterateThroughComments(AStream, AWorksheet.Comments, WriteCommentsEscherCallback);
{ The NOTE records for all comments follow subsequently. }
IterateThroughComments(AStream, AWorksheet.Comments, WriteCommentsNoteCallback);
end;
{ At first we have to write all Escher-related records for all comments;
MSODRAWING - OBJ - MSODRAWING - TXO }
index := 1;
for comment in AWorksheet.Comments do
begin
if index = 1 then
WriteMSODrawing1(AStream, FWorksheet.Comments.Count, comment)
else
WriteMSODrawing2(AStream, comment, index);
WriteOBJ(AStream, index);
WriteMSODrawing3(AStream);
WriteTXO(AStream, comment);
inc(index);
end;
// IterateThroughComments(AStream, AWorksheet.Comments, WriteCommentsEscherCallback);
{ The NOTE records for all comments follow subsequently. }
index := 1;
for comment in AWorksheet.Comments do
begin
WriteNOTE(AStream, comment, index);
inc(index);
end;
// IterateThroughComments(AStream, AWorksheet.Comments, WriteCommentsNoteCallback);
end;
(*
{@@ ----------------------------------------------------------------------------
Helper method which writes all Escher-related records required for a cell
comment:
MSODRAWING - OBJ - MSODRAWING - TXT
The NOTE records are written separately
-------------------------------------------------------------------------------}
procedure TsSpreadBIFF8Writer.WriteCommentsEscherCallback(AComment: PsComment;
ACommentIndex: Integer; AStream: TStream);
procedure TsSpreadBIFF8Writer.WriteCommentsEscher(AStream: TStream;
AComment: PsComment; ACommentIndex: Integer);
begin
if ACommentIndex = 0 then
WriteMSODrawing1(AStream, FWorksheet.Comments.Count, AComment)
@ -1843,12 +1874,12 @@ end;
The Escher-related records required for each cell comment already have been
written.
-------------------------------------------------------------------------------}
procedure TsSpreadBIFF8Writer.WriteCommentsNoteCallback(AComment: PsComment;
ACommentIndex: Integer; AStream: TStream);
procedure TsSpreadBIFF8Writer.WriteCommentNote(AStream: TStream;
AComment: PsComment; ACommentIndex: Integer);
begin
WriteNOTE(AStream, AComment, ACommentIndex+1);
end;
*)
{@@ ----------------------------------------------------------------------------
Writes an Excel 8 DIMENSIONS record
@ -2491,8 +2522,12 @@ end;
-------------------------------------------------------------------------------}
procedure TsSpreadBIFF8Writer.WriteHyperlinks(AStream: TStream;
AWorksheet: TsWorksheet);
var
hyperlink: PsHyperlink;
begin
IterateThroughHyperlinks(AStream, AWorksheet.Hyperlinks, WriteHyperlinksCallback);
for hyperlink in AWorksheet.Hyperlinks do
WriteHyperlink(AStream, hyperlink, AWorksheet);
// IterateThroughHyperlinks(AStream, AWorksheet.Hyperlinks, WriteHyperlinksCallback);
end;
{@@ ----------------------------------------------------------------------------
@ -2613,11 +2648,37 @@ procedure TsSpreadBIFF8Writer.WriteMergedCells(AStream: TStream;
const
MAX_PER_RECORD = 1026;
var
n0, n: Integer;
n0, n, i: Integer;
rng: PsCellRange;
newRecord: Boolean;
begin
n0 := AWorksheet.MergedCells.Count;
n := Min(n0, MAX_PER_RECORD);
newRecord := true;
for rng in AWorksheet.MergedCells do
begin
if newRecord then
begin
newRecord := false;
{ BIFF record header }
WriteBIFFHeader(AStream, INT_EXCEL_ID_MERGEDCELLS, 2 + n*8);
{ Number of cell ranges in this record }
AStream.WriteWord(WordToLE(n));
end;
{ Write range data }
AStream.WriteWord(WordToLE(rng^.Row1));
AStream.WriteWord(WordToLE(rng^.Row2));
AStream.WriteWord(WordToLE(rng^.Col1));
AStream.WriteWord(WordToLE(rng^.Col2));
dec(n);
if n = 0 then begin
newRecord := true;
dec(n0, MAX_PER_RECORD);
n := Min(n0, MAX_PER_RECORD);
end;
end;
(*
while n0 > 0 do begin
n := Min(n0, MAX_PER_RECORD);
// at most 1026 merged ranges per BIFF record, the rest goes into a new record
@ -2641,6 +2702,7 @@ begin
dec(n0, MAX_PER_RECORD);
end;
*)
end;
{@@-----------------------------------------------------------------------------

View File

@ -335,14 +335,14 @@ type
protected
FDateMode: TDateMode;
FCodePage: String; // in a format prepared for lconvencoding.ConvertEncoding
FLastRow: Cardinal;
FLastCol: Cardinal;
// FLastRow: Cardinal;
// FLastCol: Cardinal;
procedure CreateNumFormatList; override;
function FindXFIndex(ACell: PCell): Integer; virtual;
function FixColor(AColor: TsColor): TsColor; override;
procedure GetLastRowCallback(ACell: PCell; AStream: TStream);
// procedure GetLastRowCallback(ACell: PCell; AStream: TStream);
function GetLastRowIndex(AWorksheet: TsWorksheet): Integer;
procedure GetLastColCallback(ACell: PCell; AStream: TStream);
// procedure GetLastColCallback(ACell: PCell; AStream: TStream);
function GetLastColIndex(AWorksheet: TsWorksheet): Word;
// Helper function for writing the BIFF header
@ -1894,31 +1894,37 @@ begin
end else
Result := AColor;
end;
(*
procedure TsSpreadBIFFWriter.GetLastRowCallback(ACell: PCell; AStream: TStream);
begin
Unused(AStream);
if ACell^.Row > FLastRow then FLastRow := ACell^.Row;
end;
end; *)
function TsSpreadBIFFWriter.GetLastRowIndex(AWorksheet: TsWorksheet): Integer;
begin
Result := AWorksheet.GetLastRowIndex;
{
FLastRow := 0;
IterateThroughCells(nil, AWorksheet.Cells, @GetLastRowCallback);
Result := FLastRow;
}
end;
(*
procedure TsSpreadBIFFWriter.GetLastColCallback(ACell: PCell; AStream: TStream);
begin
Unused(AStream);
if ACell^.Col > FLastCol then FLastCol := ACell^.Col;
end;
*)
function TsSpreadBIFFWriter.GetLastColIndex(AWorksheet: TsWorksheet): Word;
begin
Result := AWorksheet.GetLastColIndex;
{
FLastCol := 0;
IterateThroughCells(nil, AWorksheet.Cells, @GetLastColCallback);
Result := FLastCol;
}
end;
{@@ ----------------------------------------------------------------------------
@ -2925,9 +2931,9 @@ var
begin
for i := 0 to ASheet.Rows.Count-1 do begin
row := ASheet.Rows[i];
cell1 := ASheet.GetFirstCellOfRow(row^.Row);
cell1 := ASheet.Cells.GetFirstCellOfRow(row^.Row);
if cell1 <> nil then begin
cell2 := ASheet.GetLastCellOfRow(row^.Row);
cell2 := ASheet.Cells.GetLastCellOfRow(row^.Row);
WriteRow(AStream, ASheet, row^.Row, cell1^.Col, cell2^.Col, row);
end else
WriteRow(AStream, ASheet, row^.Row, 0, 0, row);
@ -3170,7 +3176,8 @@ begin
lCell.BoolValue := value <> 0;
end else
lCell.ContentType := cctEmpty;
WriteCellCallback(@lCell, AStream);
WriteCellToStream(AStream, @lCell);
// WriteCellCallback(@lCell, AStream);
value := varNULL;
end;
end;

View File

@ -109,8 +109,10 @@ type
TsSpreadOOXMLWriter = class(TsCustomSpreadWriter)
private
FNext_rId: Integer;
{
procedure WriteCommentsCallback(AComment: PsComment;
ACommentIndex: Integer; AStream: TStream);
}
procedure WriteVmlDrawingsCallback(AComment: PsComment;
ACommentIndex: Integer; AStream: TStream);
@ -2018,6 +2020,9 @@ begin
end;
procedure TsSpreadOOXMLWriter.WriteComments(AWorksheet: TsWorksheet);
var
comment: PsComment;
txt: String;
begin
if AWorksheet.Comments.Count = 0 then
exit;
@ -2042,7 +2047,60 @@ begin
'<commentList>');
// Comments
IterateThroughComments(FSComments[FCurSheetNum], AWorksheet.Comments, WriteCommentsCallback);
//IterateThroughComments(FSComments[FCurSheetNum], AWorksheet.Comments, WriteCommentsCallback);
for comment in AWorksheet.Comments do
begin
txt := comment^.Text;
ValidXMLText(txt);
// Write comment text to Comments stream
AppendToStream(FSComments[FCurSheetNum], Format(
'<comment ref="%s" authorId="0">', [GetCellString(comment^.Row, comment^.Col)]) +
'<text>'+
'<r>'+
'<rPr>'+ // thie entire node could be omitted, but then Excel uses some ugly default font
'<sz val="9"/>'+
'<color rgb="000000" />'+ // Excel files have color index 81 here, but it could be that this does not exist in fps files --> use rgb instead
'<fFont vel="Arial" />'+ // It is not harmful to Excel if the font does not exist.
'<charset val="1" />'+
'</rPr>'+
'<t xml:space="preserve">' + txt + '</t>' +
'</r>' +
'</text>' +
'</comment>');
end;
(*
procedure TsSpreadOOXMLWriter.WriteCommentsCallback(AComment: PsComment;
ACommentIndex: Integer; AStream: TStream);
var
comment: String;
begin
Unused(ACommentIndex);
comment := AComment^.Text;
ValidXMLText(comment);
// Write comment to Comments stream
AppendToStream(AStream, Format(
'<comment ref="%s" authorId="0">', [GetCellString(AComment^.Row, AComment^.Col)]));
AppendToStream(AStream,
'<text>'+
'<r>'+
'<rPr>'+ // this entire node could be omitted, but then Excel uses some default font out of control
'<sz val="9"/>'+
'<color rgb="000000" />'+ // It could be that color index 81 does not exist in fps files --> use rgb instead
'<rFont val="Arial"/>'+ // It is not harmful to Excel if the font does not exist.
'<charset val="1"/>'+
'</rPr>'+
'<t xml:space="preserve">' + comment + '</t>' +
'</r>'+
'</text>');
AppendToStream(AStream,
'</comment>');
end;
*)
// Footer
AppendToStream(FSComments[FCurSheetNum],
@ -2050,7 +2108,7 @@ begin
AppendToStream(FSComments[FCurSheetNum],
'</comments>');
end;
(*
procedure TsSpreadOOXMLWriter.WriteCommentsCallback(AComment: PsComment;
ACommentIndex: Integer; AStream: TStream);
var
@ -2078,7 +2136,7 @@ begin
'</text>');
AppendToStream(AStream,
'</comment>');
end;
end; *)
procedure TsSpreadOOXMLWriter.WriteDimension(AStream: TStream;
AWorksheet: TsWorksheet);
@ -2240,13 +2298,9 @@ begin
exit;
AppendToStream(AStream, Format(
'<mergeCells count="%d">', [n]) );
rng := PsCellRange(AWorksheet.MergedCells.GetFirst);
while Assigned(rng) do
begin
for rng in AWorksheet.MergedCells do
AppendToStream(AStream, Format(
'<mergeCell ref="%s" />', [GetCellRangeString(rng.Row1, rng.Col1, rng.Row2, rng.Col2)]));
rng := PsCellRange(AWorksheet.MergedCells.GetNext);
end;
AppendToStream(AStream,
'</mergeCells>');
end;
@ -2374,7 +2428,8 @@ begin
lCell.ContentType := cctBool;
lCell.BoolValue := value <> 0;
end;
WriteCellCallback(@lCell, AStream);
WriteCellToStream(AStream, @lCell);
// WriteCellCallback(@lCell, AStream);
varClear(value);
end;
AppendToStream(AStream,
@ -2394,12 +2449,16 @@ begin
AppendToStream(AStream, Format(
'<row r="%d" spans="%d:%d"%s>', [r+1, c1+1, c2+1, rh]));
// Write cells belonging to this row.
for cell in AWorksheet.Cells.GetRowEnumerator(r) do
WriteCellToStream(AStream, cell);
{
for c := c1 to c2 do begin
cell := AWorksheet.FindCell(r, c);
if Assigned(cell) then begin
WriteCellCallback(cell, AStream);
end;
end;
}
AppendToStream(AStream,
'</row>');
end;
@ -2587,6 +2646,11 @@ begin
end;
procedure TsSpreadOOXMLWriter.WriteVmlDrawings(AWorksheet: TsWorksheet);
// My xml viewer does not format vml files property --> format in code.
var
comment: PsComment;
index: Integer;
id: Integer;
begin
if AWorksheet.Comments.Count = 0 then
exit;
@ -2597,8 +2661,6 @@ begin
else
FSVmlDrawings[FCurSheetNum] := TMemoryStream.Create;
// FDrawingCounter := 0;
// Header
AppendToStream(FSVmlDrawings[FCurSheetNum],
'<xml xmlns:v="urn:schemas-microsoft-com:vml" '+
@ -2617,7 +2679,38 @@ begin
' </v:shapetype>' + LineEnding);
// Write vmlDrawings for each comment (formatting and position of comment box)
IterateThroughComments(FSVmlDrawings[FCurSheetNum], AWorksheet.Comments, WriteVmlDrawingsCallback);
index := 1;
for comment in AWorksheet.Comments do
begin
id := 1024 + index; // if more than 1024 comments then use data="1,2,etc" above! -- not implemented yet
AppendToStream(FSVmlDrawings[FCurSheetNum], LineEnding + Format(
' <v:shape id="_x0000_s%d" type="#_x0000_t202" ', [id]) + LineEnding + Format(
' style="position:absolute; width:108pt; height:52.5pt; z-index:%d; visibility:hidden" ', [index]) + LineEnding +
// it is not necessary to specify margin-left and margin-top here!
// 'style=''position:absolute; margin-left:71.25pt; margin-top:1.5pt; ' + Format(
// 'width:108pt; height:52.5pt; z-index:%d; visibility:hidden'' ', [FDrawingCounter+1]) +
// 'width:108pt; height:52.5pt; z-index:1; visibility:hidden'' ' +
' fillcolor="#ffffe1" o:insetmode="auto"> '+ LineEnding +
' <v:fill color2="#ffffe1" />'+LineEnding+
' <v:shadow on="t" color="black" obscured="t" />'+LineEnding+
' <v:path o:connecttype="none" />'+LineEnding+
' <v:textbox style="mso-direction-alt:auto">'+LineEnding+
' <div style="text-align:left"></div>'+LineEnding+
' </v:textbox>' + LineEnding +
' <x:ClientData ObjectType="Note">'+LineEnding+
' <x:MoveWithCells />'+LineEnding+
' <x:SizeWithCells />'+LineEnding+
' <x:Anchor> 1, 15, 0, 2, 2, 79, 4, 4</x:Anchor>'+LineEnding+
' <x:AutoFill>False</x:AutoFill>'+LineEnding + Format(
' <x:Row>%d</x:Row>', [comment^.Row]) + LineEnding + Format(
' <x:Column>%d</x:Column>', [comment^.Col]) + LineEnding +
' </x:ClientData>'+ LineEnding+
' </v:shape>' + LineEnding);
end;
//IterateThroughComments(FSVmlDrawings[FCurSheetNum], AWorksheet.Comments, WriteVmlDrawingsCallback);
// Footer
AppendToStream(FSVmlDrawings[FCurSheetNum],