You've already forked lazarus-ccr
fpspreadsheet: Migrate MergeCells tree to fpsClasses
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3979 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
@ -19,6 +19,7 @@ type
|
|||||||
TsRowColAVLTree = class(TAVLTree)
|
TsRowColAVLTree = class(TAVLTree)
|
||||||
private
|
private
|
||||||
FOwnsData: Boolean;
|
FOwnsData: Boolean;
|
||||||
|
FCurrentNode: TAVLTreeNode;
|
||||||
protected
|
protected
|
||||||
procedure DisposeData(var AData: Pointer); virtual; abstract;
|
procedure DisposeData(var AData: Pointer); virtual; abstract;
|
||||||
function NewData: Pointer; virtual; abstract;
|
function NewData: Pointer; virtual; abstract;
|
||||||
@ -28,8 +29,10 @@ type
|
|||||||
function Add(ARow, ACol: Cardinal): PsRowCol;
|
function Add(ARow, ACol: Cardinal): PsRowCol;
|
||||||
procedure Clear;
|
procedure Clear;
|
||||||
procedure Delete(ANode: TAVLTreeNode);
|
procedure Delete(ANode: TAVLTreeNode);
|
||||||
procedure DeleteRowOrCol(AIndex: Cardinal; IsRow: Boolean);
|
procedure DeleteRowOrCol(AIndex: Cardinal; IsRow: Boolean); virtual;
|
||||||
function Find(ARow, ACol: Cardinal): PsRowCol;
|
function Find(ARow, ACol: Cardinal): PsRowCol;
|
||||||
|
function GetFirst: PsRowCol;
|
||||||
|
function GetNext: PsRowCol;
|
||||||
procedure InsertRowOrCol(AIndex: Cardinal; IsRow: Boolean);
|
procedure InsertRowOrCol(AIndex: Cardinal; IsRow: Boolean);
|
||||||
procedure Remove(ARow, ACol: Cardinal);
|
procedure Remove(ARow, ACol: Cardinal);
|
||||||
end;
|
end;
|
||||||
@ -54,6 +57,17 @@ type
|
|||||||
procedure DeleteHyperlink(ARow, ACol: Cardinal);
|
procedure DeleteHyperlink(ARow, ACol: Cardinal);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ TsMergedCells }
|
||||||
|
TsMergedCells = class(TsRowColAVLTree)
|
||||||
|
protected
|
||||||
|
procedure DisposeData(var AData: Pointer); override;
|
||||||
|
function NewData: Pointer; override;
|
||||||
|
public
|
||||||
|
function AddRange(ARow1, ACol1, ARow2, ACol2: Cardinal): PsCellRange;
|
||||||
|
procedure DeleteRange(ARow, ACol: Cardinal);
|
||||||
|
procedure DeleteRowOrCol(AIndex: Cardinal; IsRow: Boolean); override;
|
||||||
|
function FindRangeWithCell(ARow, ACol: Cardinal): PsCellRange;
|
||||||
|
end;
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
|
|
||||||
@ -196,6 +210,28 @@ begin
|
|||||||
Result := PsRowCol(node.Data);
|
Result := PsRowCol(node.Data);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{@@ ----------------------------------------------------------------------------
|
||||||
|
The combination of the methods GetFirst and GetNext allow a fast iteration
|
||||||
|
through all nodes of the tree.
|
||||||
|
-------------------------------------------------------------------------------}
|
||||||
|
function TsRowColAVLTree.GetFirst: PsRowCol;
|
||||||
|
begin
|
||||||
|
FCurrentNode := FindLowest;
|
||||||
|
if FCurrentNode <> nil then
|
||||||
|
Result := PsRowCol(FCurrentNode.Data)
|
||||||
|
else
|
||||||
|
Result := nil;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TsRowColAVLTree.GetNext: PsRowCol;
|
||||||
|
begin
|
||||||
|
FCurrentNode := FindSuccessor(FCurrentNode);
|
||||||
|
if FCurrentNode <> nil then
|
||||||
|
Result := PsRowCol(FCurrentNode.Data)
|
||||||
|
else
|
||||||
|
Result := nil;
|
||||||
|
end;
|
||||||
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
{@@ ----------------------------------------------------------------------------
|
||||||
This procedure adjusts row or column indexes stored in the tree nodes if a
|
This procedure adjusts row or column indexes stored in the tree nodes if a
|
||||||
row or column will be inserted into the underlying worksheet.
|
row or column will be inserted into the underlying worksheet.
|
||||||
@ -335,5 +371,128 @@ begin
|
|||||||
Result := hyperlink;
|
Result := hyperlink;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
{******************************************************************************}
|
||||||
|
{ TsMergedCell: a AVLTree to store merged cell range records for cells }
|
||||||
|
{******************************************************************************}
|
||||||
|
|
||||||
|
{@@ ----------------------------------------------------------------------------
|
||||||
|
Adds a node with a new merge cell range record to the tree. If a node already
|
||||||
|
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: Cardinal): PsCellRange;
|
||||||
|
begin
|
||||||
|
Result := PsCellRange(Add(ARow1, ACol1));
|
||||||
|
Result^.Row2 := ARow2;
|
||||||
|
Result^.Col2 := ACol2;
|
||||||
|
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: Cardinal);
|
||||||
|
begin
|
||||||
|
Remove(ARow, ACol);
|
||||||
|
end;
|
||||||
|
|
||||||
|
{@@ ----------------------------------------------------------------------------
|
||||||
|
This procedure adjusts row or column indexes stored in the tree nodes if a
|
||||||
|
row or column will be deleted from the underlying worksheet.
|
||||||
|
|
||||||
|
@param AIndex Index of the row (if IsRow=true) or column (if IsRow=false)
|
||||||
|
to be deleted
|
||||||
|
@param IsRow Identifies whether AIndex refers to a row or column index
|
||||||
|
-------------------------------------------------------------------------------}
|
||||||
|
procedure TsMergedCells.DeleteRowOrCol(AIndex: Cardinal; IsRow: Boolean);
|
||||||
|
var
|
||||||
|
rng, nextrng: PsCellRange;
|
||||||
|
begin
|
||||||
|
rng := PsCellRange(GetFirst);
|
||||||
|
while Assigned(rng) do begin
|
||||||
|
nextrng := PsCellRange(GetNext);
|
||||||
|
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);
|
||||||
|
end else
|
||||||
|
// Single-row merged block coincides with row to be deleted
|
||||||
|
if (AIndex = rng^.Row1) and (rng^.Row1 = rng^.Row2) then
|
||||||
|
DeleteRange(rng^.Row1, rng^.Col1)
|
||||||
|
else
|
||||||
|
// 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);
|
||||||
|
end else
|
||||||
|
begin
|
||||||
|
// Deleted column is at the left of the merged range
|
||||||
|
// --> Shift entire merged range to the left by 1
|
||||||
|
// 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);
|
||||||
|
end else
|
||||||
|
// Single-column block coincides with the column to be deleted
|
||||||
|
// NOTE: The "merged" flags disappear with the deleted cells
|
||||||
|
if (AIndex = rng^.Col1) and (rng^.Col1 = rng^.Col2) then
|
||||||
|
DeleteRange(rng^.Row1, rng^.Col1)
|
||||||
|
else
|
||||||
|
// Deleted column runs through the merged block
|
||||||
|
// --> Shift right column to the left by 1
|
||||||
|
if (AIndex >= rng^.Col1) and (AIndex <= rng^.Col2) then
|
||||||
|
dec(rng^.Col2);
|
||||||
|
end;
|
||||||
|
// Proceed with next merged range
|
||||||
|
rng := nextrng;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{@@ ----------------------------------------------------------------------------
|
||||||
|
Helper procedure which disposes the memory occupied by the merged cell range
|
||||||
|
data record attached to a tree node.
|
||||||
|
-------------------------------------------------------------------------------}
|
||||||
|
procedure TsMergedCells.DisposeData(var AData: Pointer);
|
||||||
|
begin
|
||||||
|
if AData <> nil then
|
||||||
|
Dispose(PsCellRange(AData));
|
||||||
|
AData := nil;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{@@ ----------------------------------------------------------------------------
|
||||||
|
Finds the cell range which contains the cell specified by its row and column
|
||||||
|
index
|
||||||
|
-------------------------------------------------------------------------------}
|
||||||
|
function TsMergedCells.FindRangeWithCell(ARow, ACol: Cardinal): PsCellRange;
|
||||||
|
var
|
||||||
|
node: TAVLTreeNode;
|
||||||
|
begin
|
||||||
|
node := FindLowest;
|
||||||
|
while Assigned(node) do
|
||||||
|
begin
|
||||||
|
Result := PsCellRange(node.Data);
|
||||||
|
if (ARow >= Result^.Row1) and (ARow <= Result^.Row2) and
|
||||||
|
(ACol >= Result^.Col1) and (ACol <= Result^.Col2) then exit;
|
||||||
|
node := FindSuccessor(node);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{@@ ----------------------------------------------------------------------------
|
||||||
|
Alloates memory of a merged cell range data record.
|
||||||
|
-------------------------------------------------------------------------------}
|
||||||
|
function TsMergedCells.NewData: Pointer;
|
||||||
|
var
|
||||||
|
range: PsCellRange;
|
||||||
|
begin
|
||||||
|
New(range);
|
||||||
|
Result := range;
|
||||||
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
@ -126,7 +126,7 @@ type
|
|||||||
FName: String; // Name of the worksheet (displayed at the tab)
|
FName: String; // Name of the worksheet (displayed at the tab)
|
||||||
FCells: TAvlTree; // Items are TCell
|
FCells: TAvlTree; // Items are TCell
|
||||||
FComments: TsComments;
|
FComments: TsComments;
|
||||||
FMergedCells: TAvlTree; // Items are TsCellRange
|
FMergedCells: TsMergedCells;
|
||||||
FHyperlinks: TsHyperlinks;
|
FHyperlinks: TsHyperlinks;
|
||||||
FCurrentNode: TAVLTreeNode; // for GetFirstCell and GetNextCell
|
FCurrentNode: TAVLTreeNode; // for GetFirstCell and GetNextCell
|
||||||
FRows, FCols: TIndexedAVLTree; // This lists contain only rows or cols with styles different from default
|
FRows, FCols: TIndexedAVLTree; // This lists contain only rows or cols with styles different from default
|
||||||
@ -159,10 +159,7 @@ type
|
|||||||
procedure DeleteRowCallback(data, arg: Pointer);
|
procedure DeleteRowCallback(data, arg: Pointer);
|
||||||
procedure InsertColCallback(data, arg: Pointer);
|
procedure InsertColCallback(data, arg: Pointer);
|
||||||
procedure InsertRowCallback(data, arg: Pointer);
|
procedure InsertRowCallback(data, arg: Pointer);
|
||||||
procedure RemoveCellRangesCallback(data, arg: pointer);
|
|
||||||
procedure RemoveCellsCallback(data, arg: pointer);
|
procedure RemoveCellsCallback(data, arg: pointer);
|
||||||
// procedure RemoveCommentsCallback(data, arg: pointer);
|
|
||||||
// procedure RemoveHyperlinksCallback(data, arg: pointer);
|
|
||||||
|
|
||||||
protected
|
protected
|
||||||
function CellUsedInFormula(ARow, ACol: Cardinal): Boolean;
|
function CellUsedInFormula(ARow, ACol: Cardinal): Boolean;
|
||||||
@ -172,13 +169,6 @@ type
|
|||||||
function RemoveCell(ARow, ACol: Cardinal): PCell;
|
function RemoveCell(ARow, ACol: Cardinal): PCell;
|
||||||
procedure RemoveAndFreeCell(ARow, ACol: Cardinal);
|
procedure RemoveAndFreeCell(ARow, ACol: Cardinal);
|
||||||
|
|
||||||
// Merged cells
|
|
||||||
function CellIsInMergedRange(ARow, ACol: Cardinal; ARange: PsCellRange): Boolean;
|
|
||||||
function FindMergedRangeForBase(ABaseRow, ABaseCol: Cardinal): PsCellRange;
|
|
||||||
function FindMergedRangeForCell(ARow, ACol: Cardinal): PsCellRange;
|
|
||||||
procedure RemoveAllMergedRanges;
|
|
||||||
procedure RemoveMergedRange(ABaseRow, ABaseCol: Cardinal);
|
|
||||||
|
|
||||||
// Sorting
|
// Sorting
|
||||||
function DoCompareCells(ARow1, ACol1, ARow2, ACol2: Cardinal;
|
function DoCompareCells(ARow1, ACol1, ARow2, ACol2: Cardinal;
|
||||||
ASortOptions: TsSortOptions): Integer;
|
ASortOptions: TsSortOptions): Integer;
|
||||||
@ -486,16 +476,15 @@ type
|
|||||||
ATooltip: String = ''); overload;
|
ATooltip: String = ''); overload;
|
||||||
|
|
||||||
{ Merged cells }
|
{ Merged cells }
|
||||||
procedure MergeCells(ARow1, ACol1, ARow2, ACol2: Cardinal); overload;
|
|
||||||
procedure MergeCells(ARange: String); overload;
|
|
||||||
procedure UnmergeCells(ARow, ACol: Cardinal); overload;
|
|
||||||
procedure UnmergeCells(ARange: String); overload;
|
|
||||||
function FindMergeBase(ACell: PCell): PCell;
|
function FindMergeBase(ACell: PCell): PCell;
|
||||||
function FindMergedRange(ACell: PCell; out ARow1, ACol1, ARow2, ACol2: Cardinal): Boolean;
|
function FindMergedRange(ACell: PCell; out ARow1, ACol1, ARow2, ACol2: Cardinal): Boolean;
|
||||||
procedure GetMergedCellRanges(out AList: TsCellRangeArray);
|
procedure MergeCells(ARow1, ACol1, ARow2, ACol2: Cardinal); overload;
|
||||||
|
procedure MergeCells(ARange: String); overload;
|
||||||
function InSameMergedRange(ACell1, ACell2: PCell): Boolean;
|
function InSameMergedRange(ACell1, ACell2: PCell): Boolean;
|
||||||
function IsMergeBase(ACell: PCell): Boolean;
|
function IsMergeBase(ACell: PCell): Boolean;
|
||||||
function IsMerged(ACell: PCell): Boolean;
|
function IsMerged(ACell: PCell): Boolean;
|
||||||
|
procedure UnmergeCells(ARow, ACol: Cardinal); overload;
|
||||||
|
procedure UnmergeCells(ARange: String); overload;
|
||||||
|
|
||||||
// Notification of changed cells content and format
|
// Notification of changed cells content and format
|
||||||
procedure ChangedCell(ARow, ACol: Cardinal);
|
procedure ChangedCell(ARow, ACol: Cardinal);
|
||||||
@ -511,7 +500,7 @@ type
|
|||||||
{@@ List of all comment records }
|
{@@ List of all comment records }
|
||||||
property Comments: TsComments read FComments;
|
property Comments: TsComments read FComments;
|
||||||
{@@ List of merged cells (contains TsCellRange records) }
|
{@@ List of merged cells (contains TsCellRange records) }
|
||||||
property MergedCells: TAVLTree read FMergedCells;
|
property MergedCells: TsMergedCells read FMergedCells;
|
||||||
{@@ List of hyperlink information records }
|
{@@ List of hyperlink information records }
|
||||||
property Hyperlinks: TsHyperlinks read FHyperlinks;
|
property Hyperlinks: TsHyperlinks read FHyperlinks;
|
||||||
{@@ FormatSettings for localization of some formatting strings }
|
{@@ FormatSettings for localization of some formatting strings }
|
||||||
@ -1150,7 +1139,7 @@ begin
|
|||||||
FRows := TIndexedAVLTree.Create(@CompareRows);
|
FRows := TIndexedAVLTree.Create(@CompareRows);
|
||||||
FCols := TIndexedAVLTree.Create(@CompareCols);
|
FCols := TIndexedAVLTree.Create(@CompareCols);
|
||||||
FComments := TsComments.Create;
|
FComments := TsComments.Create;
|
||||||
FMergedCells := TAVLTree.Create(@CompareMergedCells);
|
FMergedCells := TsMergedCells.Create;
|
||||||
FHyperlinks := TsHyperlinks.Create;
|
FHyperlinks := TsHyperlinks.Create;
|
||||||
|
|
||||||
FDefaultColWidth := 12;
|
FDefaultColWidth := 12;
|
||||||
@ -1178,7 +1167,6 @@ begin
|
|||||||
RemoveAllCells;
|
RemoveAllCells;
|
||||||
RemoveAllRows;
|
RemoveAllRows;
|
||||||
RemoveAllCols;
|
RemoveAllCols;
|
||||||
RemoveAllMergedRanges;
|
|
||||||
|
|
||||||
FCells.Free;
|
FCells.Free;
|
||||||
FRows.Free;
|
FRows.Free;
|
||||||
@ -3235,74 +3223,28 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
|
||||||
Checks whether the cell at ARow/ACol is in the specified merged cell block
|
{ Merged cells }
|
||||||
-------------------------------------------------------------------------------}
|
|
||||||
function TsWorksheet.CellIsInMergedRange(ARow, ACol: Cardinal;
|
|
||||||
ARange: PsCellRange): Boolean;
|
|
||||||
begin
|
|
||||||
Result := (ARange <> nil) and
|
|
||||||
(ARow >= ARange^.Row1) and (ARow <= ARange^.Row2) and
|
|
||||||
(ACol >= ARange^.Col1) and (ACol <= ARange^.Col2);
|
|
||||||
end;
|
|
||||||
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
{@@ ----------------------------------------------------------------------------
|
||||||
Retrieves the pointer to the cell range record of the merged block
|
Finds the upper left cell of a merged block to which a specified cell belongs.
|
||||||
which has the specified cell as base.
|
This is the "merge base". Returns nil if the cell is not merged.
|
||||||
Returns nil if the specified cell is not the base of a merged block.
|
|
||||||
|
@param ACell Cell under investigation
|
||||||
|
@return A pointer to the cell in the upper left corner of the merged block
|
||||||
|
to which ACell belongs.
|
||||||
|
If ACell is isolated then the function returns nil.
|
||||||
-------------------------------------------------------------------------------}
|
-------------------------------------------------------------------------------}
|
||||||
function TsWorksheet.FindMergedRangeForBase(ABaseRow, ABaseCol: Cardinal): PsCellRange;
|
function TsWorksheet.FindMergeBase(ACell: PCell): PCell;
|
||||||
var
|
var
|
||||||
lCellRange: TsCellRange;
|
rng: PsCellRange;
|
||||||
AVLNode: TAVLTreeNode;
|
|
||||||
begin
|
begin
|
||||||
Result := nil;
|
Result := nil;
|
||||||
if FMergedCells.Count = 0 then
|
if IsMerged(ACell) then
|
||||||
exit;
|
begin
|
||||||
|
rng := FMergedCells.FindRangeWithCell(ACell^.Row, ACell^.Col);
|
||||||
lCellRange.Row1 := ABaseRow;
|
if rng <> nil then
|
||||||
lCellRange.Col1 := ABaseCol;
|
Result := FindCell(rng^.Row1, rng^.Col1);
|
||||||
AVLNode := FMergedCells.Find(@lCellRange);
|
|
||||||
if Assigned(AVLNode) then
|
|
||||||
Result := PsCellRange(AVLNode.Data);
|
|
||||||
end;
|
|
||||||
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
|
||||||
Finds the pointer to a merged range record in the FMergedCells list to
|
|
||||||
which the specified cell belongs
|
|
||||||
-------------------------------------------------------------------------------}
|
|
||||||
function TsWorksheet.FindMergedRangeForCell(ARow, ACol: Cardinal): PsCellRange;
|
|
||||||
var
|
|
||||||
AVLNode: TAVLTreeNode;
|
|
||||||
begin
|
|
||||||
// Iterate through all merged blocks in the list FMergedCells...
|
|
||||||
AVLNode := FMergedCells.FindLowest;
|
|
||||||
while AVLNode <> nil do begin
|
|
||||||
Result := PsCellRange(AVLNode.Data);
|
|
||||||
// ... and check if the current block contains the specified cell
|
|
||||||
if CellIsInMergedRange(ARow, ACol, Result) then
|
|
||||||
exit;
|
|
||||||
AVLNode := FMergedCells.FindSuccessor(AVLNode);
|
|
||||||
end;
|
|
||||||
Result := nil;
|
|
||||||
end;
|
|
||||||
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
|
||||||
Removes and destroys a merged cell range record (i.e. unmerges the cells)
|
|
||||||
-------------------------------------------------------------------------------}
|
|
||||||
procedure TsWorksheet.RemoveMergedRange(ABaseRow, ABaseCol: Cardinal);
|
|
||||||
var
|
|
||||||
lCellRange: TsCellRange;
|
|
||||||
AVLNode: TAVLTreeNode;
|
|
||||||
begin
|
|
||||||
lCellRange.Row1 := ABaseRow;
|
|
||||||
lCellRange.Col1 := ABaseCol;
|
|
||||||
AVLNode := FMergedCells.Find(@lCellRange);
|
|
||||||
if Assigned(AVLNode) then begin
|
|
||||||
// Destroy the cell range record
|
|
||||||
Dispose(PsCellRange(AVLNode.Data));
|
|
||||||
// Delete the avl tree node.
|
|
||||||
FMergedCells.Delete(AVLNode);
|
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -3325,17 +3267,11 @@ begin
|
|||||||
exit;
|
exit;
|
||||||
|
|
||||||
// Is cell ARow1/ACol1 already the base of a merged range? ...
|
// Is cell ARow1/ACol1 already the base of a merged range? ...
|
||||||
rng := FindMergedRangeForBase(ARow1, ACol1);
|
rng := PsCellRange(FMergedCells.Find(ARow1, ACol1));
|
||||||
// ... no: --> Add a new merged range
|
// ... no: --> Add a new merged range
|
||||||
if rng = nil then
|
if rng = nil then
|
||||||
begin
|
FMergedCells.AddRange(ARow1, ACol1, ARow2, ACol2)
|
||||||
New(rng);
|
else
|
||||||
rng^.Row1 := ARow1;
|
|
||||||
rng^.Col1 := ACol1;
|
|
||||||
rng^.Row2 := ARow2;
|
|
||||||
rng^.Col2 := ACol2;
|
|
||||||
FMergedCells.Add(rng);
|
|
||||||
end else
|
|
||||||
// ... yes: --> modify the merged range accordingly
|
// ... yes: --> modify the merged range accordingly
|
||||||
begin
|
begin
|
||||||
// unmark previously merged range
|
// unmark previously merged range
|
||||||
@ -3344,7 +3280,7 @@ begin
|
|||||||
begin
|
begin
|
||||||
cell := FindCell(r, c);
|
cell := FindCell(r, c);
|
||||||
if cell <> nil then // nil happens when col/row is inserted...
|
if cell <> nil then // nil happens when col/row is inserted...
|
||||||
cell^.Flags := cell^.Flags - [cfMerged];
|
Exclude(cell^.Flags, cfMerged);
|
||||||
end;
|
end;
|
||||||
// Define new limits of merged range
|
// Define new limits of merged range
|
||||||
rng^.Row2 := ARow2;
|
rng^.Row2 := ARow2;
|
||||||
@ -3356,7 +3292,7 @@ begin
|
|||||||
for c := ACol1 to ACol2 do
|
for c := ACol1 to ACol2 do
|
||||||
begin
|
begin
|
||||||
cell := GetCell(r, c); // if not existent create new cell
|
cell := GetCell(r, c); // if not existent create new cell
|
||||||
cell^.Flags := cell^.Flags + [cfMerged];
|
Include(cell^.Flags, cfMerged);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
ChangedCell(ARow1, ACol1);
|
ChangedCell(ARow1, ACol1);
|
||||||
@ -3390,7 +3326,7 @@ var
|
|||||||
r, c: Cardinal;
|
r, c: Cardinal;
|
||||||
cell: PCell;
|
cell: PCell;
|
||||||
begin
|
begin
|
||||||
rng := FindMergedRangeForCell(ARow, ACol);
|
rng := FMergedCells.FindRangeWithCell(ARow, ACol);
|
||||||
if rng <> nil then
|
if rng <> nil then
|
||||||
begin
|
begin
|
||||||
// Remove the "merged" flag from the cells in the merged range to make them
|
// Remove the "merged" flag from the cells in the merged range to make them
|
||||||
@ -3399,9 +3335,9 @@ begin
|
|||||||
for c := rng^.Col1 to rng^.Col2 do
|
for c := rng^.Col1 to rng^.Col2 do
|
||||||
begin
|
begin
|
||||||
cell := FindCell(r, c);
|
cell := FindCell(r, c);
|
||||||
cell^.Flags := cell^.Flags - [cfMerged];
|
Exclude(cell^.Flags, cfMerged);
|
||||||
end;
|
end;
|
||||||
RemoveMergedRange(rng^.Row1, rng^.Col1);
|
FMergedCells.DeleteRange(rng^.Row1, rng^.Col1);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
ChangedCell(ARow, ACol);
|
ChangedCell(ARow, ACol);
|
||||||
@ -3417,36 +3353,13 @@ end;
|
|||||||
-------------------------------------------------------------------------------}
|
-------------------------------------------------------------------------------}
|
||||||
procedure TsWorksheet.UnmergeCells(ARange: String);
|
procedure TsWorksheet.UnmergeCells(ARange: String);
|
||||||
var
|
var
|
||||||
r1, r2, c1, c2: Cardinal;
|
sheet: TsWorksheet;
|
||||||
|
rng: TsCellRange;
|
||||||
begin
|
begin
|
||||||
if (pos(':', ARange) = 0) and ParseCellString(ARange, r1, c1) then
|
if Workbook.TryStrToCellRange(ARange, sheet, rng) then
|
||||||
UnmergeCells(r1, c1)
|
UnmergeCells(rng.Row1, rng.Col1);
|
||||||
else
|
|
||||||
if ParseCellRangeString(ARange, r1, c1, r2, c2) then
|
|
||||||
UnmergeCells(r1, c1);
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
|
||||||
Finds the upper left cell of a merged block to which a specified cell belongs.
|
|
||||||
This is the "merge base". Returns nil if the cell is not merged.
|
|
||||||
|
|
||||||
@param ACell Cell under investigation
|
|
||||||
@return A pointer to the cell in the upper left corner of the merged block
|
|
||||||
to which ACell belongs, If ACell is isolated then the function returns
|
|
||||||
nil.
|
|
||||||
-------------------------------------------------------------------------------}
|
|
||||||
function TsWorksheet.FindMergeBase(ACell: PCell): PCell;
|
|
||||||
var
|
|
||||||
rng: PsCellRange;
|
|
||||||
begin
|
|
||||||
Result := nil;
|
|
||||||
if (ACell <> nil) and IsMerged(ACell) then
|
|
||||||
begin
|
|
||||||
rng := FindMergedRangeForCell(ACell^.Row, ACell^.Col);
|
|
||||||
if rng <> nil then
|
|
||||||
Result := FindCell(rng^.Row1, rng^.Col1);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
{@@ ----------------------------------------------------------------------------
|
||||||
Finds the upper left cell of a shared formula block to which the specified
|
Finds the upper left cell of a shared formula block to which the specified
|
||||||
@ -3482,9 +3395,9 @@ function TsWorksheet.FindMergedRange(ACell: PCell;
|
|||||||
var
|
var
|
||||||
rng: PsCellRange;
|
rng: PsCellRange;
|
||||||
begin
|
begin
|
||||||
if (ACell <> nil) and IsMerged(ACell) then
|
if IsMerged(ACell) then
|
||||||
begin
|
begin
|
||||||
rng := FindMergedRangeForCell(ACell^.Row, ACell^.Col);
|
rng := FMergedCells.FindRangeWithCell(ACell^.Row, ACell^.Col);
|
||||||
if rng <> nil then
|
if rng <> nil then
|
||||||
begin
|
begin
|
||||||
ARow1 := rng^.Row1;
|
ARow1 := rng^.Row1;
|
||||||
@ -3498,6 +3411,47 @@ begin
|
|||||||
Result := false;
|
Result := false;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{@@ ----------------------------------------------------------------------------
|
||||||
|
Checks whether the two specified cells belong to the same merged cell block.
|
||||||
|
|
||||||
|
@param ACell1 Pointer to the first cell
|
||||||
|
@param ACell2 Pointer to the second cell
|
||||||
|
@reult TRUE if both cells belong to the same merged cell block
|
||||||
|
FALSE if the cells are not merged or are in different blocks
|
||||||
|
-------------------------------------------------------------------------------}
|
||||||
|
function TsWorksheet.InSameMergedRange(ACell1, ACell2: PCell): Boolean;
|
||||||
|
begin
|
||||||
|
Result := IsMerged(ACell1) and IsMerged(ACell2) and
|
||||||
|
(FindMergeBase(ACell1) = FindMergeBase(ACell2));
|
||||||
|
end;
|
||||||
|
|
||||||
|
{@@ ----------------------------------------------------------------------------
|
||||||
|
Returns true if the specified cell is the base of a merged cell range, i.e.
|
||||||
|
the upper left corner of that range.
|
||||||
|
|
||||||
|
@param ACell Pointer to the cell being considered
|
||||||
|
@return True if the cell is the upper left corner of a merged range
|
||||||
|
False if not
|
||||||
|
-------------------------------------------------------------------------------}
|
||||||
|
function TsWorksheet.IsMergeBase(ACell: PCell): Boolean;
|
||||||
|
begin
|
||||||
|
Result := (ACell <> nil) and (ACell = FindMergeBase(ACell));
|
||||||
|
end;
|
||||||
|
|
||||||
|
{@@ ----------------------------------------------------------------------------
|
||||||
|
Returns TRUE if the specified cell belongs to a merged block
|
||||||
|
|
||||||
|
@param ACell Pointer to the cell of interest
|
||||||
|
@return TRUE if the cell belongs to a merged block, FALSE if not.
|
||||||
|
-------------------------------------------------------------------------------}
|
||||||
|
function TsWorksheet.IsMerged(ACell: PCell): Boolean;
|
||||||
|
begin
|
||||||
|
Result := (ACell <> nil) and (cfMerged in ACell^.Flags);
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
{ Shared formulas }
|
||||||
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
{@@ ----------------------------------------------------------------------------
|
||||||
Determines the cell block sharing the same formula which is used by a given cell
|
Determines the cell block sharing the same formula which is used by a given cell
|
||||||
|
|
||||||
@ -3575,78 +3529,6 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
|
||||||
Collects all ranges of merged cells that can be found in the worksheet
|
|
||||||
|
|
||||||
@param AList Array containing TsCellRange records of the merged cells
|
|
||||||
-------------------------------------------------------------------------------}
|
|
||||||
procedure TsWorksheet.GetMergedCellRanges(out AList: TsCellRangeArray);
|
|
||||||
var
|
|
||||||
AVLNode: TAVLTreeNode;
|
|
||||||
rng: PsCellRange;
|
|
||||||
i: Integer;
|
|
||||||
begin
|
|
||||||
SetLength(AList, FMergedCells.Count);
|
|
||||||
i := 0;
|
|
||||||
AVLNode := FMergedCells.FindLowest;
|
|
||||||
while AVLNode <> nil do begin
|
|
||||||
rng := PsCellRange(AVLNode.Data);
|
|
||||||
AList[i].Row1 := rng^.Row1;
|
|
||||||
AList[i].Col1 := rng^.Col1;
|
|
||||||
AList[i].Row2 := rng^.Row2;
|
|
||||||
AList[i].Col2 := rng^.Col2;
|
|
||||||
inc(i);
|
|
||||||
AVLNode := FMergedCells.FindSuccessor(AVLNode);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
|
||||||
Checks whether the two specified cells belong to the same merged cell block.
|
|
||||||
|
|
||||||
@param ACell1 Pointer to the first cell
|
|
||||||
@param ACell2 Pointer to the second cell
|
|
||||||
@reult TRUE if both cells belong to the same merged cell block
|
|
||||||
FALSE if the cells are not merged or are in different blocks
|
|
||||||
-------------------------------------------------------------------------------}
|
|
||||||
function TsWorksheet.InSameMergedRange(ACell1, ACell2: PCell): Boolean;
|
|
||||||
begin
|
|
||||||
Result := IsMerged(ACell1) and IsMerged(ACell2) and
|
|
||||||
(FindMergeBase(ACell1) = FindMergeBase(ACell2));
|
|
||||||
end;
|
|
||||||
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
|
||||||
Returns true if the specified cell is the base of a merged cell range, i.e.
|
|
||||||
the upper left corner of that range.
|
|
||||||
|
|
||||||
@param ACell Pointer to the cell being considered
|
|
||||||
@return True if the cell is the upper left corner of a merged range
|
|
||||||
False if not
|
|
||||||
-------------------------------------------------------------------------------}
|
|
||||||
function TsWorksheet.IsMergeBase(ACell: PCell): Boolean;
|
|
||||||
begin
|
|
||||||
Result := (ACell <> nil) and (ACell = FindMergeBase(ACell));
|
|
||||||
end;
|
|
||||||
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
|
||||||
Returns TRUE if the specified cell belongs to a merged block
|
|
||||||
|
|
||||||
@param ACell Pointer to the cell of interest
|
|
||||||
@return TRUE if the cell belongs to a merged block, FALSE if not.
|
|
||||||
-------------------------------------------------------------------------------}
|
|
||||||
function TsWorksheet.IsMerged(ACell: PCell): Boolean;
|
|
||||||
begin
|
|
||||||
Result := (ACell <> nil) and (cfMerged in ACell^.Flags);
|
|
||||||
end;
|
|
||||||
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
|
||||||
Helper method for clearing the merged cell records in a spreadsheet.
|
|
||||||
-------------------------------------------------------------------------------}
|
|
||||||
procedure TsWorksheet.RemoveCellRangesCallback(data, arg: pointer);
|
|
||||||
begin
|
|
||||||
Unused(arg);
|
|
||||||
Dispose(PsCellRange(data));
|
|
||||||
end;
|
|
||||||
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
{@@ ----------------------------------------------------------------------------
|
||||||
Helper method for clearing the cell records in a spreadsheet.
|
Helper method for clearing the cell records in a spreadsheet.
|
||||||
-------------------------------------------------------------------------------}
|
-------------------------------------------------------------------------------}
|
||||||
@ -3664,16 +3546,6 @@ begin
|
|||||||
RemoveAllAvlTreeNodes(FCells, RemoveCellsCallback);
|
RemoveAllAvlTreeNodes(FCells, RemoveCellsCallback);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
|
||||||
Empties the list of merged cell ranges.
|
|
||||||
Is called from the destructor of the worksheet.
|
|
||||||
NOTE: The cells are left intact. They are still marked as merged!!!
|
|
||||||
-------------------------------------------------------------------------------}
|
|
||||||
procedure TsWorksheet.RemoveAllMergedRanges;
|
|
||||||
begin
|
|
||||||
RemoveAllAvlTreeNodes(FMergedCells, RemoveCellRangesCallback);
|
|
||||||
end;
|
|
||||||
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
{@@ ----------------------------------------------------------------------------
|
||||||
Removes the comment from a cell and releases the memory occupied by the node.
|
Removes the comment from a cell and releases the memory occupied by the node.
|
||||||
-------------------------------------------------------------------------------}
|
-------------------------------------------------------------------------------}
|
||||||
@ -6217,15 +6089,13 @@ end;
|
|||||||
-------------------------------------------------------------------------------}
|
-------------------------------------------------------------------------------}
|
||||||
procedure TsWorksheet.DeleteCol(ACol: Cardinal);
|
procedure TsWorksheet.DeleteCol(ACol: Cardinal);
|
||||||
var
|
var
|
||||||
AVLNode, nextAVLNode: TAVLTreeNode;
|
AVLNode: TAVLTreeNode;
|
||||||
col: PCol;
|
col: PCol;
|
||||||
i: Integer;
|
i: Integer;
|
||||||
r, rr, cc: Cardinal;
|
r, rr, cc: Cardinal;
|
||||||
cell, basecell, nextcell: PCell;
|
cell, basecell, nextcell: PCell;
|
||||||
firstRow, lastCol, lastRow: Cardinal;
|
firstRow, lastCol, lastRow: Cardinal;
|
||||||
rng: PsCellRange;
|
rng: PsCellRange;
|
||||||
comment: PsComment;
|
|
||||||
hyperlink: PsHyperlink;
|
|
||||||
begin
|
begin
|
||||||
lastCol := GetLastColIndex;
|
lastCol := GetLastColIndex;
|
||||||
lastRow := GetLastOccupiedRowIndex;
|
lastRow := GetLastOccupiedRowIndex;
|
||||||
@ -6261,23 +6131,7 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
// Fix merged cells
|
// Fix merged cells
|
||||||
AVLNode := FMergedCells.FindLowest;
|
FMergedCells.DeleteRowOrCol(ACol, false);
|
||||||
while Assigned(AVLNode) do begin
|
|
||||||
rng := PsCellRange(AVLNode.Data);
|
|
||||||
// Deleted column is at the left of the merged range
|
|
||||||
// --> Shift entire merged range to the left by 1
|
|
||||||
// The "merged" flags do not have to be changed. They move with the cells.
|
|
||||||
if (ACol < rng^.Col1) then begin
|
|
||||||
dec(rng^.Col1);
|
|
||||||
dec(rng^.Col2);
|
|
||||||
end else
|
|
||||||
// Deleted column runs through the merged block
|
|
||||||
// --> Shift right column to the left by 1
|
|
||||||
if (ACol >= rng^.Col1) and (ACol <= rng^.Col2) then
|
|
||||||
dec(rng^.Col2);
|
|
||||||
// Proceed with next merged range
|
|
||||||
AVLNode := FMergedCells.FindSuccessor(AVLNode);
|
|
||||||
end;
|
|
||||||
|
|
||||||
// Fix comments
|
// Fix comments
|
||||||
FComments.DeleteRowOrCol(ACol, false);
|
FComments.DeleteRowOrCol(ACol, false);
|
||||||
@ -6320,15 +6174,13 @@ end;
|
|||||||
-------------------------------------------------------------------------------}
|
-------------------------------------------------------------------------------}
|
||||||
procedure TsWorksheet.DeleteRow(ARow: Cardinal);
|
procedure TsWorksheet.DeleteRow(ARow: Cardinal);
|
||||||
var
|
var
|
||||||
AVLNode, nextAVLNode: TAVLTreeNode;
|
AVLNode: TAVLTreeNode;
|
||||||
row: PRow;
|
row: PRow;
|
||||||
i: Integer;
|
i: Integer;
|
||||||
c, rr, cc: Cardinal;
|
c, rr, cc: Cardinal;
|
||||||
firstCol, lastCol, lastRow: Cardinal;
|
firstCol, lastCol, lastRow: Cardinal;
|
||||||
cell, nextcell, basecell: PCell;
|
cell, nextcell, basecell: PCell;
|
||||||
rng: PsCellRange;
|
rng: PsCellRange;
|
||||||
comment: PsComment;
|
|
||||||
hyperlink: PsHyperlink;
|
|
||||||
begin
|
begin
|
||||||
firstCol := GetFirstColIndex;
|
firstCol := GetFirstColIndex;
|
||||||
lastCol := GetLastOccupiedColIndex;
|
lastCol := GetLastOccupiedColIndex;
|
||||||
@ -6363,22 +6215,7 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
// Fix merged cells
|
// Fix merged cells
|
||||||
AVLNode := FMergedCells.FindLowest;
|
FMergedCells.DeleteRowOrCol(ARow, true);
|
||||||
while Assigned(AVLNode) do begin
|
|
||||||
rng := PsCellRange(AVLNode.Data);
|
|
||||||
// Deleted row is ABOVE the merged range
|
|
||||||
// --> Shift entire merged range up by 1
|
|
||||||
if (ARow < rng^.Row1) then begin
|
|
||||||
dec(rng^.Row1);
|
|
||||||
dec(rng^.Row2);
|
|
||||||
end else
|
|
||||||
// Deleted row runs through the merged block
|
|
||||||
// --> Shift bottom row up by 1
|
|
||||||
if (ARow >= rng^.Row1) and (ARow <= rng^.Row2) then
|
|
||||||
dec(rng^.Row2);
|
|
||||||
// Proceed with next merged range
|
|
||||||
AVLNode := FMergedCells.FindSuccessor(AVLNode);
|
|
||||||
end;
|
|
||||||
|
|
||||||
// Fix comments
|
// Fix comments
|
||||||
FComments.DeleteRowOrCol(ARow, true);
|
FComments.DeleteRowOrCol(ARow, true);
|
||||||
@ -6428,8 +6265,6 @@ var
|
|||||||
cell: PCell;
|
cell: PCell;
|
||||||
AVLNode: TAVLTreeNode;
|
AVLNode: TAVLTreeNode;
|
||||||
rng: PsCellRange;
|
rng: PsCellRange;
|
||||||
comment: PsComment;
|
|
||||||
hyperlink: PsHyperlink;
|
|
||||||
begin
|
begin
|
||||||
// Handling of shared formula references is too complicated for me...
|
// Handling of shared formula references is too complicated for me...
|
||||||
// Split them into isolated cell formulas
|
// Split them into isolated cell formulas
|
||||||
@ -6463,10 +6298,9 @@ begin
|
|||||||
UpdateCaches;
|
UpdateCaches;
|
||||||
|
|
||||||
// Fix merged cells
|
// Fix merged cells
|
||||||
AVLNode := FMergedCells.FindLowest;
|
rng := PsCellRange(FMergedCells.GetFirst);
|
||||||
while AVLNode <> nil do
|
while rng <> nil do
|
||||||
begin
|
begin
|
||||||
rng := PsCellRange(AVLNode.Data);
|
|
||||||
// The new column is at the LEFT of the merged block
|
// The new column is at the LEFT of the merged block
|
||||||
// --> Shift entire range to the right by 1 column
|
// --> Shift entire range to the right by 1 column
|
||||||
if (ACol < rng^.Col1) then
|
if (ACol < rng^.Col1) then
|
||||||
@ -6475,7 +6309,8 @@ begin
|
|||||||
for r := rng^.Row1 to rng^.Row2 do
|
for r := rng^.Row1 to rng^.Row2 do
|
||||||
begin
|
begin
|
||||||
cell := FindCell(r, rng^.Col1);
|
cell := FindCell(r, rng^.Col1);
|
||||||
if cell <> nil then cell^.Flags := cell^.Flags - [cfMerged];
|
if cell <> nil then
|
||||||
|
Exclude(cell^.Flags, cfMerged);
|
||||||
end;
|
end;
|
||||||
// Shift merged block to the right
|
// Shift merged block to the right
|
||||||
// Don't call "MergeCells" here - this would add a new merged block
|
// Don't call "MergeCells" here - this would add a new merged block
|
||||||
@ -6486,7 +6321,8 @@ begin
|
|||||||
for r := rng^.Row1 to rng^.Row2 do
|
for r := rng^.Row1 to rng^.Row2 do
|
||||||
begin
|
begin
|
||||||
cell := FindCell(R, rng^.Col2);
|
cell := FindCell(R, rng^.Col2);
|
||||||
if cell <> nil then cell^.Flags := cell^.Flags + [cfMerged];
|
if cell <> nil then
|
||||||
|
Include(cell^.Flags, cfMerged);
|
||||||
end;
|
end;
|
||||||
end else
|
end else
|
||||||
// The new column goes through this cell block --> Shift only the right
|
// The new column goes through this cell block --> Shift only the right
|
||||||
@ -6494,7 +6330,7 @@ begin
|
|||||||
if (ACol >= rng^.Col1) and (ACol <= rng^.Col2) then
|
if (ACol >= rng^.Col1) and (ACol <= rng^.Col2) then
|
||||||
MergeCells(rng^.Row1, rng^.Col1, rng^.Row2, rng^.Col2+1);
|
MergeCells(rng^.Row1, rng^.Col1, rng^.Row2, rng^.Col2+1);
|
||||||
// Continue with next merged block
|
// Continue with next merged block
|
||||||
AVLNode := FMergedCells.FindSuccessor(AVLNode);
|
rng := PsCellRange(FMergedCells.GetNext);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
ChangedCell(0, ACol);
|
ChangedCell(0, ACol);
|
||||||
@ -6554,8 +6390,6 @@ var
|
|||||||
cell: PCell;
|
cell: PCell;
|
||||||
AVLNode: TAVLTreeNode;
|
AVLNode: TAVLTreeNode;
|
||||||
rng: PsCellRange;
|
rng: PsCellRange;
|
||||||
comment: PsComment;
|
|
||||||
hyperlink: PsHyperlink;
|
|
||||||
begin
|
begin
|
||||||
// Handling of shared formula references is too complicated for me...
|
// Handling of shared formula references is too complicated for me...
|
||||||
// Splits them into isolated cell formulas
|
// Splits them into isolated cell formulas
|
||||||
@ -6589,10 +6423,9 @@ begin
|
|||||||
UpdateCaches;
|
UpdateCaches;
|
||||||
|
|
||||||
// Fix merged cells
|
// Fix merged cells
|
||||||
AVLNode := FMergedCells.FindLowest;
|
rng := PsCellRange(FMergedCells.GetFirst);
|
||||||
while AVLNode <> nil do
|
while rng <> nil do
|
||||||
begin
|
begin
|
||||||
rng := PsCellRange(AVLNode.Data);
|
|
||||||
// The new row is ABOVE the merged block --> Shift entire range down by 1 row
|
// The new row is ABOVE the merged block --> Shift entire range down by 1 row
|
||||||
if (ARow < rng^.Row1) then
|
if (ARow < rng^.Row1) then
|
||||||
begin
|
begin
|
||||||
@ -6600,7 +6433,8 @@ begin
|
|||||||
for c := rng^.Col1 to rng^.Col2 do
|
for c := rng^.Col1 to rng^.Col2 do
|
||||||
begin
|
begin
|
||||||
cell := FindCell(rng^.Row1, c);
|
cell := FindCell(rng^.Row1, c);
|
||||||
if cell <> nil then cell^.Flags := cell^.Flags - [cfMerged];
|
if cell <> nil then
|
||||||
|
Exclude(cell^.Flags, cfMerged);
|
||||||
end;
|
end;
|
||||||
// Shift merged block down
|
// Shift merged block down
|
||||||
// (Don't call "MergeCells" here - this would add a new merged block
|
// (Don't call "MergeCells" here - this would add a new merged block
|
||||||
@ -6611,7 +6445,8 @@ begin
|
|||||||
for c := rng^.Col1 to rng^.Col2 do
|
for c := rng^.Col1 to rng^.Col2 do
|
||||||
begin
|
begin
|
||||||
cell := FindCell(rng^.Row2, c);
|
cell := FindCell(rng^.Row2, c);
|
||||||
if cell <> nil then cell^.Flags := cell^.Flags + [cfMerged];
|
if cell <> nil then
|
||||||
|
Include(cell^.Flags, cfMerged);
|
||||||
end;
|
end;
|
||||||
end else
|
end else
|
||||||
// The new row goes through this cell block --> Shift only the bottom row
|
// The new row goes through this cell block --> Shift only the bottom row
|
||||||
@ -6619,7 +6454,7 @@ begin
|
|||||||
if (ARow >= rng^.Row1) and (ARow <= rng^.Row2) then
|
if (ARow >= rng^.Row1) and (ARow <= rng^.Row2) then
|
||||||
MergeCells(rng^.Row1, rng^.Col1, rng^.Row2+1, rng^.Col2);
|
MergeCells(rng^.Row1, rng^.Col1, rng^.Row2+1, rng^.Col2);
|
||||||
// Continue with next block
|
// Continue with next block
|
||||||
AVLNode := FMergedCells.FindSuccessor(AVLNode);
|
rng := PsCellRange(FMergedCells.GetNext);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
ChangedCell(ARow, 0);
|
ChangedCell(ARow, 0);
|
||||||
|
@ -2815,6 +2815,9 @@ begin
|
|||||||
AStrings.Add(Format('Last column=%d', [ASheet.GetLastColIndex]));
|
AStrings.Add(Format('Last column=%d', [ASheet.GetLastColIndex]));
|
||||||
AStrings.Add(Format('Active cell=%s', [GetCellString(ASheet.ActiveCellRow, ASheet.ActiveCellCol)]));
|
AStrings.Add(Format('Active cell=%s', [GetCellString(ASheet.ActiveCellRow, ASheet.ActiveCellCol)]));
|
||||||
AStrings.Add(Format('Selection=%s', [ASheet.GetSelectionAsString]));
|
AStrings.Add(Format('Selection=%s', [ASheet.GetSelectionAsString]));
|
||||||
|
AStrings.Add(Format('Comments=%d items', [ASheet.Comments.Count]));
|
||||||
|
AStrings.Add(Format('Hyperlinks=%d items', [ASheet.Hyperlinks.Count]));
|
||||||
|
AStrings.Add(Format('MergedCells=%d items', [ASheet.MergedCells.Count]));
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
@ -2611,12 +2611,10 @@ procedure TsSpreadBIFF8Writer.WriteMergedCells(AStream: TStream;
|
|||||||
const
|
const
|
||||||
MAX_PER_RECORD = 1026;
|
MAX_PER_RECORD = 1026;
|
||||||
var
|
var
|
||||||
i, n0, n: Integer;
|
n0, n: Integer;
|
||||||
rngList: TsCellRangeArray;
|
rng: PsCellRange;
|
||||||
begin
|
begin
|
||||||
AWorksheet.GetMergedCellRanges(rngList);
|
n0 := AWorksheet.MergedCells.Count;
|
||||||
n0 := Length(rngList);
|
|
||||||
i := 0;
|
|
||||||
|
|
||||||
while n0 > 0 do begin
|
while n0 > 0 do begin
|
||||||
n := Min(n0, MAX_PER_RECORD);
|
n := Min(n0, MAX_PER_RECORD);
|
||||||
@ -2625,17 +2623,18 @@ begin
|
|||||||
{ BIFF record header }
|
{ BIFF record header }
|
||||||
WriteBIFFHeader(AStream, INT_EXCEL_ID_MERGEDCELLS, 2 + n*8);
|
WriteBIFFHeader(AStream, INT_EXCEL_ID_MERGEDCELLS, 2 + n*8);
|
||||||
|
|
||||||
// Count of cell ranges in this record
|
{ Number of cell ranges in this record }
|
||||||
AStream.WriteWord(WordToLE(n));
|
AStream.WriteWord(WordToLE(n));
|
||||||
|
|
||||||
// Loop writing the merged cell ranges
|
{ Loop for writing the merged cell ranges }
|
||||||
while (n > 0) and (i < Length(rngList)) do begin
|
rng := PsCellRange(AWorksheet.MergedCells.GetFirst);
|
||||||
AStream.WriteWord(WordToLE(rngList[i].Row1));
|
while (n > 0) do begin
|
||||||
AStream.WriteWord(WordToLE(rngList[i].Row2));
|
AStream.WriteWord(WordToLE(rng^.Row1));
|
||||||
AStream.WriteWord(WordToLE(rngList[i].Col1));
|
AStream.WriteWord(WordToLE(rng^.Row2));
|
||||||
AStream.WriteWord(WordToLE(rngList[i].Col2));
|
AStream.WriteWord(WordToLE(rng^.Col1));
|
||||||
inc(i);
|
AStream.WriteWord(WordToLE(rng^.Col2));
|
||||||
dec(n);
|
dec(n);
|
||||||
|
rng := PsCellRange(AWorksheet.MergedCells.GetNext);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
dec(n0, MAX_PER_RECORD);
|
dec(n0, MAX_PER_RECORD);
|
||||||
|
@ -2232,17 +2232,20 @@ end;
|
|||||||
procedure TsSpreadOOXMLWriter.WriteMergedCells(AStream: TStream;
|
procedure TsSpreadOOXMLWriter.WriteMergedCells(AStream: TStream;
|
||||||
AWorksheet: TsWorksheet);
|
AWorksheet: TsWorksheet);
|
||||||
var
|
var
|
||||||
rng: TsCellRangeArray;
|
rng: PsCellRange;
|
||||||
i: Integer;
|
n: Integer;
|
||||||
begin
|
begin
|
||||||
AWorksheet.GetMergedCellRanges(rng);
|
n := AWorksheet.MergedCells.Count;
|
||||||
if Length(rng) = 0 then
|
if n = 0 then
|
||||||
exit;
|
exit;
|
||||||
AppendToStream(AStream, Format(
|
AppendToStream(AStream, Format(
|
||||||
'<mergeCells count="%d">', [Length(rng)]) );
|
'<mergeCells count="%d">', [n]) );
|
||||||
for i:=0 to Length(rng)-1 do begin
|
rng := PsCellRange(AWorksheet.MergedCells.GetFirst);
|
||||||
|
while Assigned(rng) do
|
||||||
|
begin
|
||||||
AppendToStream(AStream, Format(
|
AppendToStream(AStream, Format(
|
||||||
'<mergeCell ref="%s" />', [GetCellRangeString(rng[i].Row1, rng[i].Col1, rng[i].Row2, rng[i].Col2)]));
|
'<mergeCell ref="%s" />', [GetCellRangeString(rng.Row1, rng.Col1, rng.Row2, rng.Col2)]));
|
||||||
|
rng := PsCellRange(AWorksheet.MergedCells.GetNext);
|
||||||
end;
|
end;
|
||||||
AppendToStream(AStream,
|
AppendToStream(AStream,
|
||||||
'</mergeCells>');
|
'</mergeCells>');
|
||||||
|
Reference in New Issue
Block a user