diff --git a/components/fpspreadsheet/fpsnumformatparser.pas b/components/fpspreadsheet/fpsnumformatparser.pas index 2e3534197..1bc0bbc71 100644 --- a/components/fpspreadsheet/fpsnumformatparser.pas +++ b/components/fpspreadsheet/fpsnumformatparser.pas @@ -89,13 +89,14 @@ type // Format string function BuildFormatString: String; virtual; // Token analysis + { function GetTokenIntValueAt(AToken: TsNumFormatToken; ASection,AIndex: Integer): Integer; function IsNumberAt(ASection,AIndex: Integer; out ANumFormat: TsNumberFormat; out ADecimals: Byte; out ANextIndex: Integer): Boolean; function IsTextAt(AText: string; ASection, AIndex: Integer): Boolean; function IsTokenAt(AToken: TsNumFormatToken; ASection,AIndex: Integer): Boolean; - + } public constructor Create(AWorkbook: TsWorkbook; const AFormatString: String); destructor Destroy; override; @@ -292,13 +293,11 @@ end; procedure TsNumFormatParser.CheckSection(ASection: Integer); var - el, next, i: Integer; + el, i: Integer; section: PsNumFormatSection; nfs, nfsTest: String; nf: TsNumberFormat; - datetimeFormats: set of TsNumberformat; - f1,f2: Integer; - decs: Byte; + formats: set of TsNumberFormat; begin if FStatus <> psOK then exit; @@ -308,6 +307,12 @@ begin for el := 0 to High(section^.Elements) do case section^.Elements[el].Token of + nftZeroDecs: + section^.Decimals := section^.Elements[el].IntValue; + nftFracNumSpaceDigit, nftFracNumZeroDigit: + section^.FracNumerator := section^.Elements[el].IntValue; + nftFracDenomSpaceDigit, nftFracDenomZeroDigit: + section^.FracDenominator := section^.Elements[el].IntValue; nftPercent: section^.Kind := section^.Kind + [nfkPercent]; nftExpChar: @@ -370,13 +375,18 @@ begin section^.NumFormat := nfTimeInterval else begin - datetimeFormats := [nfShortDateTime, nfLongDate, nfShortDate, nfLongTime, + formats := [nfShortDateTime, nfLongDate, nfShortDate, nfLongTime, nfShortTime, nfLongTimeAM, nfShortTimeAM, nfDayMonth, nfMonthYear]; - for nf in datetimeFormats do + for nf in formats do begin nfsTest := BuildDateTimeFormatString(nf, FWorkbook.FormatSettings); if Length(nfsTest) = Length(nfs) then begin + if SameText(nfs, nfsTest) then + begin + section^.NumFormat := nf; + break; + end; for i := 1 to Length(nfsTest) do case nfsTest[i] of '/': if not (nf in [nfLongTimeAM, nfShortTimeAM]) then @@ -390,17 +400,28 @@ begin break; end; end; -{ - if SameText(nfs, BuildDateTimeFormatString(nf, FWorkbook.FormatSettings)) then - begin - section^.NumFormat := nf; - break; - end; - } end; end; end else begin + nfs := GetFormatString; + formats := [nfFixed, nfFixedTh, nfPercentage, nfExp, nfFraction]; + for nf in formats do begin + nfsTest := BuildNumberFormatString(nf, FWorkbook.FormatSettings, section^.Decimals); + if SameText(nfs, nfsTest) then + begin + section^.NumFormat := nf; + break; + end; + end; + if (section^.NumFormat = nfCustom) and (nfkCurrency in section^.Kind) then + begin + section^.NumFormat := nfCurrency; + if section^.Color = scRed then + section^.NumFormat := nfCurrencyRed; + end; + + { el := 0; while el < Length(section^.Elements) do begin @@ -470,6 +491,7 @@ begin end; if (section^.NumFormat = nfCurrency) and (section^.Color = scRed) then section^.NumFormat := nfCurrencyRed; + } end; end; @@ -743,7 +765,7 @@ function TsNumFormatParser.GetParsedSections(AIndex: Integer): TsNumFormatSectio begin Result := FSections[AIndex]; end; - + { function TsNumFormatParser.GetTokenIntValueAt(AToken: TsNumFormatToken; ASection, AIndex: Integer): Integer; begin @@ -752,7 +774,7 @@ begin else Result := -1; end; - + } { Returns true if the format elements contain at least one date/time token } function TsNumFormatParser.IsDateTimeFormat: Boolean; var @@ -766,7 +788,7 @@ begin end; Result := false; end; - + { function TsNumFormatParser.IsNumberAt(ASection, AIndex: Integer; out ANumFormat: TsNumberFormat; out ADecimals: Byte; out ANextIndex: Integer): Boolean; @@ -837,7 +859,7 @@ begin Result := IsTokenAt(nftText, ASection, AIndex) and (FSections[ASection].Elements[AIndex].TextValue = AText); end; - + } { Returns true if the format elements contain only time, no date tokens. } function TsNumFormatParser.IsTimeFormat: Boolean; var @@ -851,7 +873,7 @@ begin end; Result := false; end; - + { function TsNumFormatParser.IsTokenAt(AToken: TsNumFormatToken; ASection, AIndex: Integer): Boolean; begin @@ -859,7 +881,7 @@ begin (AIndex < Length(FSections[ASection].Elements)) and (FSections[ASection].Elements[AIndex].Token = AToken); end; - + } { Limits the decimals to 0 or 2, as required by Excel2. } procedure TsNumFormatParser.LimitDecimals; var diff --git a/components/fpspreadsheet/tests/numformatparsertests.pas b/components/fpspreadsheet/tests/numformatparsertests.pas index c680b01c7..cf0bf06e8 100644 --- a/components/fpspreadsheet/tests/numformatparsertests.pas +++ b/components/fpspreadsheet/tests/numformatparsertests.pas @@ -19,11 +19,14 @@ type SollNumFormat: TsNumberFormat; SollSectionCount: Integer; SollDecimals: Byte; + SollFactor: Double; + SollNumeratorDigits: Integer; + SollDenominatorDigits: Integer; SollCurrencySymbol: String; end; var - ParserTestData: Array[0..8] of TParserTestData; + ParserTestData: Array[0..10] of TParserTestData; procedure InitParserTestData; @@ -56,6 +59,9 @@ begin SollNumFormat := nfFixed; SollSectionCount := 1; SollDecimals := 0; + SollFactor := 0; + SollNumeratorDigits := 0; + SollDenominatorDigits := 0; SollCurrencySymbol := ''; end; with ParserTestData[1] do begin @@ -64,6 +70,9 @@ begin SollNumFormat := nfFixed; SollSectionCount := 1; SollDecimals := 3; + SollFactor := 0; + SollNumeratorDigits := 0; + SollDenominatorDigits := 0; SollCurrencySymbol := ''; end; with ParserTestData[2] do begin @@ -72,6 +81,9 @@ begin SollNumFormat := nfFixedTh; SollSectionCount := 1; SollDecimals := 3; + SollFactor := 0; + SollNumeratorDigits := 0; + SollDenominatorDigits := 0; SollCurrencySymbol := ''; end; with ParserTestData[3] do begin @@ -80,6 +92,9 @@ begin SollNumFormat := nfPercentage; SollSectionCount := 1; SollDecimals := 3; + SollFactor := 0; + SollNumeratorDigits := 0; + SollDenominatorDigits := 0; SollCurrencySymbol := ''; end; with ParserTestData[4] do begin @@ -88,6 +103,9 @@ begin SollNumFormat := nfLongTime; SollSectionCount := 1; SollDecimals := 0; + SollFactor := 0; + SollNumeratorDigits := 0; + SollDenominatorDigits := 0; SollCurrencySymbol := ''; end; with ParserTestData[5] do begin @@ -96,32 +114,66 @@ begin SollNumFormat := nfLongTimeAM; SollSectionCount := 1; SollDecimals := 0; + SollFactor := 0; + SollNumeratorDigits := 0; + SollDenominatorDigits := 0; SollCurrencySymbol := ''; end; with ParserTestData[6] do begin FormatString := '[$-409]hh:mm:ss\ AM/PM;@'; - SollFormatString := 'hh:mm:ss AM/PM'; - SollNumFormat := nfLongTimeAM; + SollFormatString := 'hh:mm:ss\ AM/PM;@'; + SollNumFormat := nfCustom; SollSectionCount := 2; SollDecimals := 0; + SollFactor := 0; + SollNumeratorDigits := 0; + SollDenominatorDigits := 0; SollCurrencySymbol := ''; end; with ParserTestData[7] do begin FormatString := '[$-F400]dd.mm.yy\ hh:mm'; - SollFormatString := 'dd.mm.yy hh:mm'; - SollNumFormat := nfShortDateTime; + SollFormatString := 'DD.MM.YY\ hh:mm'; + SollNumFormat := nfCustom; SollSectionCount := 1; SollDecimals := 0; + SollFactor := 0; + SollNumeratorDigits := 0; + SollDenominatorDigits := 0; SollCurrencySymbol := ''; end; with ParserTestData[8] do begin - FormatString := '[$€] #,##0.00;-[$€] #,##0.00;{$€} 0.00'; - SollFormatString := '"€" #,##0.00;-"€" #,##0.00;"€" 0.00'; + FormatString := '[$€] #,##0.00;-[$€] #,##0.00;[$€] 0.00'; + SollFormatString := '[$€] #,##0.00;-[$€] #,##0.00;[$€] 0.00'; SollNumFormat := nfCurrency; SollSectionCount := 3; SollDecimals := 2; + SollFactor := 0; + SollNumeratorDigits := 0; + SollDenominatorDigits := 0; SollCurrencySymbol := '€'; end; + with ParserTestData[9] do begin + FormatString := '0.00,,'; + SollFormatString := '0.00,,'; + SollNumFormat := nfCustom; + SollSectionCount := 1; + SollDecimals := 2; + SollFactor := 1e-6; + SollNumeratorDigits := 0; + SollDenominatorDigits := 0; + SollCurrencySymbol := ''; + end; + with ParserTestData[10] do begin + FormatString := '# ??/??'; + SollFormatString := '# ??/??'; + SollNumFormat := nfFraction; + SollSectionCount := 1; + SollDecimals := 0; + SollFactor := 0; + SollNumeratorDigits := 2; + SollDenominatorDigits := 2; + SollCurrencySymbol := ''; + end; { with ParserTestData[5] do begin FormatString := '#,##0.00 "$";-#,##0.00 "$";-'; @@ -167,21 +219,28 @@ var begin MyWorkbook := TsWorkbook.Create; // needed to provide the FormatSettings for the parser try - for i:=0 to 5 do begin + for i:=0 to High(ParserTestData) do begin parser := TsNumFormatParser.Create(MyWorkbook, ParserTestData[i].FormatString); try actual := parser.FormatString; CheckEquals(ParserTestData[i].SollFormatString, actual, 'Test format string ' + ParserTestData[i].SollFormatString + ' construction mismatch'); - CheckEquals(ord(ParserTestData[i].SollNumFormat), ord(parser.ParsedSections[0].NumFormat), - 'Test format (' + GetEnumName(TypeInfo(TsNumberFormat), integer(ParserTestData[i].SollNumFormat)) + - ') detection mismatch'); + CheckEquals( + GetEnumName(TypeInfo(TsNumberFormat), ord(ParserTestData[i].SollNumFormat)), + GetEnumName(TypeInfo(TsNumberformat), ord(parser.ParsedSections[0].NumFormat)), + 'Test format (' + ParserTestData[i].FormatString + ') detection mismatch'); CheckEquals(ParserTestData[i].SollDecimals, parser.ParsedSections[0].Decimals, 'Test format (' + ParserTestData[i].FormatString + ') decimal detection mismatch'); CheckEquals(ParserTestData[i].SollCurrencySymbol, parser.ParsedSections[0].CurrencySymbol, 'Test format (' + ParserTestData[i].FormatString + ') currency symbol detection mismatch'); CheckEquals(ParserTestData[i].SollSectionCount, parser.ParsedSectionCount, 'Test format (' + ParserTestData[i].FormatString + ') section count mismatch'); + CheckEquals(ParserTestData[i].SollFactor, parser.ParsedSections[0].Factor, + 'Test format (' + ParserTestData[i].FormatString + ') factor mismatch'); + CheckEquals(ParserTestData[i].SollNumeratorDigits, parser.ParsedSections[0].FracNumerator, + 'Test format (' + ParserTestData[i].FormatString + ') numerator digits mismatch'); + CheckEquals(ParserTestData[i].SollDenominatorDigits, parser.ParsedSections[0].FracDenominator, + 'Test format (' + ParserTestData[i].FormatString + ') denominator digits mismatch'); finally parser.Free; end;