fpspreadsheet: Show message during writing when a formula is not supported by file format. Fix TsWorkbookSource ignoring its set options (i.e. ReadFormulas and AutoCalc working again).

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@4480 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2016-01-27 22:17:56 +00:00
parent 0ceb659793
commit 7679ef468c
11 changed files with 222 additions and 154 deletions

View File

@@ -29,7 +29,7 @@ object MainForm: TMainForm
Width = 707
FrozenCols = 0
FrozenRows = 0
ReadFormulas = False
ReadFormulas = True
SelectionPen.JoinStyle = pjsMiter
SelectionPen.Width = 3
TextOverflow = True

View File

@@ -10,29 +10,29 @@ object Form1: TForm1
ShowHint = True
LCLVersion = '1.7'
object Panel1: TPanel
Left = 0
Height = 41
Top = 608
Width = 894
Align = alBottom
Left = 776
Height = 649
Top = 0
Width = 118
Align = alRight
BevelOuter = bvNone
ClientHeight = 41
ClientWidth = 894
ClientHeight = 649
ClientWidth = 118
TabOrder = 0
object BtnOpen: TButton
Left = 96
Left = 8
Height = 25
Top = 8
Width = 75
Top = 31
Width = 99
Caption = 'Open...'
OnClick = BtnOpenClick
TabOrder = 0
end
object BtnSave: TButton
Left = 183
Left = 8
Height = 25
Top = 8
Width = 75
Top = 58
Width = 99
Caption = 'Save...'
OnClick = BtnSaveClick
TabOrder = 1
@@ -40,69 +40,99 @@ object Form1: TForm1
object BtnNew: TButton
Left = 8
Height = 25
Top = 8
Width = 75
Top = 4
Width = 99
Caption = 'New...'
OnClick = BtnNewClick
TabOrder = 2
end
end
object WorksheetGrid: TsWorksheetGrid
Left = 0
Height = 576
Top = 32
Width = 894
AutoCalc = True
FrozenCols = 0
FrozenRows = 0
ReadFormulas = False
SelectionPen.JoinStyle = pjsMiter
SelectionPen.Width = 3
WorkbookSource = WorksheetGrid.internal
Align = alClient
AutoAdvance = aaDown
ColCount = 10
DefaultColWidth = 64
DefaultRowHeight = 22
MouseWheelOption = mwGrid
Options = [goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine, goRangeSelect, goRowSizing, goColSizing, goEditing, goThumbTracking, goDblClickAutoSize, goSmoothScroll, goHeaderHotTracking, goHeaderPushedLook, goFixedColSizing, goCellHints]
RowCount = 10
TabOrder = 1
TitleStyle = tsNative
end
object Panel2: TPanel
Left = 0
Height = 32
Top = 0
Width = 894
Align = alTop
BevelOuter = bvNone
ClientHeight = 32
ClientWidth = 894
TabOrder = 2
object Label1: TLabel
object CbReadFormulas: TCheckBox
Left = 8
Height = 15
Top = 8
Width = 37
Caption = 'Sheets:'
ParentColor = False
Height = 19
Top = 96
Width = 96
Caption = 'Read formulas'
OnChange = CbReadFormulasChange
TabOrder = 3
end
object SheetsCombo: TComboBox
Left = 72
Height = 23
Top = 4
Width = 818
Anchors = [akTop, akLeft, akRight]
ItemHeight = 15
ItemIndex = 0
Items.Strings = (
'Sheet 1'
)
OnSelect = SheetsComboSelect
Style = csDropDownList
object CbAutoCalc: TCheckBox
Left = 8
Height = 19
Top = 120
Width = 70
Caption = 'Auto calc'
OnChange = CbAutoCalcChange
TabOrder = 4
end
end
object Panel3: TPanel
Left = 0
Height = 649
Top = 0
Width = 776
Align = alClient
BevelOuter = bvNone
Caption = 'Panel3'
ClientHeight = 649
ClientWidth = 776
TabOrder = 1
object Panel2: TPanel
Left = 0
Height = 32
Top = 0
Width = 776
Align = alTop
BevelOuter = bvNone
ClientHeight = 32
ClientWidth = 776
TabOrder = 0
Text = 'Sheet 1'
object Label1: TLabel
Left = 8
Height = 15
Top = 8
Width = 37
Caption = 'Sheets:'
ParentColor = False
end
object SheetsCombo: TComboBox
Left = 72
Height = 23
Top = 4
Width = 700
Anchors = [akTop, akLeft, akRight]
ItemHeight = 15
ItemIndex = 0
Items.Strings = (
'Sheet 1'
)
OnSelect = SheetsComboSelect
Style = csDropDownList
TabOrder = 0
Text = 'Sheet 1'
end
end
object WorksheetGrid: TsWorksheetGrid
Left = 0
Height = 617
Top = 32
Width = 776
AutoCalc = True
FrozenCols = 0
FrozenRows = 0
ReadFormulas = True
SelectionPen.JoinStyle = pjsMiter
SelectionPen.Width = 3
WorkbookSource = WorksheetGrid.internal
Align = alClient
AutoAdvance = aaDown
ColCount = 10
DefaultColWidth = 64
DefaultRowHeight = 22
MouseWheelOption = mwGrid
Options = [goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine, goRangeSelect, goRowSizing, goColSizing, goEditing, goThumbTracking, goDblClickAutoSize, goSmoothScroll, goHeaderHotTracking, goHeaderPushedLook, goFixedColSizing, goCellHints]
RowCount = 10
TabOrder = 1
TitleStyle = tsNative
end
end
object OpenDialog: TOpenDialog

View File

@@ -17,6 +17,9 @@ type
BtnOpen: TButton;
BtnSave: TButton;
BtnNew: TButton;
CbReadFormulas: TCheckBox;
CbAutoCalc: TCheckBox;
Panel3: TPanel;
SheetsCombo: TComboBox;
Label1: TLabel;
OpenDialog: TOpenDialog;
@@ -27,6 +30,8 @@ type
procedure BtnNewClick(Sender: TObject);
procedure BtnOpenClick(Sender: TObject);
procedure BtnSaveClick(Sender: TObject);
procedure CbAutoCalcChange(Sender: TObject);
procedure CbReadFormulasChange(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure SheetsComboSelect(Sender: TObject);
private
@@ -143,6 +148,16 @@ begin
end;
end;
procedure TForm1.CbAutoCalcChange(Sender: TObject);
begin
WorksheetGrid.AutoCalc := CbAutoCalc.Checked;
end;
procedure TForm1.CbReadFormulasChange(Sender: TObject);
begin
WorksheetGrid.ReadFormulas := CbReadFormulas.Checked;
end;
procedure TForm1.FormCreate(Sender: TObject);
const
THICK_BORDER: TsCellBorderStyle = (LineStyle: lsThick; Color: clNavy);
@@ -195,6 +210,9 @@ begin
WorksheetGrid.Cells[2,6] := '=B2^2*PI()';
WorksheetGrid.CellComment[2,6] := 'Area of the circle with radius given in cell B2';
WorksheetGrid.NumberFormat[2,6] := '0.000';
CbAutoCalc.Checked := WorksheetGrid.AutoCalc;
CbReadFormulas.Checked := WorksheetGrid.ReadFormulas;
end;
procedure TForm1.SheetsComboSelect(Sender: TObject);

View File

@@ -3405,7 +3405,6 @@ begin
else
n := Length(FArgumentNodes);
Result := ANext;
// for i:=Length(FArgumentNodes)-1 downto 0 do
for i:=0 to High(FArgumentNodes) do
Result := FArgumentNodes[i].AsRPNItem(Result);
Result := RPNFunc(FID.Name, n, Result);

View File

@@ -880,7 +880,6 @@ begin
FWorkbook.OnRemovingWorksheet := @WorksheetRemovingHandler;
FWorkbook.OnRenameWorksheet := @WorksheetRenamedHandler;
FWorkbook.OnSelectWorksheet := @WorksheetSelectedHandler;
// FWorkbook.OnChangePalette := @WorkbookChangedPaletteHandler;
// Pass options to workbook
SetOptions(FOptions);
end;
@@ -945,6 +944,7 @@ var
begin
book := TsWorkbook.Create;
try
book.Options := FOptions;
if AAutoDetect then
book.ReadfromFile(AFileName)
else

View File

@@ -1462,26 +1462,9 @@ procedure TsCustomWorksheetGrid.CreateNewWorkbook;
begin
GetWorkbookSource.CreateNewWorkbook;
if FReadFormulas then
Workbook.Options := Workbook.Options + [boReadFormulas] else
Workbook.Options := Workbook.Options - [boReadFormulas];
WorkbookSource.Options := WorkbookSource.Options + [boReadFormulas] else
WorkbookSource.Options := Workbooksource.Options - [boReadFormulas];
SetAutoCalc(FAutoCalc);
{
if FOwnsWorkbook then
FreeAndNil(FOwnedWorkbook);
if FWorkbookSource <> nil then
FWorkbookSource.CreateNewWorkbook
else
begin
FOwnedWorkbook := TsWorkbook.Create;
FOwnsWorkbook := true;
if FReadFormulas then
FOwnedWorkbook.Options := FOwnedWorkbook.Options + [boReadFormulas]
else
FOwnedWorkbook.Options := FOwnedWorkbook.Options - [boReadFormulas];
SetAutoCalc(FAutoCalc);
end;
}
end;
{@@ ----------------------------------------------------------------------------
@@ -5132,23 +5115,20 @@ begin
end;
procedure TsCustomWorksheetGrid.SetAutoCalc(AValue: Boolean);
var
optns: TsWorkbookOptions;
begin
FAutoCalc := AValue;
if Assigned(FWorkbookSource) then
if Assigned(WorkbookSource) then
begin
optns := WorkbookSource.Options;
if FAutoCalc then
FWorkbookSource.Options := FWorkbookSource.Options + [boAutoCalc]
else
FWorkbookSource.Options := FWorkbookSource.Options - [boAutoCalc];
end;
if Assigned(Workbook) then
begin
if FAutoCalc then
Workbook.Options := Workbook.Options + [boAutoCalc]
else
Workbook.Options := Workbook.Options - [boAutoCalc];
Include(optns, boAutoCalc) else
Exclude(optns, boAutoCalc);
WorkbookSource.Options := optns;
if FInternalWorkbookSource <> nil then
FInternalWorkbookSource.Options := optns;
end;
end;
@@ -5643,23 +5623,20 @@ begin
end;
procedure TsCustomWorksheetGrid.SetReadFormulas(AValue: Boolean);
var
optns: TsWorkbookOptions;
begin
FReadFormulas := AValue;
if Assigned(FWorkbookSource) then
if Assigned(WorkbookSource) then
begin
optns := WorkbookSource.Options;
if FReadFormulas then
FWorkbookSource.Options := FWorkbookSource.Options + [boReadFormulas]
Include(optns, boReadFormulas)
else
FWorkbookSource.Options := FWorkbookSource.Options - [boReadFormulas];
end;
if Assigned(Workbook) then
begin
if FReadFormulas then
Workbook.Options := Workbook.Options + [boReadFormulas]
else
Workbook.Options := Workbook.Options - [boReadFormulas];
Exclude(optns, boReadFormulas);
WorkbookSource.Options := optns;
if FInternalWorkbookSource <> nil then
FInternalWorkbookSource.Options := optns;
end;
end;

View File

@@ -80,6 +80,7 @@ resourcestring
rsIndexInSSTOutOfRange = 'Index %d in SST out of range (0-%d).';
rsAmbiguousDecThouSeparator = 'Assuming usage of decimal separator in "%s".';
rsCodePageNotSupported = 'Code page "%s" is not supported. Using "cp1252" (Latin 1) instead.';
rsFormulaNotSupported = 'The formula in cell %s is not supported by this file format: %s';
rsNoValidHyperlinkInternal = 'The hyperlink "%s" is not a valid cell address.';
rsNoValidHyperlinkURI = 'The hyperlink "%s" is not a valid URI.';

View File

@@ -20,15 +20,9 @@
<VersionInfo>
<StringTable ProductVersion=""/>
</VersionInfo>
<MacroValues Count="1">
<Macro1 Name="LCLWidgetType" Value="win32"/>
</MacroValues>
<BuildModes Count="3">
<Item1 Name="Release" Default="True"/>
<Item2 Name="Debug">
<MacroValues Count="1">
<Macro1 Name="LCLWidgetType" Value="win32"/>
</MacroValues>
<CompilerOptions>
<Version Value="11"/>
<PathDelim Value="\"/>
@@ -57,9 +51,6 @@
</CompilerOptions>
</Item2>
<Item3 Name="Debug with heaptrace">
<MacroValues Count="1">
<Macro1 Name="LCLWidgetType" Value="win32"/>
</MacroValues>
<CompilerOptions>
<Version Value="11"/>
<PathDelim Value="\"/>
@@ -134,6 +125,7 @@
<ComponentName Value="AboutForm"/>
<HasResources Value="True"/>
<ResourceBaseClass Value="Form"/>
<UnitName Value="beAbout"/>
</Unit1>
<Unit2>
<Filename Value="bebiffgrid.pas"/>

View File

@@ -98,6 +98,8 @@ type
procedure WriteIXFE(AStream: TStream; XFIndex: Word);
protected
procedure AddBuiltinNumFormats; override;
function FunctionSupported(AExcelCode: Integer;
const AFuncName: String): Boolean; override;
// procedure ListAllNumFormats; override;
procedure WriteBlank(AStream: TStream; const ARow, ACol: Cardinal;
ACell: PCell); override;
@@ -1013,6 +1015,13 @@ begin
InternalAddBuiltInNumFormats(FNumFormatList, Workbook.FormatSettings);
end;
function TsSpreadBIFF2Writer.FunctionSupported(AExcelCode: Integer;
const AFuncName: String): Boolean;
begin
Result := inherited and (AExcelCode < 200);
end;
{@@ ----------------------------------------------------------------------------
Determines the cell attributes needed for writing a cell content record, such
as WriteLabel, WriteNumber, etc.
@@ -1620,10 +1629,20 @@ var
RPNLength: Word;
RecordSizePos, FinalPos: Cardinal;
xf: Word;
isSupported: Boolean;
unsupportedFormulas: String;
begin
if (ARow >= FLimitations.MaxRowCount) or (ACol >= FLimitations.MaxColCount) then
exit;
{ Check if formula is supported by this file format. If not, write only
the result }
isSupported := FormulaSupported(AFormula, unsupportedFormulas);
if not IsSupported then
Workbook.AddErrorMsg(rsFormulaNotSupported, [
GetCellString(ARow, ACol), unsupportedformulas
]);
RPNLength := 0;
xf := FindXFIndex(ACell);
@@ -1650,11 +1669,7 @@ begin
AStream.WriteByte(1);
{ Formula data (RPN token array) }
{
if ACell^.SharedFormulaBase <> nil then
WriteRPNSharedFormulaLink(AStream, ACell, RPNLength)
else}
WriteRPNTokenArray(AStream, ACell, AFormula, false, RPNLength);
WriteRPNTokenArray(AStream, ACell, AFormula, false, IsSupported, RPNLength);
{ Finally write sizes after we know them }
FinalPos := AStream.Position;

View File

@@ -96,6 +96,7 @@ type
TsSpreadBIFF5Writer = class(TsSpreadBIFFWriter)
protected
function FunctionSupported(AExcelCode: Integer; const AFuncName: String): Boolean; override;
procedure InternalWriteToStream(AStream: TStream);
{ Record writing methods }
procedure WriteBOF(AStream: TStream; ADataType: Word);
@@ -214,7 +215,7 @@ implementation
uses
Math,
fpsStrings, fpsRegFileFormats, fpsStreams, fpsPalette, fpsNumFormat;
fpsStrings, fpsRegFileFormats, fpsStreams, fpsPalette, fpsNumFormat, xlsconst;
const
{ Excel record IDs }
@@ -983,6 +984,12 @@ begin
FCodePage := Excel5Settings.CodePage;
end;
function TsSpreadBIFF5Writer.FunctionSupported(AExcelCode: Integer;
const AFuncName: String): Boolean;
begin
Result := inherited and (AExcelCode <> INT_EXCEL_SHEET_FUNC_HYPERLINK);
end;
{@@ ----------------------------------------------------------------------------
Writes an Excel BIFF5 record structure

View File

@@ -468,6 +468,8 @@ type
procedure AddBuiltinNumFormats; override;
function FindXFIndex(ACell: PCell): Integer; virtual;
function FixLineEnding(const AText: String): String;
function FormulaSupported(ARPNFormula: TsRPNFormula; out AUnsupported: String): Boolean;
function FunctionSupported(AExcelCode: Integer; const AFuncName: String): Boolean; virtual;
function GetLastRowIndex(AWorksheet: TsWorksheet): Integer;
function GetLastColIndex(AWorksheet: TsWorksheet): Word;
function GetPrintOptions: Word; virtual;
@@ -544,7 +546,7 @@ type
var RPNLength: Word); virtual;
}
procedure WriteRPNTokenArray(AStream: TStream; ACell: PCell;
const AFormula: TsRPNFormula; UseRelAddr: Boolean; var RPNLength: Word);
const AFormula: TsRPNFormula; UseRelAddr, IsSupported: Boolean; var RPNLength: Word);
procedure WriteRPNTokenArraySize(AStream: TStream; ASize: Word); virtual;
// Writes out a SELECTION record
@@ -2517,6 +2519,37 @@ begin
Result[i] := #10;
end;
{@@ ----------------------------------------------------------------------------
Checks if the specified formula is supported by this file format.
-------------------------------------------------------------------------------}
function TsSpreadBIFFWriter.FormulaSupported(ARPNFormula: TsRPNFormula;
out AUnsupported: String): Boolean;
var
exprDef: TsExprIdentifierDef;
i: Integer;
begin
Result := true;
AUnsupported := '';
for i:=0 to Length(ARPNFormula)-1 do begin
if ARPNFormula[i].ElementKind = fekFunc then begin
exprDef := BuiltinIdentifiers.IdentifierByName(ARPNFormula[i].FuncName);
if not FunctionSupported(exprDef.ExcelCode, exprDef.Name) then
begin
Result := false;
AUnsupported := AUnsupported + ', ' + exprDef.Name + '()';
end;
end;
end;
if AUnsupported <> '' then Delete(AUnsupported, 1, 2);
end;
function TsSpreadBIFFWriter.FunctionSupported(AExcelCode: Integer;
const AFuncName: String): Boolean;
begin
Unused(AFuncName);
Result := AExcelCode <> INT_EXCEL_SHEET_FUNC_NOT_BIFF;
end;
function TsSpreadBIFFWriter.GetLastRowIndex(AWorksheet: TsWorksheet): Integer;
begin
Result := AWorksheet.GetLastRowIndex;
@@ -3335,16 +3368,22 @@ procedure TsSpreadBIFFWriter.WriteRPNFormula(AStream: TStream;
var
RPNLength: Word = 0;
RecordSizePos, StartPos, FinalPos: Int64;
isSupported: Boolean;
unsupportedFormulas: String;
begin
if (ARow >= FLimitations.MaxRowCount) or (ACol >= FLimitations.MaxColCount) then
exit;
if Length(AFormula) = 0 then
exit;
{
if not ((Length(AFormula) > 0) or (ACell^.SharedFormulaBase <> nil)) then
exit;
}
{ Check if formula is supported by this file format. If not, write only
the result }
isSupported := FormulaSupported(AFormula, unsupportedFormulas);
if not IsSupported then
Workbook.AddErrorMsg(rsFormulaNotSupported, [
GetCellString(ARow, ACol), unsupportedformulas
]);
{ BIFF Record header }
AStream.WriteWord(WordToLE(INT_EXCEL_ID_FORMULA));
@@ -3363,34 +3402,21 @@ begin
WriteRPNResult(AStream, ACell);
{ Options flags }
AStream.WriteWord(WordToLE(MASK_FORMULA_RECALCULATE_ALWAYS));
if IsSupported then
AStream.WriteWord(WordToLE(MASK_FORMULA_RECALCULATE_ALWAYS)) else
AStream.WriteWord(0);
{ Not used }
AStream.WriteDWord(0);
{ Formula data (RPN token array) }
{
if ACell^.SharedFormulaBase <> nil then
WriteRPNSharedFormulaLink(AStream, ACell, RPNLength)
else
}
WriteRPNTokenArray(AStream, ACell, AFormula, false, RPNLength);
WriteRPNTokenArray(AStream, ACell, AFormula, false, IsSupported, RPNLength);
{ Write sizes in the end, after we known them }
FinalPos := AStream.Position;
AStream.Position := RecordSizePos;
AStream.WriteWord(WordToLE(FinalPos - StartPos));
AStream.Position := FinalPos;
(*
{ If the cell is the first cell of a range with a shared formula write the
shared formula RECORD here. The shared formula RECORD must follow the
first FORMULA record referring to the shared formula}
if (ACell^.SharedFormulaBase <> nil) and
(ARow = ACell^.SharedFormulaBase^.Row) and
(ACol = ACell^.SharedFormulaBase^.Col)
then
WriteSharedFormula(AStream, ACell^.SharedFormulaBase);
*)
{ Write following STRING record if formula result is a non-empty string. }
if (ACell^.ContentType = cctUTF8String) and (ACell^.UTF8StringValue <> '') then
@@ -3486,14 +3512,14 @@ end;
Writes the token array of the given RPN formula and returns its size
-------------------------------------------------------------------------------}
procedure TsSpreadBIFFWriter.WriteRPNTokenArray(AStream: TStream;
ACell: PCell; const AFormula: TsRPNFormula; UseRelAddr: boolean;
ACell: PCell; const AFormula: TsRPNFormula; UseRelAddr, IsSupported: boolean;
var RPNLength: Word);
var
i: Integer;
n: Word;
dr, dc: Integer;
TokenArraySizePos: Int64;
FinalPos: Int64;
finalPos: Int64;
exprDef: TsExprIdentifierDef;
primaryExcelCode, secondaryExcelCode: Word;
begin
@@ -3504,6 +3530,9 @@ begin
TokenArraySizePos := AStream.Position;
WriteRPNTokenArraySize(AStream, 0);
if not IsSupported then
exit;
{ Formula data (RPN token array) }
for i := 0 to Length(AFormula) - 1 do begin