fpspreadsheet: Implement automatic calculation of existing rpn formulas whenever a cell changes.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3476 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2014-08-13 14:05:29 +00:00
parent 0b79376fa6
commit 03913876d3
4 changed files with 141 additions and 35 deletions

View File

@ -13,12 +13,12 @@ object Form1: TForm1
LCLVersion = '1.3' LCLVersion = '1.3'
object Panel1: TPanel object Panel1: TPanel
Left = 0 Left = 0
Height = 85 Height = 78
Top = 529 Top = 536
Width = 884 Width = 884
Align = alBottom Align = alBottom
BevelOuter = bvNone BevelOuter = bvNone
ClientHeight = 85 ClientHeight = 78
ClientWidth = 884 ClientWidth = 884
TabOrder = 0 TabOrder = 0
object CbShowHeaders: TCheckBox object CbShowHeaders: TCheckBox
@ -35,7 +35,7 @@ object Form1: TForm1
object CbShowGridLines: TCheckBox object CbShowGridLines: TCheckBox
Left = 8 Left = 8
Height = 24 Height = 24
Top = 32 Top = 39
Width = 125 Width = 125
Caption = 'Show grid lines' Caption = 'Show grid lines'
Checked = True Checked = True
@ -44,23 +44,23 @@ object Form1: TForm1
TabOrder = 1 TabOrder = 1
end end
object EdFrozenCols: TSpinEdit object EdFrozenCols: TSpinEdit
Left = 389 Left = 645
Height = 28 Height = 28
Top = 8 Top = 8
Width = 52 Width = 52
OnChange = EdFrozenColsChange OnChange = EdFrozenColsChange
TabOrder = 2 TabOrder = 5
end end
object EdFrozenRows: TSpinEdit object EdFrozenRows: TSpinEdit
Left = 389 Left = 645
Height = 28 Height = 28
Top = 39 Top = 39
Width = 52 Width = 52
OnChange = EdFrozenRowsChange OnChange = EdFrozenRowsChange
TabOrder = 3 TabOrder = 6
end end
object Label1: TLabel object Label1: TLabel
Left = 304 Left = 560
Height = 20 Height = 20
Top = 13 Top = 13
Width = 77 Width = 77
@ -69,7 +69,7 @@ object Form1: TForm1
ParentColor = False ParentColor = False
end end
object Label2: TLabel object Label2: TLabel
Left = 304 Left = 560
Height = 20 Height = 20
Top = 40 Top = 40
Width = 82 Width = 82
@ -78,16 +78,16 @@ object Form1: TForm1
ParentColor = False ParentColor = False
end end
object CbReadFormulas: TCheckBox object CbReadFormulas: TCheckBox
Left = 8 Left = 160
Height = 24 Height = 24
Top = 56 Top = 8
Width = 120 Width = 120
Caption = 'Read formulas' Caption = 'Read formulas'
OnChange = CbReadFormulasChange OnChange = CbReadFormulasChange
TabOrder = 4 TabOrder = 2
end end
object CbHeaderStyle: TComboBox object CbHeaderStyle: TComboBox
Left = 152 Left = 408
Height = 28 Height = 28
Top = 8 Top = 8
Width = 116 Width = 116
@ -100,13 +100,22 @@ object Form1: TForm1
) )
OnChange = CbHeaderStyleChange OnChange = CbHeaderStyleChange
Style = csDropDownList Style = csDropDownList
TabOrder = 5 TabOrder = 4
Text = 'Native' Text = 'Native'
end end
object CbAutoCalcFormulas: TCheckBox
Left = 160
Height = 24
Top = 39
Width = 158
Caption = 'Calculate on change'
OnChange = CbAutoCalcFormulasChange
TabOrder = 3
end
end end
object PageControl1: TPageControl object PageControl1: TPageControl
Left = 0 Left = 0
Height = 450 Height = 457
Top = 79 Top = 79
Width = 884 Width = 884
ActivePage = TabSheet1 ActivePage = TabSheet1
@ -116,11 +125,11 @@ object Form1: TForm1
OnChange = PageControl1Change OnChange = PageControl1Change
object TabSheet1: TTabSheet object TabSheet1: TTabSheet
Caption = 'Sheet1' Caption = 'Sheet1'
ClientHeight = 417 ClientHeight = 424
ClientWidth = 876 ClientWidth = 876
object WorksheetGrid: TsWorksheetGrid object WorksheetGrid: TsWorksheetGrid
Left = 0 Left = 0
Height = 417 Height = 424
Top = 0 Top = 0
Width = 876 Width = 876
FrozenCols = 0 FrozenCols = 0

View File

@ -82,6 +82,7 @@ type
CbBackgroundColor: TColorBox; CbBackgroundColor: TColorBox;
CbReadFormulas: TCheckBox; CbReadFormulas: TCheckBox;
CbHeaderStyle: TComboBox; CbHeaderStyle: TComboBox;
CbAutoCalcFormulas: TCheckBox;
EdFormula: TEdit; EdFormula: TEdit;
EdCellAddress: TEdit; EdCellAddress: TEdit;
FontComboBox: TComboBox; FontComboBox: TComboBox;
@ -252,6 +253,7 @@ type
procedure AcTextRotationExecute(Sender: TObject); procedure AcTextRotationExecute(Sender: TObject);
procedure AcVertAlignmentExecute(Sender: TObject); procedure AcVertAlignmentExecute(Sender: TObject);
procedure AcWordwrapExecute(Sender: TObject); procedure AcWordwrapExecute(Sender: TObject);
procedure CbAutoCalcFormulasChange(Sender: TObject);
procedure CbBackgroundColorSelect(Sender: TObject); procedure CbBackgroundColorSelect(Sender: TObject);
procedure CbHeaderStyleChange(Sender: TObject); procedure CbHeaderStyleChange(Sender: TObject);
procedure CbReadFormulasChange(Sender: TObject); procedure CbReadFormulasChange(Sender: TObject);
@ -603,6 +605,11 @@ begin
with WorksheetGrid do Wordwraps[Selection] := TAction(Sender).Checked; with WorksheetGrid do Wordwraps[Selection] := TAction(Sender).Checked;
end; end;
procedure TForm1.CbAutoCalcFormulasChange(Sender: TObject);
begin
WorksheetGrid.AutoCalc := CbAutoCalcFormulas.Checked;;
end;
procedure TForm1.CbBackgroundColorSelect(Sender: TObject); procedure TForm1.CbBackgroundColorSelect(Sender: TObject);
begin begin
with WorksheetGrid do BackgroundColors[Selection] := CbBackgroundColor.ItemIndex; with WorksheetGrid do BackgroundColors[Selection] := CbBackgroundColor.ItemIndex;

View File

@ -676,8 +676,11 @@ type
function WriteWordwrap(ARow, ACol: Cardinal; AValue: boolean): PCell; overload; function WriteWordwrap(ARow, ACol: Cardinal; AValue: boolean): PCell; overload;
procedure WriteWordwrap(ACell: PCell; AValue: boolean); overload; procedure WriteWordwrap(ACell: PCell; AValue: boolean); overload;
{ Data manipulation methods - For Cells } { Formulas }
procedure CalcFormulas; procedure CalcFormulas;
function CellUsedInFormula(ARow, ACol: Cardinal): Boolean;
{ Data manipulation methods - For Cells }
procedure CopyCell(AFromRow, AFromCol, AToRow, AToCol: Cardinal; AFromWorksheet: TsWorksheet); procedure CopyCell(AFromRow, AFromCol, AToRow, AToCol: Cardinal; AFromWorksheet: TsWorksheet);
procedure CopyFormat(AFormat: PCell; AToRow, AToCol: Cardinal); overload; procedure CopyFormat(AFormat: PCell; AToRow, AToCol: Cardinal); overload;
procedure CopyFormat(AFromCell, AToCell: PCell); overload; procedure CopyFormat(AFromCell, AToCell: PCell); overload;
@ -755,8 +758,11 @@ type
@param boBufStream When this option is set a buffered stream is used @param boBufStream When this option is set a buffered stream is used
for writing (a memory stream swapping to disk) or for writing (a memory stream swapping to disk) or
reading (a file stream pre-reading chunks of data reading (a file stream pre-reading chunks of data
to memory) } to memory)
TsWorkbookOption = (boVirtualMode, boBufStream); @param boAutoCalc Automatically recalculate rpn formulas whenever
a cell value changes.
}
TsWorkbookOption = (boVirtualMode, boBufStream, boAutoCalc);
{@@ {@@
Set of options flags for the workbook } Set of options flags for the workbook }
@ -789,11 +795,12 @@ type
FBuiltinFontCount: Integer; FBuiltinFontCount: Integer;
FPalette: array of TsColorValue; FPalette: array of TsColorValue;
FReadFormulas: Boolean; FReadFormulas: Boolean;
FDefaultColWidth: Single; // in "characters". Excel uses the width of char "0" in 1st font FDefaultColWidth: Single; // in "characters". Excel uses the width of char "0" in 1st font
FDefaultRowHeight: Single; // in "character heights", i.e. line count FDefaultRowHeight: Single; // in "character heights", i.e. line count
FVirtualColCount: Cardinal; FVirtualColCount: Cardinal;
FVirtualRowCount: Cardinal; FVirtualRowCount: Cardinal;
FWriting: Boolean; FWriting: Boolean;
FCalculating: Boolean;
FOptions: TsWorkbookOptions; FOptions: TsWorkbookOptions;
FOnWriteCellData: TsWorkbookWriteCellDataEvent; FOnWriteCellData: TsWorkbookWriteCellDataEvent;
FOnReadCellData: TsWorkbookReadCellDataEvent; FOnReadCellData: TsWorkbookReadCellDataEvent;
@ -1207,14 +1214,18 @@ resourcestring
const const
{ These are reserved system colors by Microsoft { These are reserved system colors by Microsoft
0x0040 - Default foreground color - window text color in the sheet display.
0x0041 - Default background color - window background color in the sheet
display and is the default background color for a cell.
0x004D - Default chart foreground color - window text color in the
chart display.
0x004E - Default chart background color - window background color in the
chart display.
0x004F - Chart neutral color which is black, an RGB value of (0,0,0).
0x0051 - ToolTip text color - automatic font color for comments.
0x7FFF - Font automatic color - window text color. }
0x0040 Default foreground color - window text color in the sheet display. // Color indexes of reserved system colors
0x0041 Default background color - window background color in the sheet display and is the default background color for a cell.
0x004D Default chart foreground color - window text color in the chart display.
0x004E Default chart background color - window background color in the chart display.
0x004F Chart neutral color which is black, an RGB value of (0,0,0).
0x0051 ToolTip text color - automatic font color for comments.
0x7FFF Font automatic color - window text color. }
DEF_FOREGROUND_COLOR = $0040; DEF_FOREGROUND_COLOR = $0040;
DEF_BACKGROUND_COLOR = $0041; DEF_BACKGROUND_COLOR = $0041;
DEF_CHART_FOREGROUND_COLOR = $004D; DEF_CHART_FOREGROUND_COLOR = $004D;
@ -1223,6 +1234,7 @@ const
DEF_TOOLTIP_TEXT_COLOR = $0051; DEF_TOOLTIP_TEXT_COLOR = $0051;
DEF_FONT_AUTOMATIC_COLOR = $7FFF; DEF_FONT_AUTOMATIC_COLOR = $7FFF;
// Color rgb values of reserved system colors
DEF_FOREGROUND_COLORVALUE = $000000; DEF_FOREGROUND_COLORVALUE = $000000;
DEF_BACKGROUND_COLORVALUE = $FFFFFF; DEF_BACKGROUND_COLORVALUE = $FFFFFF;
DEF_CHART_FOREGROUND_COLORVALUE = $000000; DEF_CHART_FOREGROUND_COLORVALUE = $000000;
@ -1845,6 +1857,47 @@ begin
Result := GetCellString(ARow, ACol, [rfRelCol, rfRelRow]); Result := GetCellString(ARow, ACol, [rfRelCol, rfRelRow]);
end; end;
{@@
Checks entire workbook, whether this cell is used in any formula.
@param ARow Row index of the cell considered
@param ACol Column index of the cell considered
@return TRUE if the cell is used in a formula, FALSE if not
}
function TsWorksheet.CellUsedInFormula(ARow, ACol: Cardinal): Boolean;
var
cell: PCell;
cellNode: TAVLTreeNode;
fe: TsFormulaElement;
i: Integer;
begin
cellNode := FCells.FindLowest;
while Assigned(cellNode) do begin
cell := PCell(cellNode.Data);
if Length(cell^.RPNFormulaValue) > 0 then
for i := 0 to Length(cell^.RPNFormulaValue)-1 do
begin
fe := cell^.RPNFormulaValue[i];
case fe.ElementKind of
fekCell, fekCellRef:
if (fe.Row = ARow) and (fe.Col = ACol) then
begin
Result := true;
exit;
end;
fekCellRange:
if (fe.Row <= ARow) and (ARow <= fe.Row2) and
(fe.Col <= ACol) and (ACol <= fe.Col2) then
begin
Result := true;
exit;
end;
end;
end;
cellNode := FCells.FindSuccessor(cellNode);
end;
end;
{@@ {@@
Is called whenever a cell value or formatting has changed. Fires an event Is called whenever a cell value or formatting has changed. Fires an event
"OnChangeCell". This is handled by TsWorksheetGrid to update the grid cell. "OnChangeCell". This is handled by TsWorksheetGrid to update the grid cell.
@ -1854,6 +1907,16 @@ end;
} }
procedure TsWorksheet.ChangedCell(ARow, ACol: Cardinal); procedure TsWorksheet.ChangedCell(ARow, ACol: Cardinal);
begin begin
if not FWorkbook.FCalculating and (boAutoCalc in FWorkbook.Options) then begin
if CellUsedInFormula(ARow, ACol) then begin
FWorkbook.FCalculating := true;
try
CalcFormulas;
finally
FWorkbook.FCalculating := false;
end;
end;
end;
if Assigned(FOnChangeCell) then FOnChangeCell(Self, ARow, ACol); if Assigned(FOnChangeCell) then FOnChangeCell(Self, ARow, ACol);
end; end;

View File

@ -46,6 +46,7 @@ type
FLockCount: Integer; FLockCount: Integer;
FEditing: Boolean; FEditing: Boolean;
FCellFont: TFont; FCellFont: TFont;
FAutoCalc: Boolean;
FReadFormulas: Boolean; FReadFormulas: Boolean;
function CalcAutoRowHeight(ARow: Integer): Integer; function CalcAutoRowHeight(ARow: Integer): Integer;
function CalcColWidth(AWidth: Single): Integer; function CalcColWidth(AWidth: Single): Integer;
@ -83,6 +84,7 @@ type
function GetVertAlignments(ARect: TGridRect): TsVertAlignment; function GetVertAlignments(ARect: TGridRect): TsVertAlignment;
function GetWordwrap(ACol, ARow: Integer): Boolean; function GetWordwrap(ACol, ARow: Integer): Boolean;
function GetWordwraps(ARect: TGridRect): Boolean; function GetWordwraps(ARect: TGridRect): Boolean;
procedure SetAutoCalc(AValue: Boolean);
procedure SetBackgroundColor(ACol, ARow: Integer; AValue: TsColor); procedure SetBackgroundColor(ACol, ARow: Integer; AValue: TsColor);
procedure SetBackgroundColors(ARect: TGridRect; AValue: TsColor); procedure SetBackgroundColors(ARect: TGridRect; AValue: TsColor);
procedure SetCellBorder(ACol, ARow: Integer; AValue: TsCellBorders); procedure SetCellBorder(ACol, ARow: Integer; AValue: TsCellBorders);
@ -114,6 +116,7 @@ type
protected protected
{ Protected declarations } { Protected declarations }
procedure CreateNewWorkbook;
procedure DoPrepareCanvas(ACol, ARow: Integer; AState: TGridDrawState); override; procedure DoPrepareCanvas(ACol, ARow: Integer; AState: TGridDrawState); override;
procedure DrawAllRows; override; procedure DrawAllRows; override;
procedure DrawCellBorders; overload; procedure DrawCellBorders; overload;
@ -140,6 +143,8 @@ type
procedure Setup; procedure Setup;
procedure UpdateColWidths(AStartIndex: Integer = 0); procedure UpdateColWidths(AStartIndex: Integer = 0);
procedure UpdateRowHeights(AStartIndex: Integer = 0); procedure UpdateRowHeights(AStartIndex: Integer = 0);
{@@ Automatically recalculate formulas whenever a cell value changes, }
property AutoCalc: Boolean read FAutoCalc write SetAutoCalc default false;
{@@ Displays column and row headers in the fixed col/row style of the grid. {@@ Displays column and row headers in the fixed col/row style of the grid.
Deprecated. Use ShowHeaders instead. } Deprecated. Use ShowHeaders instead. }
property DisplayFixedColRow: Boolean read GetShowHeaders write SetShowHeaders default true; property DisplayFixedColRow: Boolean read GetShowHeaders write SetShowHeaders default true;
@ -298,6 +303,8 @@ type
TsWorksheetGrid = class(TsCustomWorksheetGrid) TsWorksheetGrid = class(TsCustomWorksheetGrid)
published published
// inherited from TsCustomWorksheetGrid // inherited from TsCustomWorksheetGrid
{@@ Automatically recalculates the worksheet of a cell value changes. }
property AutoCalc;
{@@ Displays column and row headers in the fixed col/row style of the grid. {@@ Displays column and row headers in the fixed col/row style of the grid.
Deprecated. Use ShowHeaders instead. } Deprecated. Use ShowHeaders instead. }
property DisplayFixedColRow; deprecated 'Use ShowHeaders'; property DisplayFixedColRow; deprecated 'Use ShowHeaders';
@ -908,6 +915,18 @@ begin
end; end;
end; end;
{@@
Creates a new empty workbook into which a file will be loaded. Destroys the
previously used workbook.
}
procedure TsCustomWorksheetGrid.CreateNewWorkbook;
begin
FreeAndNil(FWorkbook);
FWorkbook := TsWorkbook.Create;
FWorkbook.ReadFormulas := FReadFormulas;
SetAutoCalc(FAutoCalc);
end;
{@@ {@@
Adjusts the grid's canvas before painting a given cell. Considers Adjusts the grid's canvas before painting a given cell. Considers
background color, horizontal alignment, vertical alignment, etc. background color, horizontal alignment, vertical alignment, etc.
@ -2606,6 +2625,18 @@ begin
inherited; inherited;
end; end;
procedure TsCustomWorksheetGrid.SetAutoCalc(AValue: Boolean);
begin
FAutoCalc := AValue;
if Assigned(FWorkbook) then
begin
if FAutoCalc then
FWorkbook.Options := FWorkbook.Options + [boAutoCalc]
else
FWorkbook.Options := FWorkbook.Options - [boAutoCalc];
end;
end;
procedure TsCustomWorksheetGrid.SetBackgroundColor(ACol, ARow: Integer; procedure TsCustomWorksheetGrid.SetBackgroundColor(ACol, ARow: Integer;
AValue: TsColor); AValue: TsColor);
var var
@ -3088,9 +3119,7 @@ procedure TsCustomWorksheetGrid.LoadFromSpreadsheetFile(AFileName: string;
begin begin
BeginUpdate; BeginUpdate;
try try
FreeAndNil(FWorkbook); CreateNewWorkbook;
FWorkbook := TsWorkbook.Create;
FWorkbook.ReadFormulas := FReadFormulas;
FWorkbook.ReadFromFile(AFileName, AFormat); FWorkbook.ReadFromFile(AFileName, AFormat);
LoadFromWorksheet(FWorkbook.GetWorksheetByIndex(AWorksheetIndex)); LoadFromWorksheet(FWorkbook.GetWorksheetByIndex(AWorksheetIndex));
finally finally
@ -3110,9 +3139,7 @@ procedure TsCustomWorksheetGrid.LoadFromSpreadsheetFile(AFileName: string;
begin begin
BeginUpdate; BeginUpdate;
try try
FreeAndNil(FWorkbook); CreateNewWorkbook;
FWorkbook := TsWorkbook.Create;
FWorkbook.ReadFormulas := FReadFormulas;
FWorkbook.ReadFromFile(AFilename); FWorkbook.ReadFromFile(AFilename);
LoadFromWorksheet(FWorkbook.GetWorksheetByIndex(AWorksheetIndex)); LoadFromWorksheet(FWorkbook.GetWorksheetByIndex(AWorksheetIndex));
finally finally