diff --git a/components/fpspreadsheet/examples/fpsgrid/mainform.pas b/components/fpspreadsheet/examples/fpsgrid/mainform.pas index 67a0bcb25..fd4bc2cb6 100644 --- a/components/fpspreadsheet/examples/fpsgrid/mainform.pas +++ b/components/fpspreadsheet/examples/fpsgrid/mainform.pas @@ -463,6 +463,10 @@ begin exit; cell := Worksheet.FindCell(GetWorksheetRow(Row), GetWorksheetCol(Col)); if (cell <> nil) then begin + if cell^.NumberFormat = nfGeneral then begin + Worksheet.WriteNumberFormat(cell, nfFixed, '0.00'); + exit; + end; Worksheet.GetNumberFormatAttributes(cell, decs, currSym); if (Sender = AcIncDecimals) then Worksheet.WriteDecimals(cell, decs+1) @@ -507,6 +511,7 @@ begin r := GetWorksheetRow(Row); cell := Worksheet.GetCell(r, c); Worksheet.GetNumberFormatAttributes(cell, decs, cs); + if cs = '' then cs := '?'; case cell^.ContentType of cctNumber, cctDateTime: if isDateTimeFmt then begin diff --git a/components/fpspreadsheet/fpsnumformatparser.pas b/components/fpspreadsheet/fpsnumformatparser.pas index e93788cee..55ed9c8ea 100644 --- a/components/fpspreadsheet/fpsnumformatparser.pas +++ b/components/fpspreadsheet/fpsnumformatparser.pas @@ -91,6 +91,10 @@ type procedure AddElement(AToken: TsNumFormatToken; AIntValue: Integer); overload; procedure AddElement(AToken: TsNumFormatToken; AFloatValue: Double); overload; procedure AddSection; + procedure DeleteElement(ASection, AIndex: Integer); + procedure InsertElement(ASection, AIndex: Integer; AToken: TsNumFormatToken; AText: String); overload; + procedure InsertElement(ASection, AIndex: Integer; AToken: TsNumFormatToken; AIntValue: Integer); overload; + procedure InsertElement(ASection, AIndex: Integer; AToken: TsNumFormatToken; AFloatValue: Double); overload; function NextToken: Char; function PrevToken: Char; @@ -634,6 +638,55 @@ begin end; *) +procedure TsNumFormatParser.DeleteElement(ASection, AIndex: Integer); +var + i, n: Integer; +begin + n := Length(FSections[ASection].Elements); + for i:= AIndex+1 to n-1 do + FSections[ASection].Elements[i-1] := FSections[ASection].Elements[i]; + SetLength(FSections[ASection].Elements, n-1); +end; + +procedure TsNumFormatParser.InsertElement(ASection, AIndex: Integer; + AToken: TsNumFormatToken; AText: String); +var + i, n: Integer; +begin + n := Length(FSections[ASection].Elements); + SetLength(FSections[ASection].Elements, n+1); + for i:= n-1 downto AIndex+1 do + FSections[ASection].Elements[i+1] := FSections[ASection].Elements[i]; + FSections[ASection].Elements[AIndex+1].Token := AToken; + FSections[ASection].Elements[AIndex+1].TextValue := AText; +end; + +procedure TsNumFormatParser.InsertElement(ASection, AIndex: Integer; + AToken: TsNumFormatToken; AIntValue: Integer); +var + i, n: Integer; +begin + n := Length(FSections[ASection].Elements); + SetLength(FSections[ASection].Elements, n+1); + for i:= n-1 downto AIndex+1 do + FSections[ASection].Elements[i+1] := FSections[ASection].Elements[i]; + FSections[ASection].Elements[AIndex+1].Token := AToken; + FSections[ASection].Elements[AIndex+1].IntValue := AIntValue; +end; + +procedure TsNumFormatParser.InsertElement(ASection, AIndex: Integer; + AToken: TsNumFormatToken; AFloatValue: Double); +var + i, n: Integer; +begin + n := Length(FSections[ASection].Elements); + SetLength(FSections[ASection].Elements, n+1); + for i:= n-1 downto AIndex+1 do + FSections[ASection].Elements[i+1] := FSections[ASection].Elements[i]; + FSections[ASection].Elements[AIndex+1].Token := AToken; + FSections[ASection].Elements[AIndex+1].FloatValue := AFloatValue; +end; + function TsNumFormatParser.GetFormatString(ADialect: TsNumFormatDialect): String; var i: Integer; @@ -1653,25 +1706,31 @@ var i, j, n: Integer; begin for j := 0 to High(FSections) do begin - i := 0; n := Length(FSections[j].Elements); - while (i < n) do begin + i := n-1; + while (i > -1) do begin case FSections[j].Elements[i].Token of nftDigit: // no decimals so far --> add decimal separator and decimals element - if i = n-1 then begin - AddElement(nftDecSep, '.'); - AddElement(nftDecs, AValue); - exit; + if (AValue > 0) then begin + // Don't use "AddElements" because nfCurrency etc have elements after the number. + InsertElement(j, i, nftDecSep, '.'); + InsertElement(j, i+1, nftDecs, AValue); + break; end; nftDecs: - begin + if AValue > 0 then begin // decimals are already used, just replace value of decimal places FSections[j].Elements[i].IntValue := AValue; - exit; + break; + end else begin + // No decimals any more: delete decs and decsep elements + DeleteElement(j, i); + DeleteElement(j, i-1); + break; end; end; - inc(i); + dec(i); end; end; end; diff --git a/components/fpspreadsheet/fpspreadsheet.pas b/components/fpspreadsheet/fpspreadsheet.pas index fa12e4f96..b5a1f3f8e 100755 --- a/components/fpspreadsheet/fpspreadsheet.pas +++ b/components/fpspreadsheet/fpspreadsheet.pas @@ -1399,14 +1399,22 @@ function TsWorksheet.GetNumberFormatAttributes(ACell: PCell; out ADecimals: byte out ACurrencySymbol: String): Boolean; var parser: TsNumFormatParser; + nf: TsNumberFormat; begin Result := false; if ACell <> nil then begin parser := TsNumFormatParser.Create(FWorkbook, ACell^.NumberFormatStr); try if parser.Status = psOK then begin - ADecimals := parser.Decimals; - ACurrencySymbol := parser.CurrencySymbol; + nf := parser.NumFormat; + if (nf = nfGeneral) or IsDateTimeFormat(nf) then begin + ADecimals := 2; + ACurrencySymbol := '?'; + end + else begin + ADecimals := parser.Decimals; + ACurrencySymbol := parser.CurrencySymbol; + end; Result := true; end; finally @@ -2053,14 +2061,14 @@ begin if ANegCurrFormat = -1 then ANegCurrFormat := Workbook.FormatSettings.NegCurrFormat; if ACurrencySymbol = '?' then - ACurrencySymbol := Workbook.FormatSettings.CurrencyString; + ACurrencySymbol := AnsiToUTF8(Workbook.FormatSettings.CurrencyString); fmt := BuildCurrencyFormatString( + nfdDefault, + AFormat, Workbook.FormatSettings, ADecimals, APosCurrFormat, ANegCurrFormat, - AFormat in [nfCurrencyRed, nfAccountingRed], - AFormat in [nfAccounting, nfAccountingRed], ACurrencySymbol); WriteCurrency(ACell, AValue, AFormat, fmt); @@ -3836,20 +3844,6 @@ begin // Iterate through all cells and collect the individual styles for i := 0 to Workbook.GetWorksheetCount - 1 do IterateThroughCells(nil, Workbook.GetWorksheetByIndex(i).Cells, ListAllFormattingStylesCallback); - - (* - // Convert the numberformats of the collected styles to be compatible with the destination file - for i:=0 to High(FFormattingStyles) do - if (FFormattingStyles[i].NumberFormatStr <> '') and - (FFormattingStyles[i].NumberFormat <> nfCustom) // don't touch custom formatstrings! - then - FNumFormatList.ConvertBeforeWriting( - FFormattingStyles[i].NumberFormatStr, - FFormattingStyles[i].NumberFormat, - FFormattingStyles[i].Decimals, - FFormattingStyles[i].CurrencySymbol - ); - *) end; {@@ diff --git a/components/fpspreadsheet/fpsutils.pas b/components/fpspreadsheet/fpsutils.pas index 15afa8a39..e5abbb6db 100644 --- a/components/fpspreadsheet/fpsutils.pas +++ b/components/fpspreadsheet/fpsutils.pas @@ -12,7 +12,7 @@ unit fpsutils; interface uses - Classes, SysUtils, StrUtils, fpspreadsheet; + Classes, SysUtils, StrUtils, fpspreadsheet, fpsNumFormatParser; // Exported types type @@ -72,9 +72,9 @@ function IsCurrencyFormat(AFormat: TsNumberFormat): Boolean; function IsDateTimeFormat(AFormat: TsNumberFormat): Boolean; overload; //function IsDateTimeFormat(AFormatStr: String): Boolean; overload; -function BuildCurrencyFormatString(const AFormatSettings: TFormatSettings; - ADecimals, APosCurrFormat, ANegCurrFormat: Integer; - ANegativeValuesRed, AAccountingStyle: Boolean; ACurrencySymbol: String = '?'): String; +function BuildCurrencyFormatString(ADialect: TsNumFormatDialect; + ANumberFormat: TsNumberFormat; const AFormatSettings: TFormatSettings; + ADecimals, APosCurrFormat, ANegCurrFormat: Integer; ACurrencySymbol: String): String; function BuildDateTimeFormatString(ANumberFormat: TsNumberFormat; const AFormatSettings: TFormatSettings; AFormatString: String = ''): String; function BuildNumberFormatString(ANumberFormat: TsNumberFormat; @@ -624,9 +624,9 @@ end; negative values) to apply a red font color. This code has to be removed by StripAccountingSymbols before applying to FormatFloat. } -function BuildCurrencyFormatString(const AFormatSettings: TFormatSettings; - ADecimals, APosCurrFormat, ANegCurrFormat: Integer; ANegativeValuesRed: Boolean; - AAccountingStyle: Boolean; ACurrencySymbol: String = '?'): String; +function BuildCurrencyFormatString(ADialect: TsNumFormatDialect; + ANumberFormat: TsNumberFormat; const AFormatSettings: TFormatSettings; + ADecimals, APosCurrFormat, ANegCurrFormat: Integer; ACurrencySymbol: String): String; const POS_FMT: array[0..3, boolean] of string = ( // Parameter 0 is "value", parameter 1 is "currency symbol" @@ -658,6 +658,8 @@ var decs: String; cf, ncf: Byte; p, n: String; + accStyle: Boolean; + negRed: Boolean; begin cf := IfThen(APosCurrFormat < 0, AFormatSettings.CurrencyFormat, APosCurrFormat); ncf := IfThen(ANegCurrFormat < 0, AFormatSettings.NegCurrFormat, ANegCurrFormat); @@ -668,10 +670,13 @@ begin decs := DupeString('0', ADecimals); if ADecimals > 0 then decs := '.' + decs; - p := POS_FMT[cf, AAccountingStyle]; - n := NEG_FMT[ncf, AAccountingStyle]; + accStyle := ANumberFormat in [nfAccounting, nfAccountingRed]; + negRed := ANumberFormat in [nfCurrencyRed, nfAccountingRed]; + + p := POS_FMT[cf, accStyle]; + n := NEG_FMT[ncf, accStyle]; // add extra space for the sign of the number for perfect alignment in Excel - if AAccountingStyle then + if accStyle then case ncf of 0, 14: p := p + '_)'; 3, 11: p := p + '_-'; @@ -681,17 +686,22 @@ begin if ACurrencySymbol <> '' then begin Result := Format(p, ['#,##0' + decs, ACurrencySymbol]) + ';' - + IfThen(ANegativeValuesRed, '[red]', '') + Format(n, ['#,##0' + decs, ACurrencySymbol]) + ';' - + Format(p, [IfThen(AAccountingStyle, '-', '0'+decs), ACurrencySymbol]); + + IfThen(negRed and (ADialect = nfdExcel), '[red]', '') + + Format(n, ['#,##0' + decs, ACurrencySymbol]) + ';' + + Format(p, [IfThen(accStyle, '-', '0'+decs), ACurrencySymbol]); end else begin Result := '#,##0' + decs; + if negRed and (ADialect = nfdExcel) then + Result := Result +';[red]' + else + Result := Result +';'; case ncf of - 0, 14, 15 : Result := Result + ';(#,##0' + decs + ')'; - 1, 2, 5, 6, 8, 9, 12: Result := Result + ';-#,##0' + decs; - else Result := Result + ';#,##0' + decs + '-'; + 0, 14, 15 : Result := Result + '(#,##0' + decs + ')'; + 1, 2, 5, 6, 8, 9, 12: Result := Result + '-#,##0' + decs; + else Result := Result + '#,##0' + decs + '-'; end; - Result := Result + ';' + IfThen(AAccountingStyle, '-', '0'+decs); + Result := Result + ';' + IfThen(accStyle, '-', '0'+decs); end; end;