fpspreadsheet: Delete columns and rows from Worksheet and WorksheetGrid

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3571 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2014-09-16 15:03:17 +00:00
parent eb4dbd80e5
commit 10ad6b7a1a
5 changed files with 335 additions and 53 deletions

View File

@ -123,7 +123,7 @@ object MainFrm: TMainFrm
Action = AcSaveAs Action = AcSaveAs
end end
object ToolButton3: TToolButton object ToolButton3: TToolButton
Left = 149 Left = 200
Top = 0 Top = 0
Action = AcQuit Action = AcQuit
end end
@ -140,7 +140,7 @@ object MainFrm: TMainFrm
Action = AcEdit Action = AcEdit
end end
object ToolButton6: TToolButton object ToolButton6: TToolButton
Left = 144 Left = 98
Top = 0 Top = 0
Width = 5 Width = 5
Caption = 'ToolButton6' Caption = 'ToolButton6'
@ -152,15 +152,32 @@ object MainFrm: TMainFrm
Action = AcNew Action = AcNew
end end
object ToolButton23: TToolButton object ToolButton23: TToolButton
Left = 98 Left = 103
Top = 0 Top = 0
Action = AcAddColumn Action = AcAddColumn
end end
object ToolButton27: TToolButton object ToolButton27: TToolButton
Left = 121 Left = 126
Top = 0 Top = 0
Action = AcAddRow Action = AcAddRow
end end
object ToolButton29: TToolButton
Left = 149
Top = 0
Action = AcDeleteColumn
end
object ToolButton30: TToolButton
Left = 172
Top = 0
Action = AcDeleteRow
end
object ToolButton31: TToolButton
Left = 195
Top = 0
Width = 5
Caption = 'ToolButton31'
Style = tbsDivider
end
end end
object FormatToolBar: TToolBar object FormatToolBar: TToolBar
Left = 0 Left = 0
@ -456,7 +473,6 @@ object MainFrm: TMainFrm
AutoAdvance = aaDown AutoAdvance = aaDown
BorderStyle = bsNone BorderStyle = bsNone
ColCount = 27 ColCount = 27
ExtendedSelect = False
MouseWheelOption = mwGrid MouseWheelOption = mwGrid
Options = [goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine, goRangeSelect, goRowSizing, goColSizing, goThumbTracking, goSmoothScroll, goFixedColSizing] Options = [goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine, goRangeSelect, goRowSizing, goColSizing, goThumbTracking, goSmoothScroll, goFixedColSizing]
RowCount = 101 RowCount = 101
@ -800,6 +816,15 @@ object MainFrm: TMainFrm
FF00A2AE8EFF5F9771FF4F8E66FF49895FFFA2AE8EFFFFFFFF00 FF00A2AE8EFF5F9771FF4F8E66FF49895FFFA2AE8EFFFFFFFF00
} }
end end
object MenuItem72: TMenuItem
Caption = '-'
end
object MenuItem73: TMenuItem
Action = AcDeleteColumn
end
object MenuItem74: TMenuItem
Action = AcDeleteRow
end
end end
object mnuFormat: TMenuItem object mnuFormat: TMenuItem
Caption = 'Format' Caption = 'Format'
@ -3036,6 +3061,20 @@ object MainFrm: TMainFrm
Hint = 'Show/hide grid lines' Hint = 'Show/hide grid lines'
OnExecute = AcShowGridlinesExecute OnExecute = AcShowGridlinesExecute
end end
object AcDeleteColumn: TAction
Category = 'Edit'
Caption = 'Delete column'
Hint = 'Delete column'
ImageIndex = 38
OnExecute = AcDeleteColumnExecute
end
object AcDeleteRow: TAction
Category = 'Edit'
Caption = 'Delete row'
Hint = 'Delete row'
ImageIndex = 37
OnExecute = AcDeleteRowExecute
end
end end
object FontDialog: TFontDialog object FontDialog: TFontDialog
MinFontSize = 0 MinFontSize = 0

View File

@ -75,6 +75,8 @@ type
AcMergeCells: TAction; AcMergeCells: TAction;
AcShowHeaders: TAction; AcShowHeaders: TAction;
AcShowGridlines: TAction; AcShowGridlines: TAction;
AcDeleteColumn: TAction;
AcDeleteRow: TAction;
AcViewInspector: TAction; AcViewInspector: TAction;
AcWordwrap: TAction; AcWordwrap: TAction;
AcVAlignDefault: TAction; AcVAlignDefault: TAction;
@ -161,6 +163,9 @@ type
MenuItem69: TMenuItem; MenuItem69: TMenuItem;
MenuItem70: TMenuItem; MenuItem70: TMenuItem;
MenuItem71: TMenuItem; MenuItem71: TMenuItem;
MenuItem72: TMenuItem;
MenuItem73: TMenuItem;
MenuItem74: TMenuItem;
mnuInspector: TMenuItem; mnuInspector: TMenuItem;
mnuView: TMenuItem; mnuView: TMenuItem;
MnuFmtDateTimeMSZ: TMenuItem; MnuFmtDateTimeMSZ: TMenuItem;
@ -223,6 +228,9 @@ type
ToolButton27: TToolButton; ToolButton27: TToolButton;
CellInspector: TValueListEditor; CellInspector: TValueListEditor;
ToolButton28: TToolButton; ToolButton28: TToolButton;
ToolButton29: TToolButton;
ToolButton30: TToolButton;
ToolButton31: TToolButton;
WorksheetGrid: TsWorksheetGrid; WorksheetGrid: TsWorksheetGrid;
ToolBar1: TToolBar; ToolBar1: TToolBar;
FormatToolBar: TToolBar; FormatToolBar: TToolBar;
@ -256,6 +264,8 @@ type
procedure AcAddRowExecute(Sender: TObject); procedure AcAddRowExecute(Sender: TObject);
procedure AcBorderExecute(Sender: TObject); procedure AcBorderExecute(Sender: TObject);
procedure AcCopyFormatExecute(Sender: TObject); procedure AcCopyFormatExecute(Sender: TObject);
procedure AcDeleteColumnExecute(Sender: TObject);
procedure AcDeleteRowExecute(Sender: TObject);
procedure AcEditExecute(Sender: TObject); procedure AcEditExecute(Sender: TObject);
procedure AcFontExecute(Sender: TObject); procedure AcFontExecute(Sender: TObject);
procedure AcFontStyleExecute(Sender: TObject); procedure AcFontStyleExecute(Sender: TObject);
@ -473,6 +483,24 @@ begin
end; end;
end; end;
procedure TMainFrm.AcDeleteColumnExecute(Sender: TObject);
var
c: Integer;
begin
c := WorksheetGrid.Col;
WorksheetGrid.DeleteCol(c);
WorksheetGrid.Col := c;
end;
procedure TMainFrm.AcDeleteRowExecute(Sender: TObject);
var
r: Integer;
begin
r := WorksheetGrid.Row;
WorksheetGrid.DeleteRow(r);
WorksheetGrid.Row := r;
end;
{ Changes the font of the selected cell by calling a standard font dialog. } { Changes the font of the selected cell by calling a standard font dialog. }
procedure TMainFrm.AcFontExecute(Sender: TObject); procedure TMainFrm.AcFontExecute(Sender: TObject);
begin begin

View File

@ -3066,8 +3066,8 @@ begin
cell := ASheet.FindCell(r, c); cell := ASheet.FindCell(r, c);
// Belongs to merged block? // 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^.MergeBase <> nil) then if (cell <> nil) and not FWorksheet.IsMergeBase(cell) and (cell^.MergeBase <> nil) then
// this means: all cells of a merged block except for the merge base
begin begin
AppendToStream(AStream, AppendToStream(AStream,
'<table:covered-table-cell />'); '<table:covered-table-cell />');

View File

@ -93,59 +93,13 @@ type
{ Functions - they are identified by their name } { Functions - they are identified by their name }
fekFunc fekFunc
); );
(*
TFEKind = (
{ Basic operands }
fekCell, fekCellRef, fekCellRange, fekCellOffset, fekNum, fekInteger,
fekString, fekBool, fekErr, fekMissingArg,
{ Basic operations }
fekAdd, fekSub, fekMul, fekDiv, fekPercent, fekPower, fekUMinus, fekUPlus,
fekConcat, // string concatenation
fekEqual, fekGreater, fekGreaterEqual, fekLess, fekLessEqual, fekNotEqual,
fekParen,
{ Built-in/Worksheet Functions}
// math
fekABS, fekACOS, fekACOSH, fekASIN, fekASINH, fekATAN, fekATANH,
fekCOS, fekCOSH, fekDEGREES, fekEXP, fekINT, fekLN, fekLOG,
fekLOG10, fekPI, fekRADIANS, fekRAND, fekROUND,
fekSIGN, fekSIN, fekSINH, fekSQRT,
fekTAN, fekTANH,
// date/time
fekDATE, fekDATEDIF, fekDATEVALUE, fekDAY, fekHOUR, fekMINUTE, fekMONTH,
fekNOW, fekSECOND, fekTIME, fekTIMEVALUE, fekTODAY, fekWEEKDAY, fekYEAR,
// statistical
fekAVEDEV, fekAVERAGE, fekBETADIST, fekBETAINV, fekBINOMDIST, fekCHIDIST,
fekCHIINV, fekCOUNT, fekCOUNTA, fekCOUNTBLANK, fekCOUNTIF,
fekMAX, fekMEDIAN, fekMIN, fekPERMUT, fekPOISSON, fekPRODUCT,
fekSTDEV, fekSTDEVP, fekSUM, fekSUMIF, fekSUMSQ, fekVAR, fekVARP,
// financial
fekFV, fekNPER, fekPMT, fekPV, fekRATE,
// logical
fekAND, fekFALSE, fekIF, fekNOT, fekOR, fekTRUE,
// string
fekCHAR, fekCODE, fekLEFT, fekLOWER, fekMID, fekPROPER, fekREPLACE, fekRIGHT,
fekSUBSTITUTE, fekTRIM, fekUPPER,
// lookup/reference
fekCOLUMN, fekCOLUMNS, fekROW, fekROWS,
// info
fekCELLINFO, fekINFO, fekIsBLANK, fekIsERR, fekIsERROR,
fekIsLOGICAL, fekIsNA, fekIsNONTEXT, fekIsNUMBER, fekIsRef, fekIsTEXT,
fekValue,
{ Other operations }
fekOpSUM {Unary sum operation. Note: CANNOT be used for summing sell contents; use fekSUM}
);
*)
{@@ These tokens identify operands in RPN formulas. } {@@ These tokens identify operands in RPN formulas. }
TOperandTokens = fekCell..fekMissingArg; TOperandTokens = fekCell..fekMissingArg;
{@@ These tokens identify basic operations in RPN formulas. } {@@ These tokens identify basic operations in RPN formulas. }
TBasicOperationTokens = fekAdd..fekParen; TBasicOperationTokens = fekAdd..fekParen;
(*
{@@ These tokens identify spreadsheet functions in RPN formulas. }
TFuncTokens = fekAbs..fekOpSum;
*)
{@@ Flags to mark the address or a cell or a range of cells to be absolute {@@ Flags to mark the address or a cell or a range of cells to be absolute
or relative. They are used in the set TsRelFlags. } or relative. They are used in the set TsRelFlags. }
TsRelFlag = (rfRelRow, rfRelCol, rfRelRow2, rfRelCol2); TsRelFlag = (rfRelRow, rfRelCol, rfRelRow2, rfRelCol2);
@ -535,6 +489,8 @@ type
{ Callback procedures called when iterating through all cells } { Callback procedures called when iterating through all cells }
procedure CalcFormulaCallback(data, arg: Pointer); procedure CalcFormulaCallback(data, arg: Pointer);
procedure CalcStateCallback(data, arg: Pointer); procedure CalcStateCallback(data, arg: Pointer);
procedure DeleteColCallback(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 RemoveCallback(data, arg: pointer); procedure RemoveCallback(data, arg: pointer);
@ -545,6 +501,8 @@ type
procedure ChangedCell(ARow, ACol: Cardinal); procedure ChangedCell(ARow, ACol: Cardinal);
procedure ChangedFont(ARow, ACol: Cardinal); procedure ChangedFont(ARow, ACol: Cardinal);
procedure RemoveCell(ARow, ACol: Cardinal);
public public
{@@ Name of the sheet. In the popular spreadsheet applications this is {@@ Name of the sheet. In the popular spreadsheet applications this is
displayed at the tab of the sheet. } displayed at the tab of the sheet. }
@ -758,6 +716,8 @@ type
function GetRowHeight(ARow: Cardinal): Single; function GetRowHeight(ARow: Cardinal): Single;
function GetCol(ACol: Cardinal): PCol; function GetCol(ACol: Cardinal): PCol;
function GetColWidth(ACol: Cardinal): Single; function GetColWidth(ACol: Cardinal): Single;
procedure DeleteCol(ACol: Cardinal);
procedure DeleteRow(ARow: Cardinal);
procedure InsertCol(ACol: Cardinal); procedure InsertCol(ACol: Cardinal);
procedure InsertRow(ARow: Cardinal); procedure InsertRow(ARow: Cardinal);
procedure RemoveAllRows; procedure RemoveAllRows;
@ -2070,6 +2030,116 @@ begin
CopyFormat(AFormat, GetCell(AToRow, AToCol)); CopyFormat(AFormat, GetCell(AToRow, AToCol));
end; end;
{@@ Internal call-back procedure for looping through all cells when deleting
a specified column. Deletion happens in DeleteCol BEFORE this callback! }
procedure TsWorksheet.DeleteColCallback(data, arg: Pointer);
var
cell: PCell;
col: PtrInt;
fe: TsFormulaElement;
formula: TsRPNFormula;
i: Integer;
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 cell
if (cell^.Col > col) then
dec(cell^.Col);
// Update formulas
if HasFormula(cell) then
begin
// (1) create an rpn formula
formula := BuildRPNFormula(cell);
// (2) update cell addresses affected by the deletion of the column
for i:=0 to High(formula) do
begin
fe := formula[i]; // "fe" means "formula element"
if (fe.ElementKind in [fekCell, fekCellRef, fekCellRange]) then
begin
if fe.Col = col then
begin
fe.ElementKind := fekErr;
fe.IntValue := ord(errIllegalRef);
end else
if fe.Col > col then
dec(fe.Col);
if (fe.ElementKind = fekCellRange) then
begin
if (fe.Col2 = col) then
begin
fe.ElementKind := fekErr;
fe.IntValue := ord(errIllegalRef);
end
else
if (fe.Col2 > col) then
dec(fe.Col2);
end;
end;
end;
// (3) convert rpn formula back to string formula
cell^.FormulaValue := ConvertRPNFormulaToStringFormula(formula);
end;
end;
{@@ Internal call-back procedure for looping through all cells when deleting
a specified row. Deletion happens in DeleteRow BEFORE this callback! }
procedure TsWorksheet.DeleteRowCallback(data, arg: Pointer);
var
cell: PCell;
row: PtrInt;
fe: TsFormulaElement;
formula: TsRPNFormula;
i: Integer;
begin
row := PtrInt(arg);
cell := PCell(data);
if cell = nil then // This should not happen. Just to make sure...
exit;
// Update row index of moved cell
if (cell^.Row > row) then
dec(cell^.Row);
// Update formulas
if HasFormula(cell) then
begin
// (1) create an rpn formula
formula := BuildRPNFormula(cell);
// (2) update cell addresses affected by the deletion of the column
for i:=0 to High(formula) do
begin
fe := formula[i]; // "fe" means "formula element"
if (fe.ElementKind in [fekCell, fekCellRef, fekCellRange]) then
begin
if fe.Row = row then
begin
fe.ElementKind := fekErr;
fe.IntValue := ord(errIllegalRef);
end else
if fe.Row > row then
dec(fe.Row);
if (fe.ElementKind = fekCellRange) then
begin
if (fe.Row2 = row) then
begin
fe.ElementKind := fekErr;
fe.IntValue := ord(errIllegalRef);
end
else
if (fe.Row2 > row) then
dec(fe.Row2);
end;
end;
end;
// (3) convert rpn formula back to string formula
cell^.FormulaValue := ConvertRPNFormulaToStringFormula(formula);
end;
end;
{@@ {@@
Tries to locate a Cell in the list of already written Cells Tries to locate a Cell in the list of already written Cells
@ -3175,6 +3245,24 @@ begin
FCells.Clear; FCells.Clear;
end; end;
{@@
Removes a cell and releases its memory.
Just for internal usage since it does not modify the other cells affects
}
procedure TsWorksheet.RemoveCell(ARow, ACol: Cardinal);
var
cellnode: TAVLTreeNode;
cell: TCell;
begin
cell.Row := ARow;
cell.Col := ACol;
cellnode := FCells.Find(@cell);
if cellnode <> nil then begin
Dispose(PCell(cellnode.Data));
FCells.Delete(cellnode);
end;
end;
{@@ {@@
Helper method to update internal caching variables Helper method to update internal caching variables
} }
@ -5064,6 +5152,106 @@ begin
end; end;
end; end;
{@@
Deletes the column at the index specified. Cells with greader column indexes are
moved one column to the left. Merged cell blocks and cell references in formulas
are considered as well.
@param ACol Index of the column to be deleted
}
procedure TsWorksheet.DeleteCol(ACol: Cardinal);
var
cellnode: TAVLTreeNode;
col: PCol;
i: Integer;
r, c, rr, cc: Cardinal;
r1, c1, r2, c2: Cardinal;
cell, nextcell: PCell;
begin
// Fix merged cells: If the deleted column is the first column of a merged
// block the merge base has to be set to the second cell.
for r := 0 to GetLastRowIndex do
begin
cell := FindCell(r, ACol);
if IsMergeBase(cell) then begin
FindMergedRange(cell, r1, c1, r2, c2);
nextCell := FindCell(r, c1 + 1);
for rr := r1 to r2 do
for cc := c1+1 to c2 do
begin
cell := FindCell(rr, cc);
cell^.MergeBase := nextcell;
end;
end;
end;
// Delete cells
for r := GetLastRowIndex downto 0 do
RemoveCell(r, ACol);
// Update column index of cell reocrds
cellnode := FCells.FindLowest;
while Assigned(cellnode) do begin
DeleteColCallback(cellnode.Data, pointer(PtrInt(ACol)));
cellnode := FCells.FindSuccessor(cellnode);
end;
// Update last column index
dec(FLastColIndex);
ChangedCell(0, ACol);
end;
{@@
Deletes the row at the index specified. Cells with greader row indexes are
moved one row up. Merged cell blocks and cell references in formulas
are considered as well.
@param ARow Index of the row to be deleted
}
procedure TsWorksheet.DeleteRow(ARow: Cardinal);
var
cellnode: TAVLTreeNode;
row: PRow;
i: Integer;
r, c, rr, cc: Cardinal;
r1, c1, r2, c2: Cardinal;
cell, nextcell: PCell;
begin
// Fix merged cells: If the deleted row is the first row of a merged
// block the merge base has to be set to the begin of the second row.
for c := 0 to GetLastRowIndex do
begin
cell := FindCell(ARow, c);
if IsMergeBase(cell) then begin
FindMergedRange(cell, r1, c1, r2, c2);
nextCell := FindCell(r1 + 1, c1);
for rr := r1+1 to r2 do
for cc := c1 to c2 do
begin
cell := FindCell(rr, cc);
cell^.MergeBase := nextcell;
end;
end;
end;
// Delete cells
for c := GetLastColIndex downto 0 do
RemoveCell(ARow, c);
// Update row index of cell reocrds
cellnode := FCells.FindLowest;
while Assigned(cellnode) do begin
DeleteRowCallback(cellnode.Data, pointer(PtrInt(ARow)));
cellnode := FCells.FindSuccessor(cellnode);
end;
// Update last row index
dec(FLastRowIndex);
ChangedCell(ARow, 0);
end;
{@@ {@@
Inserts a column BEFORE the index specified. Cells with greater column indexes are Inserts a column BEFORE the index specified. Cells with greater column indexes are
moved one column to the right. Merged cell blocks and cell references in formulas moved one column to the right. Merged cell blocks and cell references in formulas

View File

@ -175,6 +175,8 @@ type
destructor Destroy; override; destructor Destroy; override;
procedure BeginUpdate; procedure BeginUpdate;
procedure DefaultDrawCell(ACol, ARow: Integer; var ARect: TRect; AState: TGridDrawState); override; procedure DefaultDrawCell(ACol, ARow: Integer; var ARect: TRect; AState: TGridDrawState); override;
procedure DeleteCol(AGridCol: Integer);
procedure DeleteRow(AGridRow: Integer);
procedure EditingDone; override; procedure EditingDone; override;
procedure EndUpdate; procedure EndUpdate;
procedure GetSheets(const ASheets: TStrings); procedure GetSheets(const ASheets: TStrings);
@ -1033,6 +1035,31 @@ begin
end; end;
end; end;
{@@
Deletes the column specified.
}
procedure TsCustomWorksheetGrid.DeleteCol(AGridCol: Integer);
begin
if AGridCol < FHeaderCount then
exit;
FWorksheet.DeleteCol(GetWorksheetCol(AGridCol));
UpdateColWidths(AGridCol);
end;
{@@
Deletes the row specified.
}
procedure TsCustomWorksheetGrid.DeleteRow(AGridRow: Integer);
begin
if AGridRow < FHeaderCount then
exit;
FWorksheet.DeleteRow(GetWorksheetRow(AGridRow));
UpdateRowHeights(AGridRow);
end;
{@@ {@@
Creates a new empty workbook into which a file will be loaded. Destroys the Creates a new empty workbook into which a file will be loaded. Destroys the
previously used workbook. previously used workbook.