You've already forked lazarus-ccr
fpspreadsheet: Fix reading of ods files containing formulas with several arguments (separator semicolon). Update unit tests. (Still issues with files posted at https://forum.lazarus.freepascal.org/index.php/topic,42168.msg293792.html).
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@6574 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
@ -687,6 +687,7 @@ type
|
|||||||
FDialect: TsFormulaDialect;
|
FDialect: TsFormulaDialect;
|
||||||
FSourceCell: PCell;
|
FSourceCell: PCell;
|
||||||
FDestCell: PCell;
|
FDestCell: PCell;
|
||||||
|
FListSep: Char;
|
||||||
procedure CheckEOF;
|
procedure CheckEOF;
|
||||||
function GetAsBoolean: Boolean;
|
function GetAsBoolean: Boolean;
|
||||||
function GetAsDateTime: TDateTime;
|
function GetAsDateTime: TDateTime;
|
||||||
@ -703,13 +704,14 @@ type
|
|||||||
FFormatSettings: TFormatSettings;
|
FFormatSettings: TFormatSettings;
|
||||||
FContains3DRef: Boolean;
|
FContains3DRef: Boolean;
|
||||||
class function BuiltinExpressionManager: TsBuiltInExpressionManager;
|
class function BuiltinExpressionManager: TsBuiltInExpressionManager;
|
||||||
function BuildStringFormula(AFormatSettings: TFormatSettings): String;
|
function BuildStringFormula: String;
|
||||||
procedure ParserError(Msg: String);
|
procedure ParserError(Msg: String);
|
||||||
function GetExpression: String;
|
function GetExpression: String;
|
||||||
function GetLocalizedExpression(const AFormatSettings: TFormatSettings): String; virtual;
|
function GetLocalizedExpression(const AFormatSettings: TFormatSettings): String; virtual;
|
||||||
procedure SetExpression(const AValue: String);
|
procedure SetExpression(const AValue: String);
|
||||||
procedure SetLocalizedExpression(const AFormatSettings: TFormatSettings;
|
procedure SetLocalizedExpression(const AFormatSettings: TFormatSettings;
|
||||||
const AValue: String); virtual;
|
const AValue: String); virtual;
|
||||||
|
procedure UpdateExprFormatSettings;
|
||||||
|
|
||||||
procedure CheckResultType(const Res: TsExpressionResult;
|
procedure CheckResultType(const Res: TsExpressionResult;
|
||||||
AType: TsResultType); inline;
|
AType: TsResultType); inline;
|
||||||
@ -750,6 +752,7 @@ type
|
|||||||
property AsDateTime: TDateTime read GetAsDateTime;
|
property AsDateTime: TDateTime read GetAsDateTime;
|
||||||
// The expression to parse
|
// The expression to parse
|
||||||
property Expression: String read GetExpression write SetExpression;
|
property Expression: String read GetExpression write SetExpression;
|
||||||
|
property ListSeparator: Char read FListSep;
|
||||||
property LocalizedExpression[AFormatSettings: TFormatSettings]: String
|
property LocalizedExpression[AFormatSettings: TFormatSettings]: String
|
||||||
read GetLocalizedExpression write SetLocalizedExpression;
|
read GetLocalizedExpression write SetLocalizedExpression;
|
||||||
property RPNFormula: TsRPNFormula read GetRPNFormula write SetRPNFormula;
|
property RPNFormula: TsRPNFormula read GetRPNFormula write SetRPNFormula;
|
||||||
@ -847,6 +850,7 @@ procedure RegisterFunction(const AName: ShortString; const AResultType: Char;
|
|||||||
const AParamTypes: String; const AExcelCode: Integer; ACallBack: TsExprFunctionEvent); overload;
|
const AParamTypes: String; const AExcelCode: Integer; ACallBack: TsExprFunctionEvent); overload;
|
||||||
|
|
||||||
var
|
var
|
||||||
|
// Format settings used in stored parsed formulas.
|
||||||
ExprFormatSettings: TFormatSettings;
|
ExprFormatSettings: TFormatSettings;
|
||||||
|
|
||||||
const
|
const
|
||||||
@ -1060,7 +1064,7 @@ begin
|
|||||||
Result := ttLessThanEqual;
|
Result := ttLessThanEqual;
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
if D = FParser.FFormatSettings.ListSeparator then
|
if D = FParser.ListSeparator then
|
||||||
Result := ttListSep
|
Result := ttListSep
|
||||||
else
|
else
|
||||||
case D of
|
case D of
|
||||||
@ -1316,7 +1320,7 @@ end;
|
|||||||
|
|
||||||
function TsExpressionScanner.IsDelim(C: Char): Boolean;
|
function TsExpressionScanner.IsDelim(C: Char): Boolean;
|
||||||
begin
|
begin
|
||||||
Result := (C in Delimiters) or (C = FParser.FFormatSettings.ListSeparator);
|
Result := (C in Delimiters) or (C = FParser.ListSeparator);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TsExpressionScanner.IsDigit(C: Char): Boolean;
|
function TsExpressionScanner.IsDigit(C: Char): Boolean;
|
||||||
@ -1326,7 +1330,7 @@ end;
|
|||||||
|
|
||||||
function TsExpressionScanner.IsWordDelim(C: Char): Boolean;
|
function TsExpressionScanner.IsWordDelim(C: Char): Boolean;
|
||||||
begin
|
begin
|
||||||
Result := (C in WordDelimiters) or (C = FParser.FFormatSettings.ListSeparator);
|
Result := (C in WordDelimiters) or (C = FParser.ListSeparator);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TsExpressionScanner.NextPos: Char;
|
function TsExpressionScanner.NextPos: Char;
|
||||||
@ -1368,12 +1372,14 @@ end;
|
|||||||
constructor TsExpressionParser.Create(AWorksheet: TsBasicWorksheet);
|
constructor TsExpressionParser.Create(AWorksheet: TsBasicWorksheet);
|
||||||
begin
|
begin
|
||||||
inherited Create;
|
inherited Create;
|
||||||
FDialect := fdExcelA1;
|
|
||||||
FWorksheet := AWorksheet;
|
FWorksheet := AWorksheet;
|
||||||
FIdentifiers := TsExprIdentifierDefs.Create(TsExprIdentifierDef);
|
FIdentifiers := TsExprIdentifierDefs.Create(TsExprIdentifierDef);
|
||||||
FIdentifiers.FParser := Self;
|
FIdentifiers.FParser := Self;
|
||||||
FScanner := TsExpressionScanner.Create(self);
|
FScanner := TsExpressionScanner.Create(self);
|
||||||
FHashList := TFPHashObjectList.Create(False);
|
FHashList := TFPHashObjectList.Create(False);
|
||||||
|
SetDialect(fdExcelA1);
|
||||||
|
FFormatSettings := InitFormatSettings(TsWorksheet(FWorksheet).Workbook);
|
||||||
|
UpdateExprFormatSettings;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
destructor TsExpressionParser.Destroy;
|
destructor TsExpressionParser.Destroy;
|
||||||
@ -1386,17 +1392,14 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
{ Constructs the string formula from the tree of expression nodes. Gets the
|
{ Constructs the string formula from the tree of expression nodes. Gets the
|
||||||
decimal and list separator from the formatsettings provided. }
|
decimal and list separator from current formatsettings. }
|
||||||
function TsExpressionParser.BuildStringFormula(AFormatSettings: TFormatSettings): String;
|
function TsExpressionParser.BuildStringFormula: String;
|
||||||
begin
|
begin
|
||||||
ExprFormatSettings := AFormatSettings;
|
// ExprFormatSettings := AFormatSettings;
|
||||||
if FExprNode = nil then
|
if FExprNode = nil then
|
||||||
Result := ''
|
Result := ''
|
||||||
else
|
else
|
||||||
begin
|
|
||||||
FFormatSettings := AFormatSettings;
|
|
||||||
Result := FExprNode.AsString;
|
Result := FExprNode.AsString;
|
||||||
end;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
class function TsExpressionParser.BuiltinExpressionManager: TsBuiltInExpressionManager;
|
class function TsExpressionParser.BuiltinExpressionManager: TsBuiltInExpressionManager;
|
||||||
@ -1485,6 +1488,8 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TsExpressionParser.EvaluateExpression(out AResult: TsExpressionResult);
|
procedure TsExpressionParser.EvaluateExpression(out AResult: TsExpressionResult);
|
||||||
|
var
|
||||||
|
fs: TFormatSettings;
|
||||||
begin
|
begin
|
||||||
{ // Not needed. May be missing after copying formulas
|
{ // Not needed. May be missing after copying formulas
|
||||||
if (FExpression = '') then
|
if (FExpression = '') then
|
||||||
@ -1492,7 +1497,15 @@ begin
|
|||||||
}
|
}
|
||||||
if not Assigned(FExprNode) then
|
if not Assigned(FExprNode) then
|
||||||
ParserError(rsErrorInExpression);
|
ParserError(rsErrorInExpression);
|
||||||
FExprNode.GetNodeValue(AResult);
|
fs := ExprFormatSettings;
|
||||||
|
try
|
||||||
|
UpdateExprFormatSettings;
|
||||||
|
// ExprFormatSettings := FFormatSettings;
|
||||||
|
// FFormatSettings := ExprFormatSettings;
|
||||||
|
FExprNode.GetNodeValue(AResult);
|
||||||
|
finally
|
||||||
|
ExprFormatSettings := fs;
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TsExpressionParser.GetAsBoolean: Boolean;
|
function TsExpressionParser.GetAsBoolean: Boolean;
|
||||||
@ -1915,19 +1928,16 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
function TsExpressionParser.GetExpression: String;
|
function TsExpressionParser.GetExpression: String;
|
||||||
var
|
|
||||||
fs: TFormatsettings;
|
|
||||||
begin
|
begin
|
||||||
fs := DefaultFormatSettings;
|
Result := BuildStringFormula; //(FFormatSettings);
|
||||||
fs.DecimalSeparator := '.';
|
|
||||||
fs.ListSeparator := ',';
|
|
||||||
Result := BuildStringFormula(fs);
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TsExpressionParser.GetLocalizedExpression(const AFormatSettings: TFormatSettings): String;
|
function TsExpressionParser.GetLocalizedExpression(const AFormatSettings: TFormatSettings): String;
|
||||||
begin
|
begin
|
||||||
ExprFormatSettings := AFormatSettings;
|
// ExprFormatSettings := AFormatSettings;
|
||||||
Result := BuildStringFormula(AFormatSettings);
|
FFormatSettings := AFormatSettings;
|
||||||
|
UpdateExprFormatSettings;
|
||||||
|
Result := BuildStringFormula; //(AFormatSettings);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TsExpressionParser.Has3DLinks: Boolean;
|
function TsExpressionParser.Has3DLinks: Boolean;
|
||||||
@ -1944,26 +1954,17 @@ end;
|
|||||||
|
|
||||||
procedure TsExpressionParser.SetDialect(const AValue: TsFormulaDialect);
|
procedure TsExpressionParser.SetDialect(const AValue: TsFormulaDialect);
|
||||||
begin
|
begin
|
||||||
if FDialect = AValue then exit;
|
|
||||||
FDialect := AValue;
|
FDialect := AValue;
|
||||||
{
|
case FDialect of
|
||||||
if FScanner <> nil then
|
fdExcelA1,
|
||||||
case FDialect of
|
fdExcelR1C1 : FListSep := ',';
|
||||||
fdExcelA1, fdExcelR1C1: FScanner.SheetNameTerminator := '!';
|
fdOpenDocument : FListSep := ';'
|
||||||
fdOpenDocument: FScanner.Sheetnameterminator := '.';
|
end;
|
||||||
else raise Exception.Create('TsExpressionParser.SetDialect: Dialect not supported.');
|
|
||||||
end;
|
|
||||||
}
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TsExpressionParser.SetExpression(const AValue: String);
|
procedure TsExpressionParser.SetExpression(const AValue: String);
|
||||||
var
|
|
||||||
fs: TFormatSettings;
|
|
||||||
begin
|
begin
|
||||||
fs := DefaultFormatSettings;
|
SetLocalizedExpression(FFormatSettings, AValue);
|
||||||
fs.DecimalSeparator := '.';
|
|
||||||
fs.ListSeparator := ',';
|
|
||||||
SetLocalizedExpression(fs, AValue);
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TsExpressionParser.SetLocalizedExpression(const AFormatSettings: TFormatSettings;
|
procedure TsExpressionParser.SetLocalizedExpression(const AFormatSettings: TFormatSettings;
|
||||||
@ -1972,7 +1973,8 @@ begin
|
|||||||
if FExpression = AValue then
|
if FExpression = AValue then
|
||||||
exit;
|
exit;
|
||||||
FFormatSettings := AFormatSettings;
|
FFormatSettings := AFormatSettings;
|
||||||
ExprFormatSettings := AFormatSettings;
|
UpdateExprFormatSettings;
|
||||||
|
// ExprFormatSettings := AFormatSettings;
|
||||||
FExpression := AValue;
|
FExpression := AValue;
|
||||||
if (AValue <> '') and (AValue[1] = '=') then
|
if (AValue <> '') and (AValue[1] = '=') then
|
||||||
FScanner.Source := Copy(AValue, 2, Length(AValue))
|
FScanner.Source := Copy(AValue, 2, Length(AValue))
|
||||||
@ -2182,6 +2184,16 @@ begin
|
|||||||
Result := FScanner.TokenType;
|
Result := FScanner.TokenType;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TsExpressionParser.UpdateExprFormatSettings;
|
||||||
|
var
|
||||||
|
book: TsWorkbook;
|
||||||
|
begin
|
||||||
|
book := TsWorksheet(FWorksheet).Workbook;
|
||||||
|
ExprFormatSettings.ShortDateFormat := book.FormatSettings.ShortDateFormat;
|
||||||
|
ExprFormatSettings.ShortTimeFormat := book.FormatSettings.ShortTimeFormat;
|
||||||
|
ExprFormatSettings.LongTimeFormat := book.FormatSettings.LongTimeFormat;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
{------------------------------------------------------------------------------}
|
{------------------------------------------------------------------------------}
|
||||||
{ TsSpreadsheetParser }
|
{ TsSpreadsheetParser }
|
||||||
@ -3738,7 +3750,7 @@ begin
|
|||||||
for i := 0 to Length(FArgumentNodes)-1 do
|
for i := 0 to Length(FArgumentNodes)-1 do
|
||||||
begin
|
begin
|
||||||
if (S <> '') then
|
if (S <> '') then
|
||||||
S := S + Parser.FFormatSettings.ListSeparator;
|
S := S + Parser.ListSeparator;
|
||||||
if Assigned(FArgumentNodes[i]) then
|
if Assigned(FArgumentNodes[i]) then
|
||||||
S := S + FArgumentNodes[i].AsString;
|
S := S + FArgumentNodes[i].AsString;
|
||||||
end;
|
end;
|
||||||
@ -4431,8 +4443,9 @@ begin
|
|||||||
rtBoolean : if Arg.ResBoolean then Result := 1.0;
|
rtBoolean : if Arg.ResBoolean then Result := 1.0;
|
||||||
rtHyperlink,
|
rtHyperlink,
|
||||||
rtString : begin
|
rtString : begin
|
||||||
fs := ExprFormatSettings; //(Arg.Worksheet as TsWorksheet).Workbook.FormatSettings;
|
fs := ExprFormatSettings;
|
||||||
TryStrToDateTime(ArgToString(Arg), Result, fs);
|
if not TryStrToDateTime(ArgToString(Arg), Result, fs) then
|
||||||
|
Result := NaN;
|
||||||
end;
|
end;
|
||||||
rtCell : begin
|
rtCell : begin
|
||||||
cell := ArgToCell(Arg);
|
cell := ArgToCell(Arg);
|
||||||
@ -4696,14 +4709,13 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
initialization
|
initialization
|
||||||
ExprFormatSettings := DefaultFormatSettings;
|
// These are the format settings used in storage of parsed formulas.
|
||||||
ExprFormatSettings.DecimalSeparator := '.';
|
ExprFormatSettings := InitFormatSettings(nil);
|
||||||
ExprFormatSettings.ListSeparator := ',';
|
{
|
||||||
ExprFormatSettings.DateSeparator := '/';
|
|
||||||
ExprFormatSettings.TimeSeparator := ':';
|
|
||||||
ExprFormatSettings.ShortDateFormat := 'yyyy/m/d'; // the parser returns single digits
|
ExprFormatSettings.ShortDateFormat := 'yyyy/m/d'; // the parser returns single digits
|
||||||
ExprFormatSettings.LongTimeFormat := 'h:n:s';
|
ExprFormatSettings.LongTimeFormat := 'h:n:s';
|
||||||
ExprFormatSettings.ShortTimeFormat := 'h:n';
|
ExprFormatSettings.ShortTimeFormat := 'h:n';
|
||||||
|
}
|
||||||
|
|
||||||
RegisterStdBuiltins(BuiltinIdentifiers);
|
RegisterStdBuiltins(BuiltinIdentifiers);
|
||||||
|
|
||||||
|
@ -391,6 +391,10 @@ var
|
|||||||
begin
|
begin
|
||||||
start_date := ArgToDateTime(Args[0]);
|
start_date := ArgToDateTime(Args[0]);
|
||||||
end_date := ArgToDateTime(Args[1]);
|
end_date := ArgToDateTime(Args[1]);
|
||||||
|
if IsNaN(start_date) or IsNaN(end_date) then begin
|
||||||
|
Result := ErrorResult(errWrongType);
|
||||||
|
exit;
|
||||||
|
end;
|
||||||
interval := ArgToString(Args[2]);
|
interval := ArgToString(Args[2]);
|
||||||
|
|
||||||
if end_date > start_date then
|
if end_date > start_date then
|
||||||
@ -428,8 +432,10 @@ begin
|
|||||||
if (Args[0].ResultType in [rtDateTime, rtFloat, rtInteger, rtString, rtCell]) then
|
if (Args[0].ResultType in [rtDateTime, rtFloat, rtInteger, rtString, rtCell]) then
|
||||||
begin
|
begin
|
||||||
dt := ArgToDateTime(Args[0]);
|
dt := ArgToDateTime(Args[0]);
|
||||||
DecodeDate(dt, y, m, d);
|
if not IsNaN(dt) then begin
|
||||||
Result := IntegerResult(d);
|
DecodeDate(dt, y, m, d);
|
||||||
|
Result := IntegerResult(d);
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -444,8 +450,10 @@ begin
|
|||||||
if (Args[0].ResultType in [rtDateTime, rtFloat, rtInteger, rtString, rtCell]) then
|
if (Args[0].ResultType in [rtDateTime, rtFloat, rtInteger, rtString, rtCell]) then
|
||||||
begin
|
begin
|
||||||
dt := ArgToDateTime(Args[0]);
|
dt := ArgToDateTime(Args[0]);
|
||||||
DecodeTime(dt, h, m, s, ms);
|
if not IsNaN(dt) then begin
|
||||||
Result := IntegerResult(h);
|
DecodeTime(dt, h, m, s, ms);
|
||||||
|
Result := IntegerResult(h);
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -459,8 +467,10 @@ begin
|
|||||||
if (Args[0].ResultType in [rtDateTime, rtFloat, rtInteger, rtString, rtCell]) then
|
if (Args[0].ResultType in [rtDateTime, rtFloat, rtInteger, rtString, rtCell]) then
|
||||||
begin
|
begin
|
||||||
dt := ArgToDateTime(Args[0]);
|
dt := ArgToDateTime(Args[0]);
|
||||||
DecodeTime(dt, h, m, s, ms);
|
if not IsNaN(dt) then begin
|
||||||
Result := IntegerResult(m);
|
DecodeTime(dt, h, m, s, ms);
|
||||||
|
Result := IntegerResult(m);
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -474,8 +484,10 @@ begin
|
|||||||
if (Args[0].ResultType in [rtDateTime, rtFloat, rtInteger, rtString, rtCell]) then
|
if (Args[0].ResultType in [rtDateTime, rtFloat, rtInteger, rtString, rtCell]) then
|
||||||
begin
|
begin
|
||||||
dt := ArgToDateTime(Args[0]);
|
dt := ArgToDateTime(Args[0]);
|
||||||
DecodeDate(dt, y, m, d);
|
if not IsNaN(dt) then begin
|
||||||
Result := IntegerResult(m);
|
DecodeDate(dt, y, m, d);
|
||||||
|
Result := IntegerResult(m);
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -498,8 +510,10 @@ begin
|
|||||||
if (Args[0].ResultType in [rtDateTime, rtFloat, rtInteger, rtString, rtCell]) then
|
if (Args[0].ResultType in [rtDateTime, rtFloat, rtInteger, rtString, rtCell]) then
|
||||||
begin
|
begin
|
||||||
dt := ArgToDateTime(Args[0]);
|
dt := ArgToDateTime(Args[0]);
|
||||||
DecodeTime(dt, h, m, s, ms);
|
if not IsNaN(dt) then begin
|
||||||
Result := IntegerResult(s);
|
DecodeTime(dt, h, m, s, ms);
|
||||||
|
Result := IntegerResult(s);
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -542,19 +556,17 @@ var
|
|||||||
dow: Integer;
|
dow: Integer;
|
||||||
dt: TDateTime;
|
dt: TDateTime;
|
||||||
begin
|
begin
|
||||||
|
Result := ErrorResult(errWrongType);
|
||||||
if Length(Args) = 2 then
|
if Length(Args) = 2 then
|
||||||
n := ArgToInt(Args[1])
|
n := ArgToInt(Args[1])
|
||||||
else
|
else
|
||||||
n := 1;
|
n := 1;
|
||||||
if Args[0].ResultType in [rtDateTime, rtFloat, rtInteger, rtCell] then
|
dt := NaN;
|
||||||
dt := ArgToDateTime(Args[0])
|
if Args[0].ResultType in [rtDateTime, rtFloat, rtInteger, rtCell, rtString] then
|
||||||
else
|
dt := ArgToDateTime(Args[0]);
|
||||||
if Args[0].ResultType in [rtString] then
|
if IsNaN(dt) then
|
||||||
if not TryStrToDate(Args[0].ResString, dt) then
|
exit;
|
||||||
begin
|
|
||||||
Result := ErrorResult(errWrongType);
|
|
||||||
exit;
|
|
||||||
end;
|
|
||||||
dow := DayOfWeek(dt); // Sunday = 1 ... Saturday = 7
|
dow := DayOfWeek(dt); // Sunday = 1 ... Saturday = 7
|
||||||
case n of
|
case n of
|
||||||
1: ;
|
1: ;
|
||||||
@ -574,8 +586,10 @@ begin
|
|||||||
if (Args[0].ResultType in [rtDateTime, rtFloat, rtInteger, rtString, rtCell]) then
|
if (Args[0].ResultType in [rtDateTime, rtFloat, rtInteger, rtString, rtCell]) then
|
||||||
begin
|
begin
|
||||||
dt := ArgToDateTime(Args[0]);
|
dt := ArgToDateTime(Args[0]);
|
||||||
DecodeDate(dt, y, m, d);
|
if not IsNaN(dt) then begin
|
||||||
Result := IntegerResult(y);
|
DecodeDate(dt, y, m, d);
|
||||||
|
Result := IntegerResult(y);
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
@ -2400,6 +2400,7 @@ var
|
|||||||
p: Integer;
|
p: Integer;
|
||||||
fmt: PsCellFormat;
|
fmt: PsCellFormat;
|
||||||
ns: String;
|
ns: String;
|
||||||
|
hasFormula: Boolean;
|
||||||
begin
|
begin
|
||||||
{$IFDEF FPSpreadDebug}
|
{$IFDEF FPSpreadDebug}
|
||||||
DebugLn(Format('[ReadFormula] ARow=%d, ACol=%d, AStyleIndex=%d', [ARow, ACol, AStyleIndex]));
|
DebugLn(Format('[ReadFormula] ARow=%d, ACol=%d, AStyleIndex=%d', [ARow, ACol, AStyleIndex]));
|
||||||
@ -2445,7 +2446,10 @@ begin
|
|||||||
formula^.Parser.Expression := formulaStr;
|
formula^.Parser.Expression := formulaStr;
|
||||||
formula^.Parser.Dialect := fdExcelA1; // Convert formula to Excel A1 dialect
|
formula^.Parser.Dialect := fdExcelA1; // Convert formula to Excel A1 dialect
|
||||||
formula^.Text := formula^.Parser.Expression;
|
formula^.Text := formula^.Parser.Expression;
|
||||||
{
|
cell^.Flags := cell^.Flags + [cfHasFormula];
|
||||||
|
hasFormula := true;
|
||||||
|
|
||||||
|
{
|
||||||
cell^.FormulaValue := formula;
|
cell^.FormulaValue := formula;
|
||||||
// Note: This formula is still in OpenDocument dialect. Conversion to
|
// Note: This formula is still in OpenDocument dialect. Conversion to
|
||||||
// ExcelA1 dialect (used by fps) is postponed until all sheets have beeon
|
// ExcelA1 dialect (used by fps) is postponed until all sheets have beeon
|
||||||
@ -2455,9 +2459,12 @@ begin
|
|||||||
{$IFDEF FPSpreadDebug}
|
{$IFDEF FPSpreadDebug}
|
||||||
DebugLn(' Formula found: ' + formula);
|
DebugLn(' Formula found: ' + formula);
|
||||||
{$ENDIF}
|
{$ENDIF}
|
||||||
end;
|
end else
|
||||||
|
hasFormula := false;
|
||||||
|
|
||||||
// Read formula results
|
// Read formula results
|
||||||
|
if hasFormula then TsWorkbook(FWorkbook).LockFormulas;
|
||||||
|
|
||||||
valueType := GetAttrValue(ACellNode, 'office:value-type');
|
valueType := GetAttrValue(ACellNode, 'office:value-type');
|
||||||
valueStr := GetAttrValue(ACellNode, 'office:value');
|
valueStr := GetAttrValue(ACellNode, 'office:value');
|
||||||
calcExtValueType := GetAttrValue(ACellNode, 'calcext:value-type');
|
calcExtValueType := GetAttrValue(ACellNode, 'calcext:value-type');
|
||||||
@ -2520,6 +2527,8 @@ begin
|
|||||||
if (valueStr <> '') then
|
if (valueStr <> '') then
|
||||||
TsWorksheet(FWorksheet).WriteText(cell, valueStr);
|
TsWorksheet(FWorksheet).WriteText(cell, valueStr);
|
||||||
|
|
||||||
|
if hasFormula then TsWorkbook(FWorkbook).UnlockFormulas;
|
||||||
|
|
||||||
if FIsVirtualMode then
|
if FIsVirtualMode then
|
||||||
TsWorkbook(Workbook).OnReadCellData(Workbook, ARow, ACol, cell);
|
TsWorkbook(Workbook).OnReadCellData(Workbook, ARow, ACol, cell);
|
||||||
end;
|
end;
|
||||||
|
@ -4793,7 +4793,7 @@ end;
|
|||||||
procedure TsWorksheet.WriteNumber(ACell: PCell; ANumber: Double);
|
procedure TsWorksheet.WriteNumber(ACell: PCell; ANumber: Double);
|
||||||
begin
|
begin
|
||||||
if ACell <> nil then begin
|
if ACell <> nil then begin
|
||||||
// Delete any pre-existing formula
|
// Delete any pre-existing formula, but only if FormulaLock is ON.
|
||||||
DeleteFormula(ACell);
|
DeleteFormula(ACell);
|
||||||
// Write number to cell
|
// Write number to cell
|
||||||
ACell^.ContentType := cctNumber;
|
ACell^.ContentType := cctNumber;
|
||||||
|
@ -1018,13 +1018,35 @@ const
|
|||||||
HEADER_FOOTER_INDEX_EVEN = 2;
|
HEADER_FOOTER_INDEX_EVEN = 2;
|
||||||
HEADER_FOOTER_INDEX_ALL = 1;
|
HEADER_FOOTER_INDEX_ALL = 1;
|
||||||
|
|
||||||
var
|
procedure InitUTF8FormatSettings(out AFormatSettings: TFormatSettings);
|
||||||
{@@ FPC format settings for which all strings have been converted to UTF8 }
|
|
||||||
UTF8FormatSettings: TFormatSettings;
|
|
||||||
|
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
|
|
||||||
|
{@@ ----------------------------------------------------------------------------
|
||||||
|
Creates a localized FPC format settings record in which all strings are
|
||||||
|
encoded as UTF8.
|
||||||
|
-------------------------------------------------------------------------------}
|
||||||
|
procedure InitUTF8FormatSettings(out AFormatSettings: TFormatSettings);
|
||||||
|
// remove when available in LazUtils
|
||||||
|
var
|
||||||
|
i: Integer;
|
||||||
|
begin
|
||||||
|
AFormatSettings := DefaultFormatSettings;
|
||||||
|
AFormatSettings.CurrencyString := AnsiToUTF8(DefaultFormatSettings.CurrencyString);
|
||||||
|
for i:=1 to 12 do begin
|
||||||
|
AFormatSettings.LongMonthNames[i] := AnsiToUTF8(DefaultFormatSettings.LongMonthNames[i]);
|
||||||
|
AFormatSettings.ShortMonthNames[i] := AnsiToUTF8(DefaultFormatSettings.ShortMonthNames[i]);
|
||||||
|
end;
|
||||||
|
for i:=1 to 7 do begin
|
||||||
|
AFormatSettings.LongDayNames[i] := AnsiToUTF8(DefaultFormatSettings.LongDayNames[i]);
|
||||||
|
AFormatSettings.ShortDayNames[i] := AnsiToUTF8(DefaultFormatSettings.ShortDayNames[i]);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
{ TsFont }
|
||||||
|
|
||||||
constructor TsFont.Create(AFontName: String; ASize: Single; AStyle: TsFontStyles;
|
constructor TsFont.Create(AFontName: String; ASize: Single; AStyle: TsFontStyles;
|
||||||
AColor: TsColor; APosition: TsFontPosition);
|
AColor: TsColor; APosition: TsFontPosition);
|
||||||
begin
|
begin
|
||||||
@ -1078,7 +1100,7 @@ end;
|
|||||||
constructor TsBasicWorkbook.Create;
|
constructor TsBasicWorkbook.Create;
|
||||||
begin
|
begin
|
||||||
inherited;
|
inherited;
|
||||||
FormatSettings := UTF8FormatSettings;
|
InitUTF8FormatSettings(FormatSettings);
|
||||||
FUnits := suMillimeters; // Units for column width and row height
|
FUnits := suMillimeters; // Units for column width and row height
|
||||||
FFormatID := sfidUnknown;
|
FFormatID := sfidUnknown;
|
||||||
FLog := TStringList.Create;
|
FLog := TStringList.Create;
|
||||||
@ -1138,31 +1160,5 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
|
||||||
Creates a FPC format settings record in which all strings are encoded as
|
|
||||||
UTF8.
|
|
||||||
-------------------------------------------------------------------------------}
|
|
||||||
procedure InitUTF8FormatSettings;
|
|
||||||
// remove when available in LazUtils
|
|
||||||
var
|
|
||||||
i: Integer;
|
|
||||||
begin
|
|
||||||
UTF8FormatSettings := DefaultFormatSettings;
|
|
||||||
UTF8FormatSettings.CurrencyString := AnsiToUTF8(DefaultFormatSettings.CurrencyString);
|
|
||||||
for i:=1 to 12 do begin
|
|
||||||
UTF8FormatSettings.LongMonthNames[i] := AnsiToUTF8(DefaultFormatSettings.LongMonthNames[i]);
|
|
||||||
UTF8FormatSettings.ShortMonthNames[i] := AnsiToUTF8(DefaultFormatSettings.ShortMonthNames[i]);
|
|
||||||
end;
|
|
||||||
for i:=1 to 7 do begin
|
|
||||||
UTF8FormatSettings.LongDayNames[i] := AnsiToUTF8(DefaultFormatSettings.LongDayNames[i]);
|
|
||||||
UTF8FormatSettings.ShortDayNames[i] := AnsiToUTF8(DefaultFormatSettings.ShortDayNames[i]);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
|
|
||||||
initialization
|
|
||||||
InitUTF8FormatSettings;
|
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
@ -202,6 +202,7 @@ procedure InitCell(AWorksheet: TsBasicWorksheet; ARow, ACol: Cardinal;
|
|||||||
out ACell: TCell); overload;
|
out ACell: TCell); overload;
|
||||||
procedure InitCryptoInfo(out AValue: TsCryptoInfo);
|
procedure InitCryptoInfo(out AValue: TsCryptoInfo);
|
||||||
procedure InitFormatRecord(out AValue: TsCellFormat);
|
procedure InitFormatRecord(out AValue: TsCellFormat);
|
||||||
|
function InitFormatSettings(AWorkbook: TsBasicWorkbook): TFormatSettings;
|
||||||
procedure InitImageRecord(out AValue: TsImage; ARow, ACol: Cardinal;
|
procedure InitImageRecord(out AValue: TsImage; ARow, ACol: Cardinal;
|
||||||
AOffsetX, AOffsetY, AScaleX, AScaleY: Double);
|
AOffsetX, AOffsetY, AScaleX, AScaleY: Double);
|
||||||
procedure InitHeaderFooterImageRecord(out AImage: TsHeaderFooterImage);
|
procedure InitHeaderFooterImageRecord(out AImage: TsHeaderFooterImage);
|
||||||
@ -2439,6 +2440,25 @@ begin
|
|||||||
// NOTE: Cell protection is effective only after protecting a worksheet
|
// NOTE: Cell protection is effective only after protecting a worksheet
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{@@ ----------------------------------------------------------------------------
|
||||||
|
Initializes the FormatSettingsRecord to international (non-localized) values
|
||||||
|
-------------------------------------------------------------------------------}
|
||||||
|
function InitFormatSettings(AWorkbook: TsBasicWorkbook): TFormatSettings;
|
||||||
|
begin
|
||||||
|
Result := DefaultFormatSettings;
|
||||||
|
Result.DecimalSeparator := '.';
|
||||||
|
Result.ThousandSeparator := ',';
|
||||||
|
Result.ListSeparator := ',';
|
||||||
|
if AWorkbook <> nil then
|
||||||
|
with AWorkbook.FormatSettings do begin
|
||||||
|
Result.DateSeparator := DateSeparator;
|
||||||
|
Result.TimeSeparator := TimeSeparator;
|
||||||
|
Result.ShortDateFormat := ShortDateFormat; //'yyyy/m/d'; // the parser returns single digits
|
||||||
|
Result.LongTimeFormat := LongTimeFormat; //'h:n:s';
|
||||||
|
Result.ShortTimeFormat := ShortTimeFormat; //'h:n';
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
{@@ ----------------------------------------------------------------------------
|
{@@ ----------------------------------------------------------------------------
|
||||||
Initializes the fields of a TsImage record
|
Initializes the fields of a TsImage record
|
||||||
|
|
||||||
|
@ -83,6 +83,10 @@ type
|
|||||||
procedure SumMultiSheetRange_FlippedSheetsAndCells_OOXML;
|
procedure SumMultiSheetRange_FlippedSheetsAndCells_OOXML;
|
||||||
procedure SumMultiSheetRange_FlippedSheetsAndCells_ODS;
|
procedure SumMultiSheetRange_FlippedSheetsAndCells_ODS;
|
||||||
|
|
||||||
|
procedure IfConst_BIFF8;
|
||||||
|
procedure IfConst_OOXML;
|
||||||
|
procedure IfConst_ODS;
|
||||||
|
|
||||||
procedure CountIfRange_BIFF8;
|
procedure CountIfRange_BIFF8;
|
||||||
procedure CountIfRangeSheet_BIFF8;
|
procedure CountIfRangeSheet_BIFF8;
|
||||||
|
|
||||||
@ -335,7 +339,7 @@ procedure TSpreadSingleFormulaTests.YearConst_BIFF8;
|
|||||||
var
|
var
|
||||||
s: String;
|
s: String;
|
||||||
begin
|
begin
|
||||||
s := FormatDateTime(ExprFormatSettings.ShortDateFormat, EncodeDate(2012,2,5), ExprFormatSettings);
|
s := FormatDateTime(DefaultFormatSettings.ShortDateFormat, EncodeDate(2012,2,5), ExprFormatSettings);
|
||||||
TestFormula(Format('YEAR("%s")', [s]), '2012', ftkConstants, sfExcel8);
|
TestFormula(Format('YEAR("%s")', [s]), '2012', ftkConstants, sfExcel8);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -348,7 +352,7 @@ procedure TSpreadSingleFormulaTests.MonthConst_BIFF8;
|
|||||||
var
|
var
|
||||||
s: String;
|
s: String;
|
||||||
begin
|
begin
|
||||||
s := FormatDateTime(ExprFormatSettings.ShortDateFormat, EncodeDate(2012,2,5), ExprFormatSettings);
|
s := FormatDateTime(DefaultFormatSettings.ShortDateFormat, EncodeDate(2012,2,5), DefaultFormatSettings);
|
||||||
TestFormula(Format('MONTH("%s")', [s]), '2', ftkConstants, sfExcel8);
|
TestFormula(Format('MONTH("%s")', [s]), '2', ftkConstants, sfExcel8);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -361,7 +365,7 @@ procedure TSpreadSingleFormulaTests.DayConst_BIFF8;
|
|||||||
var
|
var
|
||||||
s: String;
|
s: String;
|
||||||
begin
|
begin
|
||||||
s := FormatDateTime(ExprFormatSettings.ShortDateFormat, EncodeDate(2012,2,5), ExprFormatSettings);
|
s := FormatDateTime(DefaultFormatSettings.ShortDateFormat, EncodeDate(2012,2,5), DefaultFormatSettings);
|
||||||
TestFormula(Format('DAY("%s")', [s]), '5', ftkConstants, sfExcel8);
|
TestFormula(Format('DAY("%s")', [s]), '5', ftkConstants, sfExcel8);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -376,7 +380,7 @@ procedure TSpreadSingleFormulaTests.HourConst_BIFF8;
|
|||||||
var
|
var
|
||||||
s: String;
|
s: String;
|
||||||
begin
|
begin
|
||||||
s := FormatDateTime(ExprFormatSettings.LongTimeFormat, EncodeTime(14, 20, 41, 0), ExprFormatSettings);
|
s := FormatDateTime(ExprFormatSettings.LongTimeFormat, EncodeTime(14, 20, 41, 0), DefaultFormatSettings);
|
||||||
TestFormula(Format('HOUR("%s")', [s]), '14', ftkConstants, sfExcel8);
|
TestFormula(Format('HOUR("%s")', [s]), '14', ftkConstants, sfExcel8);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -515,6 +519,23 @@ end;
|
|||||||
|
|
||||||
{ --- }
|
{ --- }
|
||||||
|
|
||||||
|
procedure TSpreadSingleFormulaTests.IfConst_BIFF8;
|
||||||
|
begin
|
||||||
|
TestFormula('IF(C3="A","is A","not A")', 'is A', ftkConstants, sfExcel8);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSpreadSingleFormulaTests.IfConst_OOXML;
|
||||||
|
begin
|
||||||
|
TestFormula('IF(C3="A","is A","not A")', 'is A', ftkConstants, sfOOXML);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSpreadSingleFormulaTests.IfConst_ODS;
|
||||||
|
begin
|
||||||
|
TestFormula('IF(C3="A","is A","not A")', 'is A', ftkConstants, sfOpenDocument);
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ --- }
|
||||||
|
|
||||||
procedure TSpreadSingleFormulaTests.CountIfRange_BIFF8;
|
procedure TSpreadSingleFormulaTests.CountIfRange_BIFF8;
|
||||||
begin
|
begin
|
||||||
TestFormula('COUNTIF(C3:C5,">1")', '1', ftkCellRange, sfExcel8);
|
TestFormula('COUNTIF(C3:C5,">1")', '1', ftkCellRange, sfExcel8);
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
{------------------------------------------------------------------------------}
|
{------------------------------------------------------------------------------}
|
||||||
// Addition
|
// Addition
|
||||||
Row := 0;
|
Row := 0;
|
||||||
|
|
||||||
formula := '1+1';
|
formula := '1+1';
|
||||||
MyWorksheet.WriteText(Row, 0, formula);
|
MyWorksheet.WriteText(Row, 0, formula);
|
||||||
if UseRPNFormula then
|
if UseRPNFormula then
|
||||||
@ -1874,7 +1873,7 @@
|
|||||||
|
|
||||||
// DAY / argument string
|
// DAY / argument string
|
||||||
inc(Row);
|
inc(Row);
|
||||||
s := DateToStr(EncodeDate(2014, 7, 1)); // Localization of the test
|
s := DateToStr(EncodeDate(2014, 7, 1), ExprFormatSettings); // Localization of the test
|
||||||
formula := 'DAY("'+s+'")';
|
formula := 'DAY("'+s+'")';
|
||||||
MyWorksheet.WriteText(Row, 0, formula);
|
MyWorksheet.WriteText(Row, 0, formula);
|
||||||
if UseRPNFormula then
|
if UseRPNFormula then
|
||||||
@ -1906,7 +1905,7 @@
|
|||||||
|
|
||||||
// HOUR / argument string
|
// HOUR / argument string
|
||||||
inc(Row);
|
inc(Row);
|
||||||
s := TimeToStr(EncodeTime(9, 59, 20, 0)); // Localization of the test
|
s := TimeToStr(EncodeTime(9, 59, 20, 0), ExprFormatSettings); // Localization of the test
|
||||||
formula := 'HOUR("'+s+'")';
|
formula := 'HOUR("'+s+'")';
|
||||||
MyWorksheet.WriteText(Row, 0, formula);
|
MyWorksheet.WriteText(Row, 0, formula);
|
||||||
if UseRPNFormula then
|
if UseRPNFormula then
|
||||||
@ -1938,7 +1937,7 @@
|
|||||||
|
|
||||||
// MINUTE / argument string
|
// MINUTE / argument string
|
||||||
inc(Row);
|
inc(Row);
|
||||||
s := TimeToStr(EncodeTime(9, 59, 20, 0)); // Localization of the test
|
s := TimeToStr(EncodeTime(9, 59, 20, 0), ExprFormatSettings); // Localization of the test
|
||||||
formula := 'MINUTE("'+s+'")';
|
formula := 'MINUTE("'+s+'")';
|
||||||
MyWorksheet.WriteText(Row, 0, formula);
|
MyWorksheet.WriteText(Row, 0, formula);
|
||||||
if UseRPNFormula then
|
if UseRPNFormula then
|
||||||
@ -1970,7 +1969,7 @@
|
|||||||
|
|
||||||
// MONTH / argument string
|
// MONTH / argument string
|
||||||
inc(Row);
|
inc(Row);
|
||||||
s := DateToStr(EncodeDate(2014, 7, 1)); // Localization of the test
|
s := DateToStr(EncodeDate(2014, 7, 1), ExprFormatSettings); // Localization of the test
|
||||||
formula := 'MONTH("'+s+'")';
|
formula := 'MONTH("'+s+'")';
|
||||||
MyWorksheet.WriteText(Row, 0, formula);
|
MyWorksheet.WriteText(Row, 0, formula);
|
||||||
if UseRPNFormula then
|
if UseRPNFormula then
|
||||||
@ -2022,7 +2021,7 @@
|
|||||||
|
|
||||||
// SECOND / argument string
|
// SECOND / argument string
|
||||||
inc(Row);
|
inc(Row);
|
||||||
s := TimeToStr(EncodeTime(9, 59, 20, 0)); // Localization of the test
|
s := TimeToStr(EncodeTime(9, 59, 20, 0), ExprFormatSettings); // Localization of the test
|
||||||
formula := 'SECOND("'+s+'")';
|
formula := 'SECOND("'+s+'")';
|
||||||
MyWorksheet.WriteText(Row, 0, formula);
|
MyWorksheet.WriteText(Row, 0, formula);
|
||||||
if UseRPNFormula then
|
if UseRPNFormula then
|
||||||
@ -2054,7 +2053,7 @@
|
|||||||
|
|
||||||
// TIMEVALUE
|
// TIMEVALUE
|
||||||
inc(Row);
|
inc(Row);
|
||||||
s := TimeToStr(EncodeTime(9, 59, 20, 0)); // Localization!
|
s := TimeToStr(EncodeTime(9, 59, 20, 0), ExprFormatSettings); // Localization!
|
||||||
formula := 'TIMEVALUE("'+s+'")';
|
formula := 'TIMEVALUE("'+s+'")';
|
||||||
MyWorksheet.WriteText(Row, 0, formula);
|
MyWorksheet.WriteText(Row, 0, formula);
|
||||||
if UseRPNFormula then
|
if UseRPNFormula then
|
||||||
@ -2104,7 +2103,7 @@
|
|||||||
|
|
||||||
// WEEKDAY / argument string
|
// WEEKDAY / argument string
|
||||||
inc(Row);
|
inc(Row);
|
||||||
s := DateToStr(EncodeDate(2014, 7, 1)); // Localization of the test
|
s := DateToStr(EncodeDate(2014, 7, 1), ExprFormatSettings); // Localization of the test
|
||||||
formula := 'WEEKDAY("'+s+'")';
|
formula := 'WEEKDAY("'+s+'")';
|
||||||
MyWorksheet.WriteText(Row, 0, formula);
|
MyWorksheet.WriteText(Row, 0, formula);
|
||||||
if UseRPNFormula then
|
if UseRPNFormula then
|
||||||
@ -2136,7 +2135,7 @@
|
|||||||
|
|
||||||
// YEAR / argument string
|
// YEAR / argument string
|
||||||
inc(Row);
|
inc(Row);
|
||||||
s := DateToStr(EncodeDate(2014, 7, 1)); // Localization of the test
|
s := DateToStr(EncodeDate(2014, 7, 1), ExprFormatSettings); // Localization of the test
|
||||||
formula := 'YEAR("'+s+'")';
|
formula := 'YEAR("'+s+'")';
|
||||||
MyWorksheet.WriteText(Row, 0, formula);
|
MyWorksheet.WriteText(Row, 0, formula);
|
||||||
if UseRPNFormula then
|
if UseRPNFormula then
|
||||||
|
Reference in New Issue
Block a user