You've already forked lazarus-ccr
fpspreadsheet: Redo internal structure of merged cells (use MergeBase cell instead of neighbor links).
Suppress TsWorksheetGrid painting of inner grid lines of merged cells. Fix inserting columns and rows running through merged cells. Silence some warnings. Fix ods reading merged cells incorrectly. git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3569 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
@ -4,7 +4,7 @@ object MainFrm: TMainFrm
|
||||
Top = 258
|
||||
Width = 884
|
||||
Caption = 'spready'
|
||||
ClientHeight = 619
|
||||
ClientHeight = 614
|
||||
ClientWidth = 884
|
||||
Menu = MainMenu
|
||||
OnActivate = FormActivate
|
||||
@ -14,7 +14,7 @@ object MainFrm: TMainFrm
|
||||
object Panel1: TPanel
|
||||
Left = 0
|
||||
Height = 78
|
||||
Top = 541
|
||||
Top = 536
|
||||
Width = 884
|
||||
Align = alBottom
|
||||
BevelOuter = bvNone
|
||||
@ -23,7 +23,7 @@ object MainFrm: TMainFrm
|
||||
TabOrder = 6
|
||||
object EdFrozenCols: TSpinEdit
|
||||
Left = 429
|
||||
Height = 23
|
||||
Height = 28
|
||||
Top = 8
|
||||
Width = 52
|
||||
OnChange = EdFrozenColsChange
|
||||
@ -31,7 +31,7 @@ object MainFrm: TMainFrm
|
||||
end
|
||||
object EdFrozenRows: TSpinEdit
|
||||
Left = 429
|
||||
Height = 23
|
||||
Height = 28
|
||||
Top = 39
|
||||
Width = 52
|
||||
OnChange = EdFrozenRowsChange
|
||||
@ -39,37 +39,37 @@ object MainFrm: TMainFrm
|
||||
end
|
||||
object Label1: TLabel
|
||||
Left = 344
|
||||
Height = 15
|
||||
Height = 20
|
||||
Top = 13
|
||||
Width = 62
|
||||
Width = 77
|
||||
Caption = 'Frozen cols:'
|
||||
FocusControl = EdFrozenCols
|
||||
ParentColor = False
|
||||
end
|
||||
object Label2: TLabel
|
||||
Left = 344
|
||||
Height = 15
|
||||
Height = 20
|
||||
Top = 40
|
||||
Width = 66
|
||||
Width = 82
|
||||
Caption = 'Frozen rows:'
|
||||
FocusControl = EdFrozenRows
|
||||
ParentColor = False
|
||||
end
|
||||
object CbReadFormulas: TCheckBox
|
||||
Left = 8
|
||||
Height = 19
|
||||
Height = 24
|
||||
Top = 8
|
||||
Width = 96
|
||||
Width = 120
|
||||
Caption = 'Read formulas'
|
||||
OnChange = CbReadFormulasChange
|
||||
TabOrder = 0
|
||||
end
|
||||
object CbHeaderStyle: TComboBox
|
||||
Left = 200
|
||||
Height = 23
|
||||
Height = 28
|
||||
Top = 8
|
||||
Width = 116
|
||||
ItemHeight = 15
|
||||
ItemHeight = 20
|
||||
ItemIndex = 2
|
||||
Items.Strings = (
|
||||
'Lazarus'
|
||||
@ -83,18 +83,18 @@ object MainFrm: TMainFrm
|
||||
end
|
||||
object CbAutoCalcFormulas: TCheckBox
|
||||
Left = 8
|
||||
Height = 19
|
||||
Height = 24
|
||||
Top = 32
|
||||
Width = 128
|
||||
Width = 158
|
||||
Caption = 'Calculate on change'
|
||||
OnChange = CbAutoCalcFormulasChange
|
||||
TabOrder = 1
|
||||
end
|
||||
object CbTextOverflow: TCheckBox
|
||||
Left = 8
|
||||
Height = 19
|
||||
Height = 24
|
||||
Top = 56
|
||||
Width = 91
|
||||
Width = 114
|
||||
Caption = 'Text overflow'
|
||||
Checked = True
|
||||
OnChange = CbTextOverflowChange
|
||||
@ -189,19 +189,19 @@ object MainFrm: TMainFrm
|
||||
end
|
||||
object FontComboBox: TComboBox
|
||||
Left = 52
|
||||
Height = 23
|
||||
Height = 28
|
||||
Top = 2
|
||||
Width = 127
|
||||
ItemHeight = 15
|
||||
ItemHeight = 20
|
||||
OnSelect = FontComboBoxSelect
|
||||
TabOrder = 0
|
||||
end
|
||||
object FontSizeComboBox: TComboBox
|
||||
Left = 179
|
||||
Height = 23
|
||||
Height = 28
|
||||
Top = 2
|
||||
Width = 48
|
||||
ItemHeight = 15
|
||||
ItemHeight = 20
|
||||
Items.Strings = (
|
||||
'8'
|
||||
'9'
|
||||
@ -375,7 +375,7 @@ object MainFrm: TMainFrm
|
||||
end
|
||||
object InspectorSplitter: TSplitter
|
||||
Left = 648
|
||||
Height = 462
|
||||
Height = 457
|
||||
Top = 79
|
||||
Width = 5
|
||||
Align = alRight
|
||||
@ -383,7 +383,7 @@ object MainFrm: TMainFrm
|
||||
end
|
||||
object InspectorPageControl: TPageControl
|
||||
Left = 653
|
||||
Height = 462
|
||||
Height = 457
|
||||
Top = 79
|
||||
Width = 231
|
||||
ActivePage = PgCellValue
|
||||
@ -393,11 +393,11 @@ object MainFrm: TMainFrm
|
||||
OnChange = InspectorPageControlChange
|
||||
object PgCellValue: TTabSheet
|
||||
Caption = 'Cell value'
|
||||
ClientHeight = 434
|
||||
ClientHeight = 424
|
||||
ClientWidth = 223
|
||||
object CellInspector: TValueListEditor
|
||||
Left = 0
|
||||
Height = 434
|
||||
Height = 424
|
||||
Top = 0
|
||||
Width = 223
|
||||
Align = alClient
|
||||
@ -438,7 +438,7 @@ object MainFrm: TMainFrm
|
||||
end
|
||||
object TabControl: TTabControl
|
||||
Left = 0
|
||||
Height = 462
|
||||
Height = 457
|
||||
Top = 79
|
||||
Width = 648
|
||||
OnChange = TabControlChange
|
||||
@ -446,7 +446,7 @@ object MainFrm: TMainFrm
|
||||
TabOrder = 3
|
||||
object WorksheetGrid: TsWorksheetGrid
|
||||
Left = 2
|
||||
Height = 457
|
||||
Height = 452
|
||||
Top = 3
|
||||
Width = 644
|
||||
FrozenCols = 0
|
||||
@ -456,6 +456,7 @@ object MainFrm: TMainFrm
|
||||
AutoAdvance = aaDown
|
||||
BorderStyle = bsNone
|
||||
ColCount = 27
|
||||
ExtendedSelect = False
|
||||
MouseWheelOption = mwGrid
|
||||
Options = [goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine, goRangeSelect, goRowSizing, goColSizing, goThumbTracking, goSmoothScroll, goFixedColSizing]
|
||||
RowCount = 101
|
||||
@ -463,7 +464,7 @@ object MainFrm: TMainFrm
|
||||
TitleStyle = tsNative
|
||||
OnSelection = WorksheetGridSelection
|
||||
ColWidths = (
|
||||
42
|
||||
56
|
||||
64
|
||||
64
|
||||
64
|
||||
|
@ -546,6 +546,7 @@ begin
|
||||
WorksheetGrid.MergeCells
|
||||
else
|
||||
WorksheetGrid.UnmergeCells;
|
||||
WorksheetGridSelection(nil, WorksheetGrid.Col, WorksheetGrid.Row);
|
||||
end;
|
||||
|
||||
procedure TMainFrm.AcNewExecute(Sender: TObject);
|
||||
@ -1041,11 +1042,11 @@ begin
|
||||
if (ACell=nil) or not (uffNumberFormat in ACell^.UsedFormattingFields)
|
||||
then Strings.Add('NumberFormatStr=')
|
||||
else Strings.Add('NumberFormatStr=' + ACell^.NumberFormatStr);
|
||||
if (ACell=nil) or (ACell^.MergedNeighbors = []) then
|
||||
Strings.Add('Merged neighbors=')
|
||||
if not WorksheetGrid.Worksheet.IsMerged(ACell) then
|
||||
Strings.Add('Merged range=')
|
||||
else begin
|
||||
WorksheetGrid.Worksheet.FindMergedRange(ACell, r1, c1, r2, c2);
|
||||
Strings.Add('Merged neighbors=' + GetCellRangeString(r1, c1, r2, c2));
|
||||
Strings.Add('Merged range=' + GetCellRangeString(r1, c1, r2, c2));
|
||||
end;
|
||||
|
||||
end;
|
||||
@ -1167,7 +1168,7 @@ begin
|
||||
AcWordwrap.Checked := wrapped;
|
||||
end;
|
||||
|
||||
procedure TMainFrm.WorksheetGridSelection(Sender: TObject; aCol, aRow: Integer);
|
||||
procedure TMainFrm.WorksheetGridSelection(Sender: TObject; ACol, ARow: Integer);
|
||||
var
|
||||
r, c: Cardinal;
|
||||
cell: PCell;
|
||||
@ -1209,7 +1210,7 @@ begin
|
||||
EdFormula.Text := '';
|
||||
|
||||
EdCellAddress.Text := GetCellString(r, c, [rfRelRow, rfRelCol]);
|
||||
AcMergeCells.Checked := (cell <> nil) and (cell^.MergedNeighbors <> []);
|
||||
AcMergeCells.Checked := WorksheetGrid.Worksheet.IsMerged(cell);
|
||||
|
||||
UpdateHorAlignmentActions;
|
||||
UpdateVertAlignmentActions;
|
||||
|
@ -1841,50 +1841,60 @@ begin
|
||||
// These nodes occur due to indentation spaces which are not skipped
|
||||
// automatically any more due to PreserveWhiteSpace option applied
|
||||
// to ReadXMLFile
|
||||
{
|
||||
if nodeName <> 'table:table-cell' then begin //= '#text' then begin
|
||||
cellNode := cellNode.NextSibling;
|
||||
Continue;
|
||||
end;
|
||||
}
|
||||
if nodeName = 'table:table-cell' then begin
|
||||
// select this cell value's type
|
||||
paramValueType := GetAttrValue(CellNode, 'office:value-type');
|
||||
paramFormula := GetAttrValue(CellNode, 'table:formula');
|
||||
tableStyleName := GetAttrValue(CellNode, 'table:style-name');
|
||||
|
||||
// select this cell value's type
|
||||
paramValueType := GetAttrValue(CellNode, 'office:value-type');
|
||||
paramFormula := GetAttrValue(CellNode, 'table:formula');
|
||||
tableStyleName := GetAttrValue(CellNode, 'table:style-name');
|
||||
if paramValueType = 'string' then
|
||||
ReadLabel(row, col, cellNode)
|
||||
else
|
||||
if (paramValueType = 'float') or (paramValueType = 'percentage') or
|
||||
(paramValueType = 'currency')
|
||||
then
|
||||
ReadNumber(row, col, cellNode)
|
||||
else if (paramValueType = 'date') or (paramValueType = 'time') then
|
||||
ReadDateTime(row, col, cellNode)
|
||||
else if (paramValueType = '') and (tableStyleName <> '') then
|
||||
ReadBlank(row, col, cellNode);
|
||||
|
||||
if paramValueType = 'string' then
|
||||
ReadLabel(row, col, cellNode)
|
||||
if ParamFormula <> '' then
|
||||
ReadFormula(row, col, cellNode);
|
||||
|
||||
paramColsSpanned := GetAttrValue(cellNode, 'table:number-columns-spanned');
|
||||
if paramColsSpanned <> '' then
|
||||
colsSpanned := StrToInt(paramColsSpanned) - 1
|
||||
else
|
||||
colsSpanned := 0;
|
||||
|
||||
paramRowsSpanned := GetAttrValue(cellNode, 'table:number-rows-spanned');
|
||||
if paramRowsSpanned <> '' then
|
||||
rowsSpanned := StrToInt(paramRowsSpanned) - 1
|
||||
else
|
||||
rowsSpanned := 0;
|
||||
|
||||
if (colsSpanned <> 0) or (rowsSpanned <> 0) then
|
||||
FWorksheet.MergeCells(row, col, row+rowsSpanned, col+colsSpanned);
|
||||
|
||||
paramColsRepeated := GetAttrValue(cellNode, 'table:number-columns-repeated');
|
||||
if paramColsRepeated = '' then paramColsRepeated := '1';
|
||||
end
|
||||
else
|
||||
if (paramValueType = 'float') or (paramValueType = 'percentage') or
|
||||
(paramValueType = 'currency')
|
||||
then
|
||||
ReadNumber(row, col, cellNode)
|
||||
else if (paramValueType = 'date') or (paramValueType = 'time') then
|
||||
ReadDateTime(row, col, cellNode)
|
||||
else if (paramValueType = '') and (tableStyleName <> '') then
|
||||
ReadBlank(row, col, cellNode);
|
||||
if nodeName = 'table:covered-table-cell' then
|
||||
begin
|
||||
paramColsRepeated := GetAttrValue(cellNode, 'table:number-columns-repeated');
|
||||
if paramColsRepeated = '' then paramColsRepeated := '1';
|
||||
end else
|
||||
paramColsRepeated := '0';
|
||||
|
||||
if ParamFormula <> '' then
|
||||
ReadFormula(row, col, cellNode);
|
||||
|
||||
paramColsSpanned := GetAttrValue(cellNode, 'table:number-columns-spanned');
|
||||
if paramColsSpanned <> '' then
|
||||
colsSpanned := StrToInt(paramColsSpanned) - 1
|
||||
else
|
||||
colsSpanned := 0;
|
||||
|
||||
paramRowsSpanned := GetAttrValue(cellNode, 'table:number-rows-spanned');
|
||||
if paramRowsSpanned <> '' then
|
||||
rowsSpanned := StrToInt(paramRowsSpanned) - 1
|
||||
else
|
||||
rowsSpanned := 0;
|
||||
|
||||
if (colsSpanned <> 0) or (rowsSpanned <> 0) then
|
||||
FWorksheet.MergeCells(row, col, row+rowsSpanned, col+colsSpanned);
|
||||
|
||||
paramColsRepeated := GetAttrValue(cellNode, 'table:number-columns-repeated');
|
||||
if paramColsRepeated = '' then paramColsRepeated := '1';
|
||||
col := col + StrToInt(paramColsRepeated);
|
||||
|
||||
cellNode := cellNode.NextSibling;
|
||||
end; //while Assigned(cellNode)
|
||||
|
||||
@ -3056,7 +3066,8 @@ begin
|
||||
cell := ASheet.FindCell(r, c);
|
||||
|
||||
// Belongs to merged block?
|
||||
if (cell <> nil) and not FWorksheet.IsMergeBase(cell) and (cell^.MergedNeighbors <> []) then
|
||||
// if (cell <> nil) and not FWorksheet.IsMergeBase(cell) and (cell^.MergedNeighbors <> []) then
|
||||
if (cell <> nil) and not FWorksheet.IsMergeBase(cell) and (cell^.MergeBase <> nil) then
|
||||
begin
|
||||
AppendToStream(AStream,
|
||||
'<table:covered-table-cell />');
|
||||
|
@ -435,7 +435,8 @@ type
|
||||
BoolValue: Boolean;
|
||||
ErrorValue: TsErrorValue;
|
||||
SharedFormulaBase: PCell; // Cell containing the shared formula
|
||||
MergedNeighbors: TsCellBorders;
|
||||
MergeBase: PCell; // Upper left cell if a merged range
|
||||
//MergedNeighbors: TsCellBorders;
|
||||
{ Formatting fields }
|
||||
{ When adding/deleting formatting fields don't forget to update CopyFormat! }
|
||||
UsedFormattingFields: TsUsedFormattingFields;
|
||||
@ -583,6 +584,7 @@ type
|
||||
function FindMergedRange(ACell: PCell; out ARow1, ACol1, ARow2, ACol2: Cardinal): Boolean;
|
||||
procedure GetMergedCellRanges(out AList: TsCellRangeArray);
|
||||
function IsMergeBase(ACell: PCell): Boolean;
|
||||
function IsMerged(ACell: PCell): Boolean;
|
||||
|
||||
{ Writing of values }
|
||||
function WriteBlank(ARow, ACol: Cardinal): PCell; overload;
|
||||
@ -2918,69 +2920,13 @@ begin
|
||||
if (ARow1 = ARow2) and (ACol1 = ACol2) then
|
||||
exit;
|
||||
|
||||
// Case 2: single row
|
||||
if (ARow1 = ARow2) and (ACol1 <> ACol2) then begin
|
||||
cell := GetCell(ARow1, ACol1);
|
||||
cell^.MergedNeighbors := [cbEast];
|
||||
cell := GetCell(ARow2, ACol2);
|
||||
cell^.MergedNeighbors := [cbWest];
|
||||
for c := ACol1+1 to ACol2-1 do begin
|
||||
cell := GetCell(ARow1, c);
|
||||
cell^.MergedNeighbors := [cbEast, cbWest];
|
||||
base := GetCell(ARow1, ACol1);
|
||||
for r := ARow1 to ARow2 do
|
||||
for c := ACol1 to ACol2 do
|
||||
begin
|
||||
cell := GetCell(r, c);
|
||||
cell^.MergeBase := base;
|
||||
end;
|
||||
end else
|
||||
// Case 3: single column
|
||||
if (ARow1 <> ARow2) and (ACol1 = ACol2) then begin
|
||||
cell := GetCell(ARow1, ACol1);
|
||||
cell^.MergedNeighbors := [cbSouth];
|
||||
cell := GetCell(ARow2, ACol2);
|
||||
cell^.MergedNeighbors := [cbNorth];
|
||||
for r := ARow1+1 to ARow2-1 do begin
|
||||
cell := GetCell(r, ACol1);
|
||||
cell^.MergedNeighbors := [cbNorth, cbSouth];
|
||||
end;
|
||||
end else
|
||||
// case 4: general case
|
||||
begin
|
||||
// left/top corner
|
||||
cell := GetCell(ARow1, ACol1);
|
||||
cell^.MergedNeighbors := [cbEast, cbSouth];
|
||||
// right/top corner
|
||||
cell := GetCell(ARow1, ACol2);
|
||||
cell^.MergedNeighbors := [cbWest, cbSouth];
|
||||
// left/bottom corner
|
||||
cell := GetCell(ARow2, ACol1);
|
||||
cell^.MergedNeighbors := [cbEast, cbNorth];
|
||||
// right/bottom corner
|
||||
cell := GetCell(ARow2, ACol2);
|
||||
cell^.MergedNeighbors := [cbWest, cbNorth];
|
||||
// top row
|
||||
for c := ACol1+1 to ACol2-1 do begin
|
||||
cell := GetCell(ARow1, c);
|
||||
cell^.MergedNeighbors := [cbSouth, cbEast, cbWest];
|
||||
end;
|
||||
// bottom row
|
||||
for c := ACol1+1 to ACol2-1 do begin
|
||||
cell := GetCell(ARow2, c);
|
||||
cell^.MergedNeighbors := [cbNorth, cbEast, cbWest];
|
||||
end;
|
||||
// left column
|
||||
for r := ARow1+1 to ARow2-1 do begin
|
||||
cell := GetCell(r, ACol1);
|
||||
cell^.MergedNeighbors := [cbEast, cbNorth, cbSouth];
|
||||
end;
|
||||
// right column
|
||||
for r := ARow1+1 to ARow2-1 do begin
|
||||
cell := GetCell(r, ACol2);
|
||||
cell^.MergedNeighbors := [cbWest, cbNorth, cbSouth];
|
||||
end;
|
||||
// inner
|
||||
for r := ARow1+1 to ARow2-1 do
|
||||
for c := ACol1+1 to ACol2-1 do begin
|
||||
cell := GetCell(r, c);
|
||||
cell^.MergedNeighbors := [cbEast, cbWest, cbNorth, cbSouth];
|
||||
end;
|
||||
end;
|
||||
ChangedCell(ARow1, ACol1);
|
||||
end;
|
||||
|
||||
@ -3008,9 +2954,9 @@ end;
|
||||
}
|
||||
procedure TsWorksheet.UnmergeCells(ARow, ACol: Cardinal);
|
||||
var
|
||||
cell: PCell;
|
||||
r, c: Cardinal;
|
||||
r1, c1, r2, c2: Cardinal;
|
||||
cell: PCell;
|
||||
begin
|
||||
cell := FindCell(ARow, ACol);
|
||||
if not FindMergedRange(cell, r1, c1, r2, c2) then
|
||||
@ -3020,7 +2966,8 @@ begin
|
||||
begin
|
||||
cell := FindCell(r, c);
|
||||
if cell <> nil then
|
||||
cell^.MergedNeighbors := [];
|
||||
cell^.MergeBase := nil;
|
||||
// cell^.MergedNeighbors := [];
|
||||
end;
|
||||
ChangedCell(ARow, ACol);
|
||||
end;
|
||||
@ -3054,6 +3001,13 @@ end;
|
||||
nil.
|
||||
}
|
||||
function TsWorksheet.FindMergeBase(ACell: PCell): PCell;
|
||||
begin
|
||||
if ACell = nil then
|
||||
Result := nil
|
||||
else
|
||||
Result := ACell^.MergeBase;
|
||||
end;
|
||||
(*
|
||||
var
|
||||
r, c: Cardinal;
|
||||
begin
|
||||
@ -3072,7 +3026,7 @@ begin
|
||||
Result := FindCell(r, c);
|
||||
end;
|
||||
end;
|
||||
|
||||
*)
|
||||
{@@
|
||||
Determines the merged cell block to which a given cell belongs
|
||||
|
||||
@ -3090,15 +3044,38 @@ function TsWorksheet.FindMergedRange(ACell: PCell;
|
||||
var
|
||||
r, c: Cardinal;
|
||||
cell: PCell;
|
||||
base: PCell;
|
||||
begin
|
||||
cell := FindMergeBase(ACell);
|
||||
if cell = nil then begin
|
||||
base := FindMergeBase(ACell);
|
||||
if base = nil then begin
|
||||
Result := false;
|
||||
exit;
|
||||
end;
|
||||
ARow1 := cell^.Row;
|
||||
ACol1 := cell^.Col;
|
||||
// Assuming that the merged block is rectangular, we start at merge base...
|
||||
ARow1 := base^.Row;
|
||||
ARow2 := ARow1;
|
||||
ACol1 := base^.Col;
|
||||
ACol2 := ACol1;
|
||||
// ... and go along first COLUMN to find the end of the merged block, ...
|
||||
for c := ACol1+1 to GetLastColIndex do
|
||||
begin
|
||||
cell := FindCell(ARow1, c);
|
||||
if (cell = nil) or (cell^.MergeBase <> base) then
|
||||
break
|
||||
else
|
||||
ACol2 := c;
|
||||
end;
|
||||
// ... and go along first ROW to find the end of the merged block
|
||||
for r := ARow1 + 1 to GetLastRowIndex do
|
||||
begin
|
||||
cell := FindCell(r, ACol1);
|
||||
if (cell = nil) or (cell^.MergeBase <> base) then
|
||||
break
|
||||
else
|
||||
ARow2 := r;
|
||||
end;
|
||||
|
||||
{
|
||||
while (cell <> nil) and (cbSouth in cell^.MergedNeighbors) do begin
|
||||
inc(ARow2);
|
||||
cell := FindCell(ARow2, ACol1);
|
||||
@ -3116,6 +3093,7 @@ begin
|
||||
Result := false;
|
||||
exit;
|
||||
end;
|
||||
}
|
||||
Result := true;
|
||||
end;
|
||||
|
||||
@ -3145,9 +3123,9 @@ begin
|
||||
SetLength(AList, n+1);
|
||||
AList[n] := rng;
|
||||
inc(n);
|
||||
c := rng.Col2; // jump to next cell not belonging to this block
|
||||
c := rng.Col2; // jump to last cell of block
|
||||
end;
|
||||
inc(c);
|
||||
inc(c); // go to next cell
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
@ -3161,11 +3139,24 @@ end;
|
||||
}
|
||||
function TsWorksheet.IsMergeBase(ACell: PCell): Boolean;
|
||||
begin
|
||||
Result := (ACell <> nil) and (ACell = ACell^.MergeBase);
|
||||
{
|
||||
Result := (ACell <> nil) and (
|
||||
(ACell^.MergedNeighbors = [cbEast]) or // single row
|
||||
(ACell^.MergedNeighbors = [cbSouth]) or // single column
|
||||
(ACell^.MergedNeighbors = [cbEast, cbSouth]) // 2d
|
||||
);
|
||||
}
|
||||
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 (ACell^.MergeBase <> nil);
|
||||
end;
|
||||
|
||||
{@@
|
||||
@ -5033,36 +5024,50 @@ var
|
||||
begin
|
||||
col := PtrInt(arg);
|
||||
cell := PCell(data);
|
||||
if cell = nil then // This should not happen. Just to make sure...
|
||||
exit;
|
||||
|
||||
// Update column index of moved cells
|
||||
if cell^.Col >= col then
|
||||
if (cell^.Col >= col) then
|
||||
// Update column index of moved cell
|
||||
inc(cell^.Col);
|
||||
|
||||
// Update formulas
|
||||
// (1) create an rpn formula
|
||||
formula := BuildRPNFormula(cell);
|
||||
// (2) update cell addresses affected by the insertion of a column
|
||||
for i:=0 to Length(formula)-1 do begin
|
||||
fe := Formula[i]; // "fe" means "formula element"
|
||||
case fe.ElementKind of
|
||||
fekCell, fekCellRef:
|
||||
if fe.Col >= col then inc(fe.Col);
|
||||
fekCellRange:
|
||||
begin
|
||||
if fe.Col >= col then inc(fe.Col);
|
||||
if fe.Col2 >= col then inc(fe.Col2);
|
||||
if HasFormula(cell) then
|
||||
begin
|
||||
{
|
||||
if cell^.SharedFormulaBase <> cell then
|
||||
begin
|
||||
newCell := GetCell(cell^.Row, col);
|
||||
newCell^.SharedFormulaBase := cell^.SharedFormulaBasse;
|
||||
end else
|
||||
}
|
||||
begin
|
||||
// (1) create an rpn formula
|
||||
formula := BuildRPNFormula(cell);
|
||||
// (2) update cell addresses affected by the insertion of a column
|
||||
for i:=0 to Length(formula)-1 do
|
||||
begin
|
||||
fe := Formula[i]; // "fe" means "formula element"
|
||||
case fe.ElementKind of
|
||||
fekCell, fekCellRef:
|
||||
if fe.Col >= col then inc(fe.Col);
|
||||
fekCellRange:
|
||||
begin
|
||||
if fe.Col >= col then inc(fe.Col);
|
||||
if fe.Col2 >= col then inc(fe.Col2);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
// (3) convert rpn formula back to string formula
|
||||
cell^.FormulaValue := ConvertRPNFormulaToStringFormula(formula);
|
||||
end;
|
||||
end;
|
||||
// (3) convert rpn formula back to string formula
|
||||
cell^.FormulaValue := ConvertRPNFormulaToStringFormula(formula);
|
||||
end;
|
||||
|
||||
{@@
|
||||
Inserts a column BEFORE the index specified. Cells with greater column indexes are
|
||||
moved one column up. Cell references in rpn formulas are considered as well.
|
||||
However, lacking a parser for string formulas, references in string formulas
|
||||
are not changed which may lead to incorrect operation!
|
||||
moved one column to the right. Merged cell blocks and cell references in formulas
|
||||
are considered as well.
|
||||
|
||||
@param ACol Index of the column before which a new column is inserted.
|
||||
}
|
||||
@ -5071,6 +5076,9 @@ var
|
||||
cellnode: TAVLTreeNode;
|
||||
col: PCol;
|
||||
i: Integer;
|
||||
r, c: Cardinal;
|
||||
r1, c1, r2, c2: Cardinal;
|
||||
cell, nextcell, gapcell: PCell;
|
||||
begin
|
||||
// Update column index of cell records
|
||||
cellnode := FCells.FindLowest;
|
||||
@ -5079,6 +5087,26 @@ begin
|
||||
cellnode := FCells.FindSuccessor(cellnode);
|
||||
end;
|
||||
|
||||
// Fix merged cells: If the inserted column runs through a block of merged
|
||||
// cells the block is cut into two pieces. Here we fill the gap with dummy
|
||||
// cells and set their MergeBase correctly.
|
||||
for r := 0 to GetLastRowIndex do
|
||||
for c := 0 to GetLastColIndex do
|
||||
begin
|
||||
cell := FindCell(r, c);
|
||||
if IsMergeBase(cell) then begin
|
||||
FindMergedRange(cell, r1, c1, r2, c2);
|
||||
if ACol = c2 + 1 then begin
|
||||
nextcell := FindCell(r, ACol + 1);
|
||||
if Assigned(nextcell) and (nextcell^.MergeBase = cell) then
|
||||
begin
|
||||
gapcell := GetCell(r, ACol);
|
||||
gapcell^.MergeBase := cell;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
// Update column index of column records
|
||||
for i:=0 to FCols.Count-1 do begin
|
||||
col := PCol(FCols.Items[i]);
|
||||
@ -5106,31 +5134,33 @@ begin
|
||||
if cell^.Row >= row then
|
||||
inc(cell^.Row);
|
||||
|
||||
// Update rpn formulas
|
||||
// (1) create an rpn formula
|
||||
formula := BuildRPNFormula(cell);
|
||||
// (2) update cell addresses affected by the insertion of a column
|
||||
for i:=0 to Length(formula)-1 do begin
|
||||
fe := formula[i]; // "fe" means "formula element"
|
||||
case fe.ElementKind of
|
||||
fekCell, fekCellRef:
|
||||
if fe.Row >= row then inc(fe.Row);
|
||||
fekCellRange:
|
||||
begin
|
||||
// Update formulas
|
||||
if HasFormula(cell) then
|
||||
begin
|
||||
// (1) create an rpn formula
|
||||
formula := BuildRPNFormula(cell);
|
||||
// (2) update cell addresses affected by the insertion of a column
|
||||
for i:=0 to Length(formula)-1 do begin
|
||||
fe := formula[i]; // "fe" means "formula element"
|
||||
case fe.ElementKind of
|
||||
fekCell, fekCellRef:
|
||||
if fe.Row >= row then inc(fe.Row);
|
||||
if fe.Row2 >= row then inc(fe.Row2);
|
||||
end;
|
||||
fekCellRange:
|
||||
begin
|
||||
if fe.Row >= row then inc(fe.Row);
|
||||
if fe.Row2 >= row then inc(fe.Row2);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
// (3) convert rpn formula back to string formula
|
||||
cell^.FormulaValue := ConvertRPNFormulaToStringFormula(formula);
|
||||
end;
|
||||
// (3) convert rpn formula back to string formula
|
||||
cell^.FormulaValue := ConvertRPNFormulaToStringFormula(formula);
|
||||
end;
|
||||
|
||||
{@@
|
||||
Inserts a row BEFORE the row specified. Cells with greater row indexes are
|
||||
moved one row up. Cell references in rpn formulas are considered as well.
|
||||
However, lacking a parser for string formulas, references in string formulas
|
||||
are not changed which may lead to incorrect operation!
|
||||
moved one row down. Merged cell blocks and cell references in formulas are
|
||||
considered as well.
|
||||
|
||||
@param ARow Index of the row before which a new row is inserted.
|
||||
}
|
||||
@ -5139,6 +5169,8 @@ var
|
||||
row: PRow;
|
||||
cellnode: TAVLTreeNode;
|
||||
i: Integer;
|
||||
r, c, r1, c1, r2, c2: Cardinal;
|
||||
cell, nextcell, gapcell: PCell;
|
||||
begin
|
||||
// Update row index of cell records
|
||||
cellnode := FCells.FindLowest;
|
||||
@ -5147,6 +5179,26 @@ begin
|
||||
cellnode := FCells.FindSuccessor(cellnode);
|
||||
end;
|
||||
|
||||
// Fix merged cells: If the inserted row runs through a block of merged
|
||||
// cells the block is cut into two pieces. Here we fill the gap with dummy
|
||||
// cells and set their MergeBase correctly.
|
||||
for r := 0 to GetLastRowIndex do
|
||||
for c := 0 to GetLastColIndex do
|
||||
begin
|
||||
cell := FindCell(r, c);
|
||||
if IsMergeBase(cell) then begin
|
||||
FindMergedRange(cell, r1, c1, r2, c2);
|
||||
if ARow = r2 + 1 then begin
|
||||
nextcell := FindCell(ARow + 1, c);
|
||||
if Assigned(nextcell) and (nextcell^.MergeBase = cell) then
|
||||
begin
|
||||
gapcell := GetCell(ARow, c);
|
||||
gapcell^.MergeBase := cell;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
// Update row index of row records
|
||||
for i:=0 to FRows.Count-1 do begin
|
||||
row := PRow(FRows.Items[i]);
|
||||
|
@ -1489,7 +1489,8 @@ begin
|
||||
then
|
||||
Continue;
|
||||
// Overflow possible from non-merged, non-right-aligned, horizontal label cells
|
||||
if (cell^.MergedNeighbors = []) and (cell^.ContentType = cctUTF8String) and
|
||||
// if (cell^.MergedNeighbors = []) and (cell^.ContentType = cctUTF8String) and
|
||||
if (cell^.MergeBase = nil) and (cell^.ContentType = cctUTF8String) and
|
||||
not (uffTextRotation in cell^.UsedFormattingFields) and
|
||||
(uffHorAlign in cell^.UsedFormattingFields) and (cell^.HorAlignment <> haRight)
|
||||
then
|
||||
@ -1514,7 +1515,8 @@ begin
|
||||
then
|
||||
continue;
|
||||
// Overflow possible from non-merged, horizontal, non-left-aligned label cells
|
||||
if (cell^.MergedNeighbors = []) and (cell^.ContentType = cctUTF8String) and
|
||||
// if (cell^.MergedNeighbors = []) and (cell^.ContentType = cctUTF8String) and
|
||||
if (cell^.MergeBase = nil) and (cell^.ContentType = cctUTF8String) and
|
||||
not (uffTextRotation in cell^.UsedFormattingFields) and
|
||||
(uffHorAlign in cell^.UsedFormattingFields) and (cell^.HorAlignment <> haLeft)
|
||||
then
|
||||
@ -1535,7 +1537,9 @@ begin
|
||||
if Assigned(FWorksheet) and (gr >= FixedRows) and (gc >= FixedCols) then
|
||||
begin
|
||||
cell := FWorksheet.FindCell(GetWorksheetRow(gr), GetWorksheetCol(gc));
|
||||
if (cell = nil) or (cell^.MergedNeighbors = []) then begin
|
||||
//if (cell = nil) or (cell^.MergedNeighbors = []) then begin
|
||||
if (cell = nil) or (cell^.Mergebase = nil) then
|
||||
begin
|
||||
// single cell
|
||||
FDrawingCell := cell;
|
||||
if FTextOverflow then
|
||||
@ -2265,7 +2269,9 @@ begin
|
||||
|
||||
lCell := FWorksheet.FindCell(ARow-FHeaderCount, ACol-FHeaderCount);
|
||||
if lCell <> nil then begin
|
||||
if lCell^.MergedNeighbors <> [] then begin
|
||||
//if lCell^.MergedNeighbors <> [] then begin
|
||||
if (lCell^.Mergebase <> nil) then
|
||||
begin
|
||||
FWorksheet.FindMergedRange(lCell, r1, c1, r2, c2);
|
||||
if r1 <> r2 then
|
||||
// If the merged range encloses several rows we skip automatic row height
|
||||
@ -2323,7 +2329,7 @@ end;
|
||||
@param ACol Grid column index of the cell
|
||||
@param ARow Grid row index of the cell
|
||||
@return Text to be displayed in the cell.
|
||||
}
|
||||
}
|
||||
function TsCustomWorksheetGrid.GetCellText(ACol, ARow: Integer): String;
|
||||
var
|
||||
lCell: PCell;
|
||||
@ -2362,7 +2368,6 @@ begin
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
// else Result := 'NIL';
|
||||
end;
|
||||
end;
|
||||
|
||||
@ -2374,7 +2379,7 @@ end;
|
||||
@param ACol Grid column index of the cell being edited
|
||||
@param ARow Grid row index of the grid cell being edited
|
||||
@return Text to be passed to the cell editor.
|
||||
}
|
||||
}
|
||||
function TsCustomWorksheetGrid.GetEditText(aCol, aRow: Integer): string;
|
||||
begin
|
||||
Result := GetCellText(aCol, aRow);
|
||||
@ -2383,7 +2388,9 @@ end;
|
||||
|
||||
{ Determines the style of the border between a cell and its neighbor given by
|
||||
ADeltaCol and ADeltaRow (one of them must be 0, the other one can only be +/-1).
|
||||
ACol and ARow are in grid units. }
|
||||
ACol and ARow are in grid units.
|
||||
Result is FALSE if there is no border line.
|
||||
}
|
||||
function TsCustomWorksheetGrid.GetBorderStyle(ACol, ARow, ADeltaCol, ADeltaRow: Integer;
|
||||
out ABorderStyle: TsCellBorderStyle): Boolean;
|
||||
var
|
||||
@ -2408,37 +2415,46 @@ begin
|
||||
border := cbSouth;
|
||||
neighborBorder := cbNorth;
|
||||
end else
|
||||
raise Exception.Create('TsCustomWorksheetGrid: incorrect col/row for GetBorderStyle.');
|
||||
raise Exception.Create('[TsCustomWorksheetGrid] Incorrect col/row for GetBorderStyle.');
|
||||
|
||||
r := GetWorksheetRow(ARow);
|
||||
c := GetWorksheetCol(ACol);
|
||||
cell := FWorksheet.FindCell(r, c);
|
||||
if (r+ADeltaRow < 0) or (c + ADeltaCol < 0) then
|
||||
if (ARow - FHeaderCount + ADeltaRow < 0) or (ACol - FHeaderCount + ADeltaCol < 0) then
|
||||
neighborcell := nil
|
||||
else
|
||||
neighborcell := FWorksheet.FindCell(r+ADeltaRow, c+ADeltaCol);
|
||||
neighborcell := FWorksheet.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 ((cell <> nil) and (border in cell^.Border)) and
|
||||
((neighborcell = nil) or (neighborborder in neighborcell^.Border))
|
||||
then }
|
||||
ABorderStyle := cell^.BorderStyles[border]
|
||||
begin
|
||||
if FWorksheet.IsMerged(cell) and FWorksheet.IsMerged(neighborcell) and
|
||||
(cell^.MergeBase = neighborcell^.Mergebase)
|
||||
then
|
||||
result := false
|
||||
else
|
||||
ABorderStyle := cell^.BorderStyles[border]
|
||||
end
|
||||
else
|
||||
// Only neighbor has border, cell has not
|
||||
if not HasBorder(cell, border) and HasBorder(neighborCell, neighborBorder) then
|
||||
{
|
||||
if ((cell = nil) or not (border in cell^.Border)) and
|
||||
(neighborcell <> nil) and (neighborborder in neighborcell^.Border)
|
||||
then
|
||||
}
|
||||
ABorderStyle := neighborcell^.BorderStyles[neighborborder]
|
||||
begin
|
||||
if FWorksheet.IsMerged(cell) and FWorksheet.IsMerged(neighborcell) and
|
||||
(cell^.MergeBase = neighborcell^.Mergebase)
|
||||
then
|
||||
result := false
|
||||
else
|
||||
ABorderStyle := neighborcell^.BorderStyles[neighborborder]
|
||||
end
|
||||
else
|
||||
// Both cells have shared border -> use top or left border
|
||||
if HasBorder(cell, border) and HasBorder(neighborCell, neighborBorder) then begin
|
||||
{
|
||||
if (cell <> nil) and (border in cell^.Border) and
|
||||
(neighborcell <> nil) and (neighborborder in neighborcell^.Border)
|
||||
then begin
|
||||
}
|
||||
if HasBorder(cell, border) and HasBorder(neighborCell, neighborBorder) then
|
||||
begin
|
||||
if FWorksheet.IsMerged(cell) and FWorksheet.IsMerged(neighborcell) and
|
||||
(cell^.MergeBase = neighborcell^.Mergebase)
|
||||
then
|
||||
result := false
|
||||
else
|
||||
if (border in [cbNorth, cbWest]) then
|
||||
ABorderStyle := neighborcell^.BorderStyles[neighborborder]
|
||||
else
|
||||
@ -2457,7 +2473,7 @@ end;
|
||||
}
|
||||
function TsCustomWorksheetGrid.GetGridCol(ASheetCol: Cardinal): Integer;
|
||||
begin
|
||||
Result := ASheetCol + FHeaderCount
|
||||
Result := Integer(ASheetCol) + FHeaderCount
|
||||
end;
|
||||
|
||||
{@@
|
||||
@ -2470,7 +2486,7 @@ end;
|
||||
}
|
||||
function TsCustomWorksheetGrid.GetGridRow(ASheetRow: Cardinal): Integer;
|
||||
begin
|
||||
Result := ASheetRow + FHeaderCount;
|
||||
Result := Integer(ASheetRow) + FHeaderCount;
|
||||
end;
|
||||
|
||||
function TsCustomWorksheetGrid.GetHorAlignment(ACol, ARow: Integer): TsHorAlignment;
|
||||
@ -2700,7 +2716,7 @@ begin
|
||||
if AGridCol < FHeaderCount then
|
||||
exit;
|
||||
|
||||
if FWorksheet.GetLastColIndex+1 + FHeaderCount >= FInitColCount then
|
||||
if FWorksheet.GetLastColIndex + 1 + FHeaderCount >= FInitColCount then
|
||||
ColCount := ColCount + 1;
|
||||
c := AGridCol - FHeaderCount;
|
||||
FWorksheet.InsertCol(c);
|
||||
@ -2833,7 +2849,7 @@ begin
|
||||
P := ARect.TopLeft;
|
||||
case AJustification of
|
||||
0: ts.Alignment := taLeftJustify;
|
||||
1: if (FDrawingCell <> nil) and (FDrawingCell^.MergedNeighbors = []) then
|
||||
1: if (FDrawingCell <> nil) and (FDrawingCell^.MergeBase = nil) then //(FDrawingCell^.MergedNeighbors = []) then
|
||||
begin
|
||||
// Special treatment for overflowing cells: they must be centered
|
||||
// at their original column, not in the total enclosing rectangle.
|
||||
|
Reference in New Issue
Block a user