From 13a9fb260398aa4f1b2f166c7d4be1505a54cbce Mon Sep 17 00:00:00 2001 From: wp_xxyyzz Date: Mon, 23 Jun 2014 09:15:56 +0000 Subject: [PATCH] fpspreadsheet: Remove built-in number formats nfSci (not suppored by ods and fpc) and nfAccounting/nfAccountingRed (too much display-oriented, too cumbersome to implement for reading and displaying in the grid). If needed in Excel they can still be written using nfCustom. Update all files and tests for these now no longer existing formats. git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3222 8e941d3f-bd1b-0410-a28a-d453659cc2b4 --- .../examples/excel2demo/excel2write.lpr | 40 --- .../examples/excel5demo/excel5write.lpr | 28 --- .../examples/excel8demo/excel8write.lpr | 37 +-- .../examples/opendocdemo/opendocwrite.lpr | 35 +-- .../examples/spready/mainform.lfm | 200 ++++++++------- .../examples/spready/mainform.pas | 27 +- .../fpspreadsheet/fpsnumformatparser.pas | 53 +--- components/fpspreadsheet/fpspreadsheet.pas | 40 ++- .../fpspreadsheet/fpspreadsheetgrid.pas | 191 ++------------ components/fpspreadsheet/fpsutils.pas | 235 ++++++------------ .../fpspreadsheet/tests/formattests.pas | 12 +- .../fpspreadsheet/tests/numberstests.pas | 27 +- .../fpspreadsheet/tests/spreadtestgui.lpi | 1 + .../fpspreadsheet/tests/stringtests.pas | 20 +- .../fpspreadsheet/tests/testbiff8_1899.xls | Bin 34816 -> 34816 bytes .../fpspreadsheet/tests/testbiff8_1904.xls | Bin 35328 -> 34816 bytes .../fpspreadsheet/tests/testodf_1899.ods | Bin 18295 -> 18176 bytes .../fpspreadsheet/tests/testodf_1904.ods | Bin 19197 -> 19408 bytes components/fpspreadsheet/xlsbiff2.pas | 36 +-- components/fpspreadsheet/xlscommon.pas | 10 +- 20 files changed, 291 insertions(+), 701 deletions(-) diff --git a/components/fpspreadsheet/examples/excel2demo/excel2write.lpr b/components/fpspreadsheet/examples/excel2demo/excel2write.lpr index 92e97b4af..0f9139622 100644 --- a/components/fpspreadsheet/examples/excel2demo/excel2write.lpr +++ b/components/fpspreadsheet/examples/excel2demo/excel2write.lpr @@ -212,36 +212,6 @@ begin MyWorksheet.WriteNumber(r, 2, -number, nfFixedTh, 3); MyWorksheet.WriteFontColor(r, 2, scGray); inc(r); - MyWorksheet.WriteUTF8Text(r, 0, 'nfSci, 1 dec'); - MyWorksheet.WriteNumber(r, 1, number, nfSci, 1); - MyWorksheet.WriteFontColor(r, 1, scGray); - MyWorksheet.WriteNumber(r, 2, -number, nfSci, 1); - MyWorksheet.WriteFontColor(r, 2, scGray); - MyWorksheet.WriteNumber(r, 3, 1.0/number, nfSci, 1); - MyWorksheet.WriteFontColor(r, 3, scGray); - MyWorksheet.WriteNumber(r, 4, -1.0/number, nfSci, 1); - MyWorksheet.WriteFontColor(r, 4, scGray); - inc(r); - MyWorksheet.WriteUTF8Text(r, 0, 'nfSci, 2 dec'); - MyWorksheet.WriteNumber(r, 1, number, nfSci, 2); - MyWorksheet.WriteFontColor(r, 1, scGray); - MyWorksheet.WriteNumber(r, 2, -number, nfSci, 2); - MyWorksheet.WriteFontColor(r, 2, scGray); - MyWorksheet.WriteNumber(r, 3, 1.0/number, nfSci, 2); - MyWorksheet.WriteFontColor(r, 3, scGray); - MyWorksheet.WriteNumber(r, 4, -1.0/number, nfSci, 2); - MyWorksheet.WriteFontColor(r, 4, scGray); - inc(r); - MyWorksheet.WriteUTF8Text(r, 0, 'nfSci, 3 dec'); - MyWorksheet.WriteNumber(r, 1, number, nfSci, 3); - MyWorksheet.WriteFontColor(r, 1, scGray); - MyWorksheet.WriteNumber(r, 2, -number, nfSci, 3); - MyWorksheet.WriteFontColor(r, 2, scGray); - MyWorksheet.WriteNumber(r, 3, 1.0/number, nfSci, 3); - MyWorksheet.WriteFontColor(r, 3, scGray); - MyWorksheet.WriteNumber(r, 4, -1.0/number, nfSci, 3); - MyWorksheet.WriteFontColor(r, 4, scGray); - inc(r); MyWorksheet.WriteUTF8Text(r, 0, 'nfExp, 1 dec'); MyWorksheet.WriteNumber(r, 1, number, nfExp, 1); MyWorksheet.WriteFontColor(r, 1, scGray); @@ -278,16 +248,6 @@ begin MyWorksheet.WriteCurrency(r, 2, -number, nfCurrencyRed, 0, 'USD'); MyWorksheet.WriteCurrency(r, 3, 0.0, nfCurrencyRed, 0, 'USD'); inc(r); - MyWorksheet.WriteUTF8Text(r, 0, 'nfAccounting, 0 decs'); - MyWorksheet.WriteCurrency(r, 1, number, nfAccounting, 0, 'USD'); - MyWorksheet.WriteCurrency(r, 2, -number, nfAccounting, 0, 'USD'); - MyWorksheet.WriteCurrency(r, 3, 0.0, nfAccounting, 0, 'USD'); - inc(r); - MyWorksheet.WriteUTF8Text(r, 0, 'nfAccountingRed, 0 decs'); - MyWorksheet.WriteCurrency(r, 1, number, nfAccountingRed, 0, 'USD'); - MyWorksheet.WriteCurrency(r, 2, -number, nfAccountingRed, 0, 'USD'); - MyWorksheet.WriteCurrency(r, 3, 0.0, nfAccountingRed, 0, 'USD'); - inc(r, 2); MyWorksheet.WriteUTF8Text(r, 0, 'nfCustom, "$"#,##0_);("$"#,##0)'); MyWorksheet.WriteNumber(r, 1, number); MyWorksheet.WriteFontColor(r, 1, scGray); diff --git a/components/fpspreadsheet/examples/excel5demo/excel5write.lpr b/components/fpspreadsheet/examples/excel5demo/excel5write.lpr index d896159de..0f950117c 100644 --- a/components/fpspreadsheet/examples/excel5demo/excel5write.lpr +++ b/components/fpspreadsheet/examples/excel5demo/excel5write.lpr @@ -238,24 +238,6 @@ begin MyWorksheet.WriteNumber(r, 1, number, nfFixedTh, 3); MyWorksheet.WriteNumber(r, 2, -number, nfFixedTh, 3); inc(r); - MyWorksheet.WriteUTF8Text(r, 0, 'nfSci, 1 dec'); - MyWorksheet.WriteNumber(r, 1, number, nfSci, 1); - MyWorksheet.WriteNumber(r, 2, -number, nfSci, 1); - MyWorksheet.WriteNumber(r, 3, 1.0/number, nfSci, 1); - MyWorksheet.WriteNumber(r, 4, -1.0/number, nfSci, 1); - inc(r); - MyWorksheet.WriteUTF8Text(r, 0, 'nfSci, 2 dec'); - MyWorksheet.WriteNumber(r, 1, number, nfSci, 2); - MyWorksheet.WriteNumber(r, 2, -number, nfSci, 2); - MyWorksheet.WriteNumber(r, 3, 1.0/number, nfSci, 2); - MyWorksheet.WriteNumber(r, 4, -1.0/number, nfSci, 2); - inc(r); - MyWorksheet.WriteUTF8Text(r, 0, 'nfSci, 3 dec'); - MyWorksheet.WriteNumber(r, 1, number, nfSci, 3); - MyWorksheet.WriteNumber(r, 2, -number, nfSci, 3); - MyWorksheet.WriteNumber(r, 3, 1.0/number, nfSci, 3); - MyWorksheet.WriteNumber(r, 4, -1.0/number, nfSci, 3); - inc(r); MyWorksheet.WriteUTF8Text(r, 0, 'nfExp, 1 dec'); MyWorksheet.WriteNumber(r, 1, number, nfExp, 1); MyWorksheet.WriteNumber(r, 2, -number, nfExp, 1); @@ -284,16 +266,6 @@ begin MyWorksheet.WriteCurrency(r, 1, number, nfCurrencyRed, 0, 'USD'); MyWorksheet.WriteCurrency(r, 2, -number, nfCurrencyRed, 0, 'USD'); MyWorksheet.WriteCurrency(r, 3, 0.0, nfCurrencyRed, 0, 'USD'); - inc(r); - MyWorksheet.WriteUTF8Text(r, 0, 'nfAccounting, 0 decs'); - MyWorksheet.WriteCurrency(r, 1, number, nfAccounting, 0, 'USD'); - MyWorksheet.WriteCurrency(r, 2, -number, nfAccounting, 0, 'USD'); - MyWorksheet.WriteCurrency(r, 3, 0.0, nfAccounting, 0, 'USD'); - inc(r); - MyWorksheet.WriteUTF8Text(r, 0, 'nfAccountingRed, 0 decs'); - MyWorksheet.WriteCurrency(r, 1, number, nfAccountingRed, 0, 'USD'); - MyWorksheet.WriteCurrency(r, 2, -number, nfAccountingRed, 0, 'USD'); - MyWorksheet.WriteCurrency(r, 3, 0.0, nfAccountingRed, 0, 'USD'); inc(r, 2); MyWorksheet.WriteUTF8Text(r, 0, 'nfCustom, "$"#,##0_);("$"#,##0)'); diff --git a/components/fpspreadsheet/examples/excel8demo/excel8write.lpr b/components/fpspreadsheet/examples/excel8demo/excel8write.lpr index 49dfbae0f..248df45ec 100644 --- a/components/fpspreadsheet/examples/excel8demo/excel8write.lpr +++ b/components/fpspreadsheet/examples/excel8demo/excel8write.lpr @@ -256,30 +256,7 @@ begin MyWorksheet.WriteNumber(r, 1, number, nfFixedTh, 3); MyWorksheet.WriteNumber(r, 2, -number, nfFixedTh, 3); inc(r,2); - MyWorksheet.WriteUTF8Text(r, 0, 'nfSci, 0 dec'); - MyWorksheet.WriteNumber(r, 1, number, nfSci, 0); - MyWorksheet.WriteNumber(r, 2, -number, nfSci, 0); - MyWorksheet.WriteNumber(r, 3, 1.0/number, nfSci, 0); - MyWorksheet.WriteNumber(r, 4, -1.0/number, nfSci, 0); - inc(r); - MyWorksheet.WriteUTF8Text(r, 0, 'nfSci, 1 dec'); - MyWorksheet.WriteNumber(r, 1, number, nfSci, 1); - MyWorksheet.WriteNumber(r, 2, -number, nfSci, 1); - MyWorksheet.WriteNumber(r, 3, 1.0/number, nfSci, 1); - MyWorksheet.WriteNumber(r, 4, -1.0/number, nfSci, 1); - inc(r); - MyWorksheet.WriteUTF8Text(r, 0, 'nfSci, 2 dec'); - MyWorksheet.WriteNumber(r, 1, number, nfSci, 2); - MyWorksheet.WriteNumber(r, 2, -number, nfSci, 2); - MyWorksheet.WriteNumber(r, 3, 1.0/number, nfSci, 2); - MyWorksheet.WriteNumber(r, 4, -1.0/number, nfSci, 2); - inc(r); - MyWorksheet.WriteUTF8Text(r, 0, 'nfSci, 3 dec'); - MyWorksheet.WriteNumber(r, 1, number, nfSci, 3); - MyWorksheet.WriteNumber(r, 2, -number, nfSci, 3); - MyWorksheet.WriteNumber(r, 3, 1.0/number, nfSci, 3); - MyWorksheet.WriteNumber(r, 4, -1.0/number, nfSci, 3); - inc(r); + MyWorksheet.WriteUTF8Text(r, 0, 'nfExp, 0 dec'); MyWorksheet.WriteNumber(r, 1, number, nfExp, 0); MyWorksheet.WriteNumber(r, 2, -number, nfExp, 0); @@ -314,18 +291,8 @@ begin MyWorksheet.WriteCurrency(r, 1, number, nfCurrencyRed, 0, 'USD'); MyWorksheet.WriteCurrency(r, 2, -number, nfCurrencyRed, 0, 'USD'); MyWorksheet.WriteCurrency(r, 3, 0.0, nfCurrencyRed, 0, 'USD'); - inc(r); - MyWorksheet.WriteUTF8Text(r, 0, 'nfAccounting, 0 decs'); - MyWorksheet.WriteCurrency(r, 1, number, nfAccounting, 0, 'USD'); - MyWorksheet.WriteCurrency(r, 2, -number, nfAccounting, 0, 'USD'); - MyWorksheet.WriteCurrency(r, 3, 0.0, nfAccounting, 0, 'USD'); - inc(r); - MyWorksheet.WriteUTF8Text(r, 0, 'nfAccountingRed, 0 decs'); - MyWorksheet.WriteCurrency(r, 1, -number, nfAccountingRed, 0, 'USD'); - MyWorksheet.WriteCurrency(r, 2, number, nfAccountingRed, 0, 'USD'); - MyWorksheet.WriteCurrency(r, 3, 0.0, nfAccountingRed, 0, 'USD'); - { + { inc(r,2); MyWorksheet.WriteUTF8Text(r, 0, 'nfCustom, "EUR "#,##0_);("EUR "#,##0)'); MyWorksheet.WriteNumber(r, 1, number); diff --git a/components/fpspreadsheet/examples/opendocdemo/opendocwrite.lpr b/components/fpspreadsheet/examples/opendocdemo/opendocwrite.lpr index 9769918ce..181db344c 100644 --- a/components/fpspreadsheet/examples/opendocdemo/opendocwrite.lpr +++ b/components/fpspreadsheet/examples/opendocdemo/opendocwrite.lpr @@ -184,16 +184,6 @@ begin MyWorksheet.WriteNumber(row, 7, number7, nfExp, 3); MyWorksheet.WriteNumber(row, 8, number8, nfExp, 3); inc(row); - MyWorksheet.WriteUTF8Text(row, 0, 'nfSci, 2 decimals'); - MyWorksheet.WriteNumber(row, 1, number1, nfSci, 2); - MyWorksheet.WriteNumber(row, 2, number2, nfSci, 2); - MyWorksheet.WriteNumber(row, 3, number3, nfSci, 2); - MyWorksheet.WriteNumber(row, 4, number4, nfSci, 2); - MyWorksheet.WriteNumber(row, 5, number5, nfSci, 2); - MyWorksheet.WriteNumber(row, 6, number6, nfSci, 2); - MyWorksheet.WriteNumber(row, 7, number7, nfSci, 2); - MyWorksheet.WriteNumber(row, 8, number8, nfSci, 2); - inc(row); MyWorksheet.WriteUTF8Text(row, 0, 'nfCurrency, 2 decimals'); MyWorksheet.WriteCurrency(row, 1, number1, nfCurrency, 2, '$'); MyWorksheet.WriteCurrency(row, 2, number2, nfCurrency, 2, '$'); @@ -213,28 +203,8 @@ begin MyWorksheet.WriteCurrency(row, 6, number6, nfCurrencyRed, 2, '$', pcfCSV, ncfBCSVB); MyWorksheet.WriteCurrency(row, 7, number7, nfCurrencyRed, 2, '$', pcfCSV, ncfBCSVB); MyWorksheet.WriteCurrency(row, 8, number8, nfCurrencyRed, 2, '$', pcfCSV, ncfBCSVB); - inc(row); - // Note: nfAccounting's not supported by ods, will be replaced by nfCurrency's - MyWorksheet.WriteUTF8Text(row, 0, 'nfAccounting, 2 decimals'); - MyWorksheet.WriteCurrency(row, 1, number1, nfAccounting, 2, '$'); - MyWorksheet.WriteCurrency(row, 2, number2, nfAccounting, 2, '$'); - MyWorksheet.WriteCurrency(row, 3, number3, nfAccounting, 2, '$'); - MyWorksheet.WriteCurrency(row, 4, number4, nfAccounting, 2, '$'); - MyWorksheet.WriteCurrency(row, 5, number5, nfAccounting, 2, '$'); - MyWorksheet.WriteCurrency(row, 6, number6, nfAccounting, 2, '$'); - MyWorksheet.WriteCurrency(row, 7, number7, nfAccounting, 2, '$'); - MyWorksheet.WriteCurrency(row, 8, number8, nfAccounting, 2, '$'); - inc(row); - MyWorksheet.WriteUTF8Text(row, 0, 'nfAccountingRed, 2 decimals, >0: EUR 1000, <0: -EUR 1000)'); - MyWorksheet.WriteCurrency(row, 1, number1, nfAccountingRed, 2, 'EUR', pcfCSV, ncfMCSV); - MyWorksheet.WriteCurrency(row, 2, number2, nfAccountingRed, 2, 'EUR', pcfCSV, ncfMCSV); - MyWorksheet.WriteCurrency(row, 3, number3, nfAccountingRed, 2, 'EUR', pcfCSV, ncfMCSV); - MyWorksheet.WriteCurrency(row, 4, number4, nfAccountingRed, 2, 'EUR', pcfCSV, ncfMCSV); - MyWorksheet.WriteCurrency(row, 5, number5, nfAccountingRed, 2, 'EUR', pcfCSV, ncfMCSV); - MyWorksheet.WriteCurrency(row, 6, number6, nfAccountingRed, 2, 'EUR', pcfCSV, ncfMCSV); - MyWorksheet.WriteCurrency(row, 7, number7, nfAccountingRed, 2, 'EUR', pcfCSV, ncfMCSV); - MyWorksheet.WriteCurrency(row, 8, number8, nfAccountingRed, 2, 'EUR', pcfCSV, ncfMCSV); inc(row,2); + MyWorksheet.WriteUTF8Text(row, 0, 'Some date/time values in various formats:'); inc(row); MyWorksheet.WriteUTF8Text(row, 0, 'nfShortDateTime'); @@ -264,6 +234,9 @@ begin MyWorksheet.WriteUTF8Text(row, 0, 'nfLongTimeAM'); MyWorksheet.WriteDateTime(row, 1, dt1, nfLongTimeAM); MyWorksheet.WriteDateTime(row, 2, dt2, nfLongTimeAM); + inc(row,2); + + MyWorksheet.WriteUTF8Text(row, 0, 'Some custom formats'); inc(row); // In order to use a semicolon as a date-time separator it must be escaped either by // using the backslash or quotes (because the semicolon is the separator between sections) diff --git a/components/fpspreadsheet/examples/spready/mainform.lfm b/components/fpspreadsheet/examples/spready/mainform.lfm index 380c67b11..12c90740b 100644 --- a/components/fpspreadsheet/examples/spready/mainform.lfm +++ b/components/fpspreadsheet/examples/spready/mainform.lfm @@ -4,7 +4,7 @@ object Form1: TForm1 Top = 248 Width = 884 Caption = 'spready' - ClientHeight = 629 + ClientHeight = 624 ClientWidth = 884 Menu = MainMenu OnActivate = FormActivate @@ -14,7 +14,7 @@ object Form1: TForm1 object Panel1: TPanel Left = 0 Height = 85 - Top = 544 + Top = 539 Width = 884 Align = alBottom BevelOuter = bvNone @@ -23,9 +23,9 @@ object Form1: TForm1 TabOrder = 0 object CbShowHeaders: TCheckBox Left = 8 - Height = 19 + Height = 24 Top = 8 - Width = 93 + Width = 116 Caption = 'Show headers' Checked = True OnClick = CbShowHeadersClick @@ -34,9 +34,9 @@ object Form1: TForm1 end object CbShowGridLines: TCheckBox Left = 8 - Height = 19 + Height = 24 Top = 32 - Width = 100 + Width = 125 Caption = 'Show grid lines' Checked = True OnClick = CbShowGridLinesClick @@ -45,7 +45,7 @@ object Form1: TForm1 end object EdFrozenCols: TSpinEdit Left = 389 - Height = 23 + Height = 28 Top = 8 Width = 52 OnChange = EdFrozenColsChange @@ -53,7 +53,7 @@ object Form1: TForm1 end object EdFrozenRows: TSpinEdit Left = 389 - Height = 23 + Height = 28 Top = 39 Width = 52 OnChange = EdFrozenRowsChange @@ -61,37 +61,37 @@ object Form1: TForm1 end object Label1: TLabel Left = 304 - Height = 15 + Height = 20 Top = 13 - Width = 62 + Width = 77 Caption = 'Frozen cols:' FocusControl = EdFrozenCols ParentColor = False end object Label2: TLabel Left = 304 - Height = 15 + Height = 20 Top = 40 - Width = 66 + Width = 82 Caption = 'Frozen rows:' FocusControl = EdFrozenRows ParentColor = False end object CbReadFormulas: TCheckBox Left = 8 - Height = 19 + Height = 24 Top = 56 - Width = 96 + Width = 120 Caption = 'Read formulas' OnChange = CbReadFormulasChange TabOrder = 4 end object CbHeaderStyle: TComboBox Left = 152 - Height = 23 + Height = 28 Top = 8 Width = 116 - ItemHeight = 15 + ItemHeight = 20 ItemIndex = 2 Items.Strings = ( 'Lazarus' @@ -106,7 +106,7 @@ object Form1: TForm1 end object PageControl1: TPageControl Left = 0 - Height = 465 + Height = 460 Top = 79 Width = 884 ActivePage = TabSheet1 @@ -116,11 +116,11 @@ object Form1: TForm1 OnChange = PageControl1Change object TabSheet1: TTabSheet Caption = 'Sheet1' - ClientHeight = 437 + ClientHeight = 427 ClientWidth = 876 object WorksheetGrid: TsWorksheetGrid Left = 0 - Height = 437 + Height = 427 Top = 0 Width = 876 FrozenCols = 0 @@ -136,7 +136,7 @@ object Form1: TForm1 TitleStyle = tsNative OnSelection = WorksheetGridSelection ColWidths = ( - 42 + 56 64 64 64 @@ -244,19 +244,19 @@ object Form1: TForm1 end object FontComboBox: TComboBox Left = 52 - Height = 23 + Height = 28 Top = 2 Width = 127 - ItemHeight = 15 + ItemHeight = 20 OnSelect = FontComboBoxSelect TabOrder = 0 end object FontSizeComboBox: TComboBox Left = 179 - Height = 23 + Height = 28 Top = 2 Width = 48 - ItemHeight = 15 + ItemHeight = 20 Items.Strings = ( '8' '9' @@ -977,6 +977,9 @@ object Form1: TForm1 Action = AcNFGeneral AutoCheck = True end + object MenuItem38: TMenuItem + Caption = '-' + end object MenuItem34: TMenuItem Action = AcNFFixed AutoCheck = True @@ -993,8 +996,54 @@ object Form1: TForm1 Action = AcNFExp AutoCheck = True end - object MenuItem38: TMenuItem - Action = AcNFSci + object MenuItem47: TMenuItem + Caption = '-' + end + object MenuItem48: TMenuItem + Action = AcNFCurrency + AutoCheck = True + end + object MenuItem49: TMenuItem + Action = AcNFCurrencyRed + AutoCheck = True + end + object MenuItem50: TMenuItem + Caption = '-' + end + object MenuItem51: TMenuItem + Action = AcNFShortDateTime + AutoCheck = True + end + object MenuItem52: TMenuItem + Action = AcNFShortDate + AutoCheck = True + end + object MenuItem53: TMenuItem + Action = AcNFLongDate + AutoCheck = True + end + object MenuItem54: TMenuItem + Action = AcNFCustomDM + AutoCheck = True + end + object MenuItem55: TMenuItem + Action = AcNFCustomMY + AutoCheck = True + end + object MenuItem56: TMenuItem + Action = AcNFShortTime + AutoCheck = True + end + object MenuItem57: TMenuItem + Action = AcNFLongTime + AutoCheck = True + end + object MenuItem58: TMenuItem + Action = AcNFShortTimeAM + AutoCheck = True + end + object MenuItem59: TMenuItem + Action = AcNFLongTimeAM AutoCheck = True end end @@ -2398,127 +2447,106 @@ object Form1: TForm1 Caption = 'Exponential' OnExecute = AcNumFormatExecute end - object AcNFSci: TAction - Tag = 1040 - Category = 'Format' - AutoCheck = True - Caption = 'Scientific' - OnExecute = AcNumFormatExecute - end object AcNFPercentage: TAction - Tag = 1050 + Tag = 1040 Category = 'Format' AutoCheck = True Caption = 'Percentage' OnExecute = AcNumFormatExecute end object AcNFCurrency: TAction - Tag = 1060 + Tag = 1050 Category = 'Format' AutoCheck = True Caption = 'Currency' OnExecute = AcNumFormatExecute end object AcNFCurrencyRed: TAction - Tag = 1070 + Tag = 1060 Category = 'Format' AutoCheck = True Caption = 'Currency (negative values in red)' OnExecute = AcNumFormatExecute end - object AcNFAccounting: TAction - Tag = 1080 - Category = 'Format' - AutoCheck = True - Caption = 'Accounting' - OnExecute = AcNumFormatExecute - end - object AcNFAccountingRed: TAction - Tag = 1090 - Category = 'Format' - AutoCheck = True - Caption = 'Accounting (negative values in red)' - OnExecute = AcNumFormatExecute - end object AcNFShortDateTime: TAction - Tag = 1100 + Tag = 1070 Category = 'Format' AutoCheck = True Caption = 'Date + time' OnExecute = AcNumFormatExecute end object AcNFShortDate: TAction - Tag = 1110 + Tag = 1080 Category = 'Format' AutoCheck = True Caption = 'Short date' OnExecute = AcNumFormatExecute end - object AcNFFmtDateTimeDM: TAction - Tag = 1181 - Category = 'Format' - AutoCheck = True - Caption = 'Day + month' - OnExecute = AcNumFormatExecute - end - object AcNFFmtDateTimeMY: TAction - Tag = 1182 - Category = 'Format' - AutoCheck = True - Caption = 'Month + year' - OnExecute = AcNumFormatExecute - end object AcNFLongDate: TAction - Tag = 1120 + Tag = 1090 Category = 'Format' AutoCheck = True Caption = 'Long date' OnExecute = AcNumFormatExecute end + object AcNFCustomDM: TAction + Tag = 1151 + Category = 'Format' + AutoCheck = True + Caption = 'Day + month' + OnExecute = AcNumFormatExecute + end + object AcNFCustomMY: TAction + Tag = 1152 + Category = 'Format' + AutoCheck = True + Caption = 'Month + year' + OnExecute = AcNumFormatExecute + end object AcNFShortTime: TAction - Tag = 1130 + Tag = 1100 Category = 'Format' AutoCheck = True Caption = 'Short time' OnExecute = AcNumFormatExecute end object AcNFLongTime: TAction - Tag = 1140 + Tag = 1110 Category = 'Format' AutoCheck = True Caption = 'Long time' OnExecute = AcNumFormatExecute end object AcNFShortTimeAM: TAction - Tag = 1150 + Tag = 1120 Category = 'Format' AutoCheck = True Caption = 'Short time AM/PM' OnExecute = AcNumFormatExecute end object AcNFLongTimeAM: TAction - Tag = 1160 + Tag = 1130 Category = 'Format' AutoCheck = True Caption = 'Long time AM/PM' OnExecute = AcNumFormatExecute end - object AcNFFmtDateTimeMS: TAction - Tag = 1183 + object AcNFCusstomMS: TAction + Tag = 1153 Category = 'Format' AutoCheck = True Caption = 'Minutes + seconds' OnExecute = AcNumFormatExecute end - object AcNFFmtDateTimeMSZ: TAction - Tag = 1184 + object AcNFCustomMSZ: TAction + Tag = 1154 Category = 'Format' AutoCheck = True Caption = 'Minutes + seconds + milliseconds' OnExecute = AcNumFormatExecute end object AcNFTimeInterval: TAction - Tag = 1170 + Tag = 1140 Category = 'Format' AutoCheck = True Caption = 'Time interval' @@ -3218,10 +3246,6 @@ object Form1: TForm1 Action = AcNFExp AutoCheck = True end - object MnuNFSci: TMenuItem - Action = AcNFSci - AutoCheck = True - end object MenuItem39: TMenuItem Caption = '-' end @@ -3233,14 +3257,6 @@ object Form1: TForm1 Action = AcNFCurrencyRed AutoCheck = True end - object MnuAccounting: TMenuItem - Action = AcNFAccounting - AutoCheck = True - end - object MnuAccountingRed: TMenuItem - Action = AcNFAccountingRed - AutoCheck = True - end object MenuItem40: TMenuItem Caption = '-' end @@ -3260,11 +3276,11 @@ object Form1: TForm1 AutoCheck = True end object MnuFmtDateTimeDM: TMenuItem - Action = AcNFFmtDateTimeDM + Action = AcNFCustomDM AutoCheck = True end object MnuFmtDateTimeMY: TMenuItem - Action = AcNFFmtDateTimeMY + Action = AcNFCustomMY AutoCheck = True end object MenuItem43: TMenuItem @@ -3287,11 +3303,11 @@ object Form1: TForm1 AutoCheck = True end object MenuItem45: TMenuItem - Action = AcNFFmtDateTimeMS + Action = AcNFCusstomMS AutoCheck = True end object MnuFmtDateTimeMSZ: TMenuItem - Action = AcNFFmtDateTimeMSZ + Action = AcNFCustomMSZ AutoCheck = True end object MenuItem44: TMenuItem diff --git a/components/fpspreadsheet/examples/spready/mainform.pas b/components/fpspreadsheet/examples/spready/mainform.pas index 9bfd2be7c..68810ce75 100644 --- a/components/fpspreadsheet/examples/spready/mainform.pas +++ b/components/fpspreadsheet/examples/spready/mainform.pas @@ -53,12 +53,9 @@ type AcDecDecimals: TAction; AcNFGeneral: TAction; AcNFExp: TAction; - AcNFSci: TAction; AcCopyFormat: TAction; AcNFCurrency: TAction; AcNFCurrencyRed: TAction; - AcNFAccounting: TAction; - AcNFAccountingRed: TAction; AcNFShortDateTime: TAction; AcNFShortDate: TAction; AcNFLongDate: TAction; @@ -67,10 +64,10 @@ type AcNFShortTimeAM: TAction; AcNFLongTimeAM: TAction; AcNFTimeInterval: TAction; - AcNFFmtDateTimeDM: TAction; - AcNFFmtDateTimeMY: TAction; - AcNFFmtDateTimeMS: TAction; - AcNFFmtDateTimeMSZ: TAction; + AcNFCustomDM: TAction; + AcNFCustomMY: TAction; + AcNFCusstomMS: TAction; + AcNFCustomMSZ: TAction; AcNew: TAction; AcWordwrap: TAction; AcVAlignDefault: TAction; @@ -132,6 +129,19 @@ type MenuItem44: TMenuItem; MenuItem45: TMenuItem; MenuItem46: TMenuItem; + MenuItem47: TMenuItem; + MenuItem48: TMenuItem; + MenuItem49: TMenuItem; + MenuItem50: TMenuItem; + MenuItem51: TMenuItem; + MenuItem52: TMenuItem; + MenuItem53: TMenuItem; + MenuItem54: TMenuItem; + MenuItem55: TMenuItem; + MenuItem56: TMenuItem; + MenuItem57: TMenuItem; + MenuItem58: TMenuItem; + MenuItem59: TMenuItem; MnuFmtDateTimeMSZ: TMenuItem; MnuTimeInterval: TMenuItem; MnuShortTimeAM: TMenuItem; @@ -143,8 +153,6 @@ type MnuLongTime: TMenuItem; MnuLongDate: TMenuItem; MnuShortDateTime: TMenuItem; - MnuAccountingRed: TMenuItem; - MnuAccounting: TMenuItem; MnuCurrencyRed: TMenuItem; MnuCurrency: TMenuItem; MnuNumberFormat: TMenuItem; @@ -152,7 +160,6 @@ type MnuNFFixedTh: TMenuItem; MnuNFPercentage: TMenuItem; MnuNFExp: TMenuItem; - MnuNFSci: TMenuItem; MnuNFGeneral: TMenuItem; MnuTextRotation: TMenuItem; MenuItem3: TMenuItem; diff --git a/components/fpspreadsheet/fpsnumformatparser.pas b/components/fpspreadsheet/fpsnumformatparser.pas index 193ef5dac..422e9a2e7 100644 --- a/components/fpspreadsheet/fpsnumformatparser.pas +++ b/components/fpspreadsheet/fpsnumformatparser.pas @@ -133,8 +133,6 @@ type var ANextIndex: Integer): Boolean; function IsNumberAt(ASection,AIndex: Integer; out ANumberFormat: TsNumberFormat; out ADecimals: Byte; out ANextIndex: Integer): Boolean; - function IsSciAt(ASection, AIndex: Integer; out ANumberFormat: TsNumberFormat; - out ADecimals: Byte; out ANextIndex: Integer): Boolean; function IsTextAt(AText: string; ASection, AIndex: Integer): Boolean; function IsTimeAt(ASection,AIndex: Integer; out ANumberFormat: TsNumberFormat; out ANextIndex: Integer): Boolean; @@ -179,7 +177,7 @@ begin inherited Create; FCreateMethod := 0; FWorkbook := AWorkbook; - FHasRedSection := (ANumFormat in [nfCurrencyRed, nfAccountingRed]); + FHasRedSection := (ANumFormat = nfCurrencyRed); Parse(AFormatString); end; @@ -584,10 +582,6 @@ begin end; end; - // Look for scientific format - if IsSciAt(ASection, 0, ANumFormat, ADecimals, next) then - exit; - // Currency? if IsCurrencyAt(ASection, ANumFormat, ADecimals, ACurrencySymbol, AColor) then exit; @@ -677,7 +671,7 @@ begin result := nfGeneral else begin Result := FSections[0].NumFormat; - if (Result in [nfCurrency, nfAccounting]) then begin + if (Result = nfCurrency) then begin if Length(FSections) = 2 then begin Result := FSections[1].NumFormat; if FSections[1].CurrencySymbol <> FSections[0].CurrencySymbol then begin @@ -688,10 +682,6 @@ begin (FSections[1].NumFormat in [nfCurrency, nfCurrencyRed]) then exit; - if FSections[1].NumFormat = nfAccounting then begin - Result := nfAccounting; - exit; - end; end else if Length(FSections) = 3 then begin Result := FSections[1].NumFormat; @@ -706,12 +696,6 @@ begin (FSections[2].NumFormat in [nfCurrency, nfCurrencyRed]) then exit; - if (FSections[1].NumFormat = nfAccounting) and - (FSections[2].NumFormat in [nfCurrency, nfAccounting]) - then begin - Result := nfAccounting; - exit; - end; end; Result := nfCustom; exit; @@ -739,7 +723,6 @@ function TsNumFormatParser.IsCurrencyAt(ASection: Integer; out ANumFormat: TsNumberFormat; out ADecimals: byte; out ACurrencySymbol: String; out AColor: TsColor): Boolean; var - isAccounting : Boolean; hasCurrSymbol: Boolean; hasColor: Boolean; el: Integer; @@ -750,7 +733,6 @@ begin ACurrencySymbol := ''; ADecimals := 0; AColor := scNotDefined; - isAccounting := false; hasColor := false; hasCurrSymbol := false; @@ -783,7 +765,7 @@ begin hasColor := true; end; nftRepeat: - isAccounting := true; + ; nftCurrSymbol: begin ACurrencySymbol := FSections[ASection].Elements[el].TextValue; @@ -812,13 +794,8 @@ begin Result := hasCurrSymbol and ((ANumFormat = nfFixedTh) or (ASection = 2)); if Result then begin - if isAccounting then begin - if AColor = scNotDefined then ANumFormat := nfAccounting else - if AColor = scRed then ANumFormat := nfAccountingRed; - end else begin - if AColor = scNotDefined then ANumFormat := nfCurrency else - if AColor = scRed then ANumFormat := nfCurrencyRed; - end; + if AColor = scNotDefined then ANumFormat := nfCurrency else + if AColor = scRed then ANumFormat := nfCurrencyRed; end else ANumFormat := nfCustom; end; @@ -974,26 +951,6 @@ begin end; end; -function TsNumFormatParser.IsSciAt(ASection, AIndex: Integer; - out ANumberFormat: TsNumberFormat; out ADecimals: Byte; out ANextIndex: Integer): Boolean; -begin - if IsTokenAt(nftOptDigit, ASection, AIndex) and // '#' - IsTokenAt(nftOptDigit, ASection, Aindex+1) and // '#' - IsTokenAt(nftDigit, ASection, AIndex+2) and // '0' - IsTokenAt(nftDecSep, ASection, AIndex+3) and // '.' - IsTokenAt(nftDecs, ASection, AIndex+4) and // count of decimals - IsTokenAt(nftExpChar, ASection, AIndex+5) and // E - IsTokenAt(nftExpSign, ASection, AIndex+6) and // +/- - IsTokenAt(nftExpDigits, ASection, AIndex+7) - then begin - Result := true; - ANumberFormat := nfSci; - ADecimals := FSections[ASection].Elements[AIndex+4].IntValue; - ANextIndex := AIndex + 8; - end else - Result := false; -end; - function TsNumFormatParser.IsTextAt(AText: String; ASection, AIndex: Integer): Boolean; begin Result := IsTokenAt(nftText, ASection, AIndex) and diff --git a/components/fpspreadsheet/fpspreadsheet.pas b/components/fpspreadsheet/fpspreadsheet.pas index f18c687aa..4cea0f812 100755 --- a/components/fpspreadsheet/fpspreadsheet.pas +++ b/components/fpspreadsheet/fpspreadsheet.pas @@ -183,9 +183,9 @@ type // general-purpose for all numbers nfGeneral, // numbers - nfFixed, nfFixedTh, nfExp, nfSci, nfPercentage, + nfFixed, nfFixedTh, nfExp, nfPercentage, // currency - nfCurrency, nfCurrencyRed, nfAccounting, nfAccountingRed, + nfCurrency, nfCurrencyRed, // dates and times nfShortDateTime, {nfFmtDateTime, }nfShortDate, nfLongDate, nfShortTime, nfLongTime, nfShortTimeAM, nfLongTimeAM, nfTimeInterval, @@ -1779,18 +1779,8 @@ function TsWorksheet.ReadAsUTF8Text(ACell: PCell): ansistring; if (ANumberFormat = nfGeneral) or (ANumberFormatStr = '') then Result := FloatToStr(Value, fs) else - if ANumberFormat = nfSci then - Result := SciFloat(Value, CountDecs(ANumberFormatStr, ['0']), fs) - else if (ANumberFormat = nfPercentage) then Result := FormatFloat(ANumberFormatStr, Value*100, fs) - else - if (ANumberFormat in [nfAccounting, nfAccountingRed]) then - case SplitAccountingFormatString(ANumberFormatStr, Sign(Value), left, right) of - 0: Result := FormatFloat(ANumberFormatStr, Value, fs); - 1: Result := FormatFloat(left, abs(Value), fs) + ' ' + Right; - 2: Result := Left + ' ' + FormatFloat(right, abs(Value), fs); - end else Result := FormatFloat(ANumberFormatStr, Value, fs) end; @@ -2203,15 +2193,14 @@ begin if ACell <> nil then begin ACell^.ContentType := cctNumber; ACell^.NumberValue := ANumber; + ACell^.NumberFormat := AFormat; if AFormat <> nfGeneral then begin Include(ACell^.UsedFormattingFields, uffNumberFormat); - ACell^.NumberFormat := AFormat; ACell^.NumberFormatStr := BuildNumberFormatString(ACell^.NumberFormat, Workbook.FormatSettings, ADecimals); end else begin Exclude(ACell^.UsedFormattingFields, uffNumberFormat); - ACell^.NumberFormat := nfGeneral; ACell^.NumberFormatStr := ''; end; @@ -2268,11 +2257,16 @@ begin parser.Free; end; - Include(ACell^.UsedFormattingFields, uffNumberFormat); ACell^.ContentType := cctNumber; ACell^.NumberValue := ANumber; - ACell^.NumberFormat := AFormat; //nfCustom; - ACell^.NumberFormatStr := AFormatString; + ACell^.NumberFormat := AFormat; + if AFormat <> nfGeneral then begin + Include(ACell^.UsedFormattingFields, uffNumberFormat); + ACell^.NumberFormatStr := AFormatString; + end else begin + Exclude(ACell^.UsedFormattingFields, uffNumberFormat); + ACell^.NumberFormatStr := ''; + end; ChangedCell(ACell^.Row, ACell^.Col); end; @@ -2381,8 +2375,7 @@ end; @param ARow Cell row index @param ACol Cell column index @param AValue Number value to be written - @param AFormat Format identifier, must be nfCurrency, nfCurrencyRed, - nfAccounting, or nfAccountingRed + @param AFormat Format identifier, must be nfCurrency, or nfCurrencyRed. @param ADecimals Number of decimal places @param APosCurrFormat Code specifying the order of value, currency symbol and spaces (see pcfXXXX constants) @@ -2408,8 +2401,7 @@ end; @param ACell Pointer to the cell considered @param AValue Number value to be written - @param AFormat Format identifier, must be nfCurrency, nfCurrencyRed, - nfAccounting, or nfAccountingRed + @param AFormat Format identifier, must be nfCurrency or nfCurrencyRed. @param ADecimals Number of decimal places @param APosCurrFormat Code specifying the order of value, currency symbol and spaces (see pcfXXXX constants) @@ -2454,8 +2446,7 @@ end; @param ARow Cell row index @param ACol Cell column index @param AValue Number value to be written - @param AFormat Format identifier, must be nfCurrency, nfCurrencyRed, - nfAccounting, or nfAccountingRed + @param AFormat Format identifier, must be nfCurrency or nfCurrencyRed. @param AFormatString String of formatting codes, including currency symbol. Can contain sections for different formatting of positive and negative number. Example: '"EUR" #,##0.00;("EUR" #,##0.00)' @@ -2472,8 +2463,7 @@ end; @param ACell Pointer to the cell considered @param AValue Number value to be written - @param AFormat Format identifier, must be nfCurrency, nfCurrencyRed, - nfAccounting, or nfAccountingRed + @param AFormat Format identifier, must be nfCurrency or nfCurrencyRed. @param AFormatString String of formatting codes, including currency symbol. Can contain sections for different formatting of positive and negative number. Example: '"EUR" #,##0.00;("EUR" #,##0.00)' diff --git a/components/fpspreadsheet/fpspreadsheetgrid.pas b/components/fpspreadsheet/fpspreadsheetgrid.pas index 5859ef88e..3b675d6ba 100644 --- a/components/fpspreadsheet/fpspreadsheetgrid.pas +++ b/components/fpspreadsheet/fpspreadsheetgrid.pas @@ -695,7 +695,7 @@ begin Canvas.Font.Size := round(fnt.Size); end; end; - if (lCell^.NumberFormat in [nfCurrencyRed, nfAccountingRed]) and + if (lCell^.NumberFormat = nfCurrencyRed) and not IsNaN(lCell^.NumberValue) and (lCell^.NumberValue < 0) then Canvas.Font.Color := FWorkbook.GetPaletteColor(scRed); @@ -941,174 +941,34 @@ begin InflateRect(ARect, -constCellPadding, -constCellPadding); - if (lCell^.NumberFormat in [nfAccounting, nfAccountingRed]) and not IsNaN(lCell^.Numbervalue) - then begin - case SplitAccountingFormatString(lCell^.NumberFormatStr, Sign(lCell^.NumberValue), - txtLeft, txtRight) of - 1: begin - txtLeft := FormatFloat(txtLeft, lCell^.NumberValue); - if txtLeft = '' then exit; - txt := txtLeft + ' ' + txtRight; - end; - 2: begin - txtRight := FormatFloat(txtRight, lCell^.NumberValue); - if txtRight = '' then exit; - txt := txtLeft + ' ' + txtRight; - end; - end; - InternalDrawTextInCell(txtLeft, txt, ARect, 0, horAlign, vertAlign, - txtRot, wrapped, true); - InternalDrawTextInCell(txtRight, txt, ARect, 2, horAlign, vertAlign, - txtRot, wrapped, true); - end else begin - txt := GetCellText(ACol, ARow); - if txt = '' then - exit; - case txtRot of - trHorizontal: - case horAlign of - haLeft : justif := 0; - haCenter : justif := 1; - haRight : justif := 2; - end; - rtStacked, - rt90DegreeClockwiseRotation: - case vertAlign of - vaTop : justif := 0; - vaCenter: justif := 1; - vaBottom: justif := 2; - end; - rt90DegreeCounterClockwiseRotation: - case vertAlign of - vaTop : justif := 2; - vaCenter: justif := 1; - vaBottom: justif := 0; - end; - end; - InternalDrawTextInCell(txt, txt, ARect, justif, horAlign, vertAlign, - txtRot, wrapped, false); - end; -end; + txt := GetCellText(ACol, ARow); + if txt = '' then + exit; -(* - - - - - - procedure InternalDrawTextInCell(AText, AMeasureText: String; ARect: TRect; - AJustification: Byte; ACellHorAlign: TsHorAlignment; - ACellVertAlign: TsVertAlignment; ATextRot: TsTextRotation; - ATextWrap, ReplaceTooLong: Boolean); - - - - - if (lCell^.TextRotation in [trHorizontal, rtStacked]) or - (not (uffTextRotation in lCell^.UsedFormattingFields)) - then begin - // HORIZONAL TEXT DRAWING DIRECTION - ts := Canvas.TextStyle; - if wrapped then begin - ts.Wordbreak := true; - ts.SingleLine := false; - flags := DT_WORDBREAK and not DT_SINGLELINE; - LCLIntf.DrawText(Canvas.Handle, PChar(txt), Length(txt), txtRect, - DT_CALCRECT or flags); - w := txtRect.Right - txtRect.Left; - h := txtRect.Bottom - txtRect.Top; - end else begin - ts.WordBreak := false; - ts.SingleLine := false; - w := Canvas.TextWidth(txt); - h := Canvas.TextHeight('Tg'); - end; - - Canvas.Font.Orientation := 0; - ts.Alignment := HOR_ALIGNMENTS[horAlign]; - ts.Opaque := false; - if h > ARect.Bottom - ARect.Top then - ts.Layout := tlTop - else - ts.Layout := VERT_ALIGNMENTS[vertAlign]; - - Canvas.TextStyle := ts; - Canvas.TextRect(ARect, ARect.Left, ARect.Top, txt); - end - else - begin - // ROTATED TEXT DRAWING DIRECTION - L := TStringList.Create; - try - txtRect := Bounds(ARect.Left, ARect.Top, ARect.Bottom - ARect.Top, ARect.Right - ARect.Left); - hline := Canvas.TextHeight('Tg'); - if wrapped then begin - L.Text := WrapText(Canvas, txt, txtRect.Right - txtRect.Left); - flags := DT_WORDBREAK and not DT_SINGLELINE; - LCLIntf.DrawText(Canvas.Handle, PChar(L.Text), Length(L.Text), txtRect, - DT_CALCRECT or flags); - w := txtRect.Right - txtRect.Left; - h := txtRect.Bottom - txtRect.Top; - h0 := hline; - end - else begin - L.Text := txt; - w := Canvas.TextWidth(txt); - h := hline; - h0 := 0; + case txtRot of + trHorizontal: + case horAlign of + haLeft : justif := 0; + haCenter : justif := 1; + haRight : justif := 2; end; - - ts := Canvas.TextStyle; - ts.SingleLine := true; // Draw text line by line - ts.Clipping := false; - ts.Layout := tlTop; - ts.Alignment := taLeftJustify; - ts.Opaque := false; - - if lCell^.TextRotation = rt90DegreeClockwiseRotation then begin - // Clockwise - Canvas.Font.Orientation := -900; - case horAlign of - haLeft : P.X := Min(ARect.Right-1, ARect.Left + h - h0); - haCenter : P.X := Min(ARect.Right-1, (ARect.Left + ARect.Right + h) div 2); - haRight : P.X := ARect.Right - 1; - end; - for i:= 0 to L.Count-1 do begin - w := Canvas.TextWidth(L[i]); - case vertAlign of - vaTop : P.Y := ARect.Top; - vaCenter : P.Y := Max(ARect.Top, (ARect.Top + ARect.Bottom - w) div 2); - vaBottom : P.Y := Max(ARect.Top, ARect.Bottom - w); - end; - Canvas.TextRect(ARect, P.X, P.Y, L[i], ts); - dec(P.X, hline); - end - end - else begin - // Counter-clockwise - Canvas.Font.Orientation := +900; - case horAlign of - haLeft : P.X := ARect.Left; - haCenter : P.X := Max(ARect.Left, (ARect.Left + ARect.Right - h + h0) div 2); - haRight : P.X := MAx(ARect.Left, ARect.Right - h + h0); - end; - for i:= 0 to L.Count-1 do begin - w := Canvas.TextWidth(L[i]); - case vertAlign of - vaTop : P.Y := Min(ARect.Bottom, ARect.Top + w); - vaCenter : P.Y := Min(ARect.Bottom, (ARect.Top + ARect.Bottom + w) div 2); - vaBottom : P.Y := ARect.Bottom; - end; - Canvas.TextRect(ARect, P.X, P.Y, L[i], ts); - inc(P.X, hline); - end; + rtStacked, + rt90DegreeClockwiseRotation: + case vertAlign of + vaTop : justif := 0; + vaCenter: justif := 1; + vaBottom: justif := 2; + end; + rt90DegreeCounterClockwiseRotation: + case vertAlign of + vaTop : justif := 2; + vaCenter: justif := 1; + vaBottom: justif := 0; end; - finally - L.Free; - end; end; + InternalDrawTextInCell(txt, txt, ARect, justif, horAlign, vertAlign, + txtRot, wrapped, false); end; -*) procedure TsCustomWorksheetGrid.EditingDone; var @@ -1899,7 +1759,8 @@ end; filling the cell. The reason to separate AJustification from ACellHorAlign and ACelVertAlign is the output of nfAccounting formatted numbers where the numbers are always - right-aligned, and the currency symbol is left-aligned. } + right-aligned, and the currency symbol is left-aligned. + NOTE: THIS FEATURE IS NO LONGER SUPPORTED. } procedure TsCustomWorksheetGrid.InternalDrawTextInCell(AText, AMeasureText: String; ARect: TRect; AJustification: Byte; ACellHorAlign: TsHorAlignment; ACellVertAlign: TsVertAlignment; ATextRot: TsTextRotation; diff --git a/components/fpspreadsheet/fpsutils.pas b/components/fpspreadsheet/fpsutils.pas index 29a017906..acf261bb0 100644 --- a/components/fpspreadsheet/fpsutils.pas +++ b/components/fpspreadsheet/fpsutils.pas @@ -93,13 +93,9 @@ function MakeLongDateFormat(AShortDateFormat: String): String; function MakeShortDateFormat(AShortDateFormat: String): String; function SpecialDateTimeFormat(ACode: String; const AFormatSettings: TFormatSettings; ForWriting: Boolean): String; -function SplitAccountingFormatString(const AFormatString: String; ASection: ShortInt; - out ALeft, ARight: String): Byte; procedure SplitFormatString(const AFormatString: String; out APositivePart, ANegativePart, AZeroPart: String); -function SciFloat(AValue: Double; ADecimals: Byte): String; overload; -function SciFloat(AValue: Double; ADecimals: Byte; AFormatSettings: TFormatSettings): String; overload; procedure MakeTimeIntervalMask(Src: String; var Dest: String); // These two functions are copies of fpc trunk until they are available in stable fpc. @@ -565,11 +561,11 @@ begin if ACondition then Result := AValue1 else Result := AValue2; end; -{ Checks whether the given number format code is for currency or accounting +{ Checks whether the given number format code is for currency, i.e. requires currency symbol. } function IsCurrencyFormat(AFormat: TsNumberFormat): Boolean; begin - Result := AFormat in [nfCurrency, nfCurrencyRed, nfAccounting, nfAccountingRed]; + Result := AFormat in [nfCurrency, nfCurrencyRed]; end; { Checks whether the given number format code is for date/times. } @@ -648,69 +644,81 @@ begin end; end; -{ Builds a currency format string. The presentation of negative values (brackets, +{@@ + Builds a currency format string. The presentation of negative values (brackets, or minus signs) is taken from the provided format settings. The format string consists of three sections, separated by semicolons. - Additional code is inserted for the destination file format: - - AAccountingStyle = true adds code to align the currency symbols below each - other. - - ANegativeValuesRed adds code to the second section of the format code (for - negative values) to apply a red font color. - This code has to be removed by StripAccountingSymbols before applying to - FormatFloat. } + + @param ADialect Determines whether the format string is for use by + fpspreadsheet (nfdDefault) or by Excel (nfdExcel) + @param ANumberFormat Identifier of the built-in number format for which the + format string is to be generated. + @param AFormatSettings FormatSettings to be applied (used to extract default + values for the next parameters) + @param ADecimals number of decimal places. If < 0, the CurrencyDecimals + of the FormatSettings is used. + @param APosCurrFormat Identifier for the order of currency symbol, value and + spaces of positive values + - see pcfXXXX constants in fpspreadsheet.pas. + If < 0, the CurrencyFormat of the FormatSettings is used. + @param ANegCurrFormat Identifier for the order of currency symbol, value and + spaces of negative values. Specifies also usage of (). + - see ncfXXXX constants in fpspreadsheet.pas. + If < 0, the NegCurrFormat of the FormatSettings is used. + @param ACurrencySymbol Name of the currency, like $ or USD. + If ? the CurrencyString of the FormatSettings is used. + + @return String of formatting codes, such as '"$"#,##0.00;("$"#,##0.00);"EUR"0.00' +} 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" - // AccountingStyle = false --> 1st column, true --> 2nd column - ('"%1:s"%0:s', '"%1:s"* %0:s'), // 0: $1 - ('%0:s"%1:s"', '%0:s "%1:s"'), // 1: 1$ - ('"%1:s" %0:s', '"%1:s"* %0:s'), // 2: $ 1 - ('%0:s "%1:s"', '%0:s "%1:s"') // 3: 1 $ + POS_FMT: array[0..3] of string = ( + // Format parameter 0 is "value", parameter 1 is "currency symbol" + ('"%1:s"%0:s'), // 0: $1 + ('%0:s"%1:s"'), // 1: 1$ + ('"%1:s" %0:s'), // 2: $ 1 + ('%0:s "%1:s"') // 3: 1 $ ); - NEG_FMT: array[0..15, boolean] of string = ( - ('("%1:s"%0:s)', '"%1:s"* (%0:s)'), // 0: ($1) - ('-"%1:s"%0:s', '-* "%1:s" %0:s'), // 1: -$1 - ('"%1:s"-%0:s', '"%1:s"* -%0:s'), // 2: $-1 - ('"%1:s"%0:s-', '"%1:s"%0:s-'), // 3: $1- - ('(%0:s"%1:s")', '(%0:s)%1:s"'), // 4: (1$) - ('-%0:s"%1:s"', '-* %0:s"%1:s"'), // 5: -1$ - ('%0:s-"%1:s"', '%0:s-"%1:s"'), // 6: 1-$ - ('%0:s"%1:s"-', '%0:s-"%1:s"'), // 7: 1$- - ('-%0:s "%1:s"', '-* %0:s"%1:s"'), // 8: -1 $ - ('-"%1:s" %0:s', '-* "%1:s" %0:s'), // 9: -$ 1 - ('%0:s "%1:s"-', '%0:s- "%1:s"'), // 10: 1 $- - ('"%1:s" %0:s-', '"%1:s"* %0:s-'), // 11: $ 1- - ('"%1:s" -%0:s', '"%1:s"* -%0:s'), // 12: $ -1 - ('%0:s- "%1:s"', '%0:s- "%1:s"'), // 13: 1- $ - ('("%1:s" %0:s)', '"%1:s"* (%0:s)'), // 14: ($ 1) - ('(%0:s "%1:s")', '(%0:s "%1:s")') // 15: (1 $) + NEG_FMT: array[0..15] of string = ( + ('("%1:s"%0:s)'), // 0: ($1) + ('-"%1:s"%0:s'), // 1: -$1 + ('"%1:s"-%0:s'), // 2: $-1 + ('"%1:s"%0:s-'), // 3: $1- + ('(%0:s"%1:s")'), // 4: (1$) + ('-%0:s"%1:s"'), // 5: -1$ + ('%0:s-"%1:s"'), // 6: 1-$ + ('%0:s"%1:s"-'), // 7: 1$- + ('-%0:s "%1:s"'), // 8: -1 $ + ('-"%1:s" %0:s'), // 9: -$ 1 + ('%0:s "%1:s"-'), // 10: 1 $- + ('"%1:s" %0:s-'), // 11: $ 1- + ('"%1:s" -%0:s'), // 12: $ -1 + ('%0:s- "%1:s"'), // 13: 1- $ + ('("%1:s" %0:s)'), // 14: ($ 1) + ('(%0:s "%1:s")') // 15: (1 $) ); var decs: String; pcf, ncf: Byte; p, n: String; - accStyle: Boolean; negRed: Boolean; begin pcf := IfThen(APosCurrFormat < 0, AFormatSettings.CurrencyFormat, APosCurrFormat); ncf := IfThen(ANegCurrFormat < 0, AFormatSettings.NegCurrFormat, ANegCurrFormat); - if ADecimals < 0 then + if (ADecimals < 0) then ADecimals := AFormatSettings.CurrencyDecimals; if ACurrencySymbol = '?' then ACurrencySymbol := AnsiToUTF8(AFormatSettings.CurrencyString); decs := DupeString('0', ADecimals); if ADecimals > 0 then decs := '.' + decs; - accStyle := ANumberFormat in [nfAccounting, nfAccountingRed]; - negRed := ANumberFormat in [nfCurrencyRed, nfAccountingRed]; - - p := POS_FMT[pcf, accStyle]; - n := NEG_FMT[ncf, accStyle]; + negRed := (ANumberFormat = nfCurrencyRed); + p := POS_FMT[pcf]; // Format mask for positive values + n := NEG_FMT[ncf]; // Format mask for negative values // add extra space for the sign of the number for perfect alignment in Excel - if accStyle then + if ADialect = nfdExcel then case ncf of 0, 14: p := p + '_)'; 3, 11: p := p + '_-'; @@ -722,7 +730,7 @@ begin Result := Format(p, ['#,##0' + decs, ACurrencySymbol]) + ';' + IfThen(negRed and (ADialect = nfdExcel), '[red]', '') + Format(n, ['#,##0' + decs, ACurrencySymbol]) + ';' - + Format(p, [IfThen(accStyle, '-', '0'+decs), ACurrencySymbol]); + + Format(p, ['0'+decs, ACurrencySymbol]); end else begin Result := '#,##0' + decs; @@ -735,12 +743,22 @@ begin 1, 2, 5, 6, 8, 9, 12: Result := Result + '-#,##0' + decs; else Result := Result + '#,##0' + decs + '-'; end; - Result := Result + ';' + IfThen(accStyle, '-', '0'+decs); + Result := Result + ';0' + decs; end; end; -{ Builds a number format string from the number format code, the count of - decimals, and the currencysymbol (if not empty). } +{@@ + Builds a number format string from the number format code and the count of + decimal places. + + @param ANumberFormat Identifier of the built-in numberformat for which a + format string is to be generated + @param AFormatSettings FormatSettings for default parameters + @param ADecimals Number of decimal places. If < 0 the CurrencyDecimals + value of the FormatSettings is used. + + @return String of formatting codes, such as '#,##0.00' for nfFixedTh and 2 decimals +} function BuildNumberFormatString(ANumberFormat: TsNumberFormat; const AFormatSettings: TFormatSettings; ADecimals: Integer = -1): String; var @@ -758,11 +776,9 @@ begin Result := '#,##0' + decs; nfExp: Result := '0' + decs + 'E+00'; - nfSci: - Result := '##0' + decs + 'E+0'; nfPercentage: Result := '0' + decs + '%'; - nfCurrency, nfCurrencyRed, nfAccounting, nfAccountingRed: + nfCurrency, nfCurrencyRed: raise Exception.Create('BuildNumberFormatString: Use BuildCurrencyFormatString '+ 'to create a format string for currency values.'); nfShortDateTime, nfShortDate, nfLongDate, nfShortTime, nfLongTime, @@ -933,84 +949,6 @@ begin Result := ACode; end; -{ Splits the sections +1 (positive) or -1 (negative values) or 0 (zero values) - of the accounting format string at the position of the '*' into a left - and right part and returns 1 if the format string is in the left, and 2 if - it is in the right part. Additionally removes Excel format codes '_' } -function SplitAccountingFormatString(const AFormatString: String; ASection: ShortInt; - out ALeft, ARight: String): Byte; -var - P: PChar; - PStart, PEnd: PChar; - token: Char; - done: Boolean; - i: Integer; -begin - Result := 0; - PStart := PChar(@AFormatString[1]); - PEnd := PStart + Length(AFormatString); - P := PStart; - - done := false; - case ASection of - -1 : while (P < PEnd) and not done do begin - token := P^; - if token = ';' then done := true; - inc(P); - end; - 0 : for i := 1 to 2 do begin - done := false; - while (P < PEnd) and not done do begin - token := P^; - if token = ';' then done := true; - inc(P); - end; - end; - +1: ; - end; - - ALeft := ''; - done := false; - - while (P < PEnd) and not done do begin - token := P^; - case token of - '_': inc(P); - ';': done := true; - '"': ; - '*': begin - inc(P); - done := true; - end; - '0', - '#': begin - ALeft := ALeft + token; - Result := 1; - end; - else ALeft := ALeft + token; - end; - inc(P); - end; - - ARight := ''; - done := false; - while (P < PEnd) and not done do begin - token := P^; - case token of - '_': inc(P); - ';': done := true; - '"': ; - '0', - '#': begin - ARight := ARight + token; - Result := 2; - end; - else ARight := ARight + token; - end; - inc(P); - end; -end; - procedure SplitFormatString(const AFormatString: String; out APositivePart, ANegativePart, AZeroPart: String); @@ -1068,35 +1006,14 @@ begin end; end; -{ Formats the number AValue in "scientific" format with the given number of - decimals. "Scientific" is the same as "exponential", but with exponents rounded - to multiples of 3 (like for "kilo" - "Mega" - "Giga" etc.). } -function SciFloat(AValue: Double; ADecimals: Byte; - AFormatSettings: TFormatSettings): String; -var - m: Double; - ex: Integer; -begin - if AValue = 0 then - Result := Format('%0.*fE+0', [ADecimals, 0.0], AFormatSettings) - // Excel shows "000.0E+0", but I think the "0.0E+0" shown here is better. - else begin - ex := floor(log10(abs(AValue))); // exponent - // round exponent to multiples of 3 - ex := (ex div 3) * 3; - if ex < 0 then dec(ex, 3); - m := AValue * Power(10, -ex); // mantisse - Result := Format('%.*fE+%d', [ADecimals, m, ex], AFormatSettings); - end; -end; +{@@ + Creates a "time interval" format string having the first time code identifier + in square brackets. -function SciFloat(AValue: Double; ADecimals: Byte): String; -begin - Result := SciFloat(AValue, ADecimals, DefaultFormatSettings); -end; - -{ Creates a "time interval" format string having the first code identifier - in square brackets. } + @param Src Source format string, must be a time format string, like 'hh:nn' + @param Dest Destination format string, will have the first time code element + of the src format string in square brackets, like '[hh]:nn'. +} procedure MakeTimeIntervalMask(Src: String; var Dest: String); var L: TStrings; diff --git a/components/fpspreadsheet/tests/formattests.pas b/components/fpspreadsheet/tests/formattests.pas index b740c149b..6c97eea5d 100644 --- a/components/fpspreadsheet/tests/formattests.pas +++ b/components/fpspreadsheet/tests/formattests.pas @@ -18,10 +18,10 @@ uses var // Norm to test against - list of strings that should occur in spreadsheet - SollNumberStrings: array[0..6, 0..8] of string; + SollNumberStrings: array[0..6, 0..7] of string; SollNumbers: array[0..6] of Double; - SollNumberFormats: array[0..8] of TsNumberFormat; - SollNumberDecimals: array[0..8] of word; + SollNumberFormats: array[0..7] of TsNumberFormat; + SollNumberDecimals: array[0..7] of word; SollDateTimeStrings: array[0..4, 0..9] of string; SollDateTimes: array[0..4] of TDateTime; @@ -164,7 +164,6 @@ begin SollNumberFormats[5] := nfExp; SollNumberDecimals[5] := 2; SollNumberFormats[6] := nfPercentage; SollNumberDecimals[6] := 0; SollNumberFormats[7] := nfPercentage; SollNumberDecimals[7] := 2; - SollNumberFormats[8] := nfSci; SollNumberDecimals[8] := 1; for i:=Low(SollNumbers) to High(SollNumbers) do begin SollNumberStrings[i, 0] := FloatToStr(SollNumbers[i], fs); @@ -175,7 +174,6 @@ begin SollNumberStrings[i, 5] := FormatFloat('0.00E+00', SollNumbers[i], fs); SollNumberStrings[i, 6] := FormatFloat('0', SollNumbers[i]*100, fs) + '%'; SollNumberStrings[i, 7] := FormatFloat('0.00', SollNumbers[i]*100, fs) + '%'; - SollNumberStrings[i, 8] := SciFloat(SollNumbers[i], 1, fs); end; // Date/time values @@ -288,8 +286,6 @@ begin for Col := ord(Low(SollNumberFormats)) to ord(High(SollNumberFormats)) do begin MyWorksheet.WriteNumber(Row, Col, SollNumbers[Row], SollNumberFormats[Col], SollNumberDecimals[Col]); ActualString := MyWorksheet.ReadAsUTF8Text(Row, Col); - if (AFormat=sfExcel2) and (SollNumberFormats[Col] = nfSci) then - Continue; // BIFF2 does not support nfSci -> ignore CheckEquals(SollNumberStrings[Row, Col], ActualString, 'Test unsaved string mismatch cell ' + CellNotation(MyWorksheet,Row,Col)); end; MyWorkBook.WriteToFile(TempFile, AFormat, true); @@ -306,8 +302,6 @@ begin fail('Error in test code. Failed to get named worksheet'); for Row := Low(SollNumbers) to High(SollNumbers) do for Col := Low(SollNumberFormats) to High(SollNumberFormats) do begin - if (AFormat=sfExcel2) and (SollNumberFormats[Col] = nfSci) then - Continue; // BIFF2 does not support nfSci --> ignore ActualString := MyWorkSheet.ReadAsUTF8Text(Row,Col); CheckEquals(SollNumberStrings[Row,Col],ActualString,'Test saved string mismatch cell '+CellNotation(MyWorkSheet,Row,Col)); end; diff --git a/components/fpspreadsheet/tests/numberstests.pas b/components/fpspreadsheet/tests/numberstests.pas index ea7192c8b..479cc9684 100644 --- a/components/fpspreadsheet/tests/numberstests.pas +++ b/components/fpspreadsheet/tests/numberstests.pas @@ -22,7 +22,7 @@ uses var // Norm to test against - list of numbers/times that should occur in spreadsheet - SollNumbers: array[0..23] of double; //"Soll" is a German word in Dutch accountancy jargon meaning "normative value to check against". There ;) + SollNumbers: array[0..22] of double; //"Soll" is a German word in Dutch accountancy jargon meaning "normative value to check against". There ;) // Initializes Soll*/normative variables. // Useful in test setup procedures to make sure the norm is correct. procedure InitSollNumbers; @@ -64,7 +64,6 @@ type procedure TestReadNumber20; procedure TestReadNumber21; procedure TestReadNumber22; - procedure TestReadNumber23; procedure TestReadODFNumber0; //number tests using ODF/LibreOffice file format procedure TestReadODFNumber1; //number and time procedure TestReadODFNumber2; @@ -88,7 +87,6 @@ type procedure TestReadODFNumber20; procedure TestReadODFNumber21; procedure TestReadODFNumber22; - procedure TestReadODFNumber23; end; { TSpreadWriteReadNumberTests } @@ -142,13 +140,12 @@ begin SollNumbers[14]:=0.3536; // 0.3536 formatted as percentage, 2 decimals SollNumbers[15]:=59000000.1234; // 59 million + 0.1234 formatted with thousand separator, no decimals SollNumbers[16]:=59000000.1234; // 59 million + 0.1234 formatted with thousand separator, 2 decimals - SollNumbers[17]:=-59000000.1234; // minus 59 million + 0.1234, formatted as "scientific" with 1 decimal - SollNumbers[18]:=-59000000.1234; // minus 59 million + 0.1234, formatted as "exp" with 2 decimals - SollNumbers[19]:=59000000.1234; // 59 million + 0.1234 formatted as currrency (EUROs, at end), 2 decimals - SollNumbers[20]:=59000000.1234; // 59 million + 0.1234 formatted as currrency (Dollars, at end), 2 decimals - SollNumbers[21]:=-59000000.1234; // minus 59 million + 0.1234 formatted as currrency (EUROs, at end), 2 decimals - SollNumbers[22]:=-59000000.1234; // minus 59 million + 0.1234 formatted as currrency (Dollars, at end), 2 decimals - SollNumbers[23]:=-59000000.1234; // minus 59 million + 0.1234 formatted as currrency (Dollars, at end, neg red), 2 decimals + SollNumbers[17]:=-59000000.1234; // minus 59 million + 0.1234, formatted as "exp" with 2 decimals + SollNumbers[18]:=59000000.1234; // 59 million + 0.1234 formatted as currrency (EUROs, at end), 2 decimals + SollNumbers[19]:=59000000.1234; // 59 million + 0.1234 formatted as currrency (Dollars, at end), 2 decimals + SollNumbers[20]:=-59000000.1234; // minus 59 million + 0.1234 formatted as currrency (EUROs, at end), 2 decimals + SollNumbers[21]:=-59000000.1234; // minus 59 million + 0.1234 formatted as currrency (Dollars, at end), 2 decimals + SollNumbers[22]:=-59000000.1234; // minus 59 million + 0.1234 formatted as currrency (Dollars, at end, neg red), 2 decimals end; { TSpreadWriteReadNumberTests } @@ -395,11 +392,6 @@ begin TestReadNumber(ExtractFilePath(ParamStr(0)) + TestFileBIFF8,22); end; -procedure TSpreadReadNumberTests.TestReadNumber23; -begin - TestReadNumber(ExtractFilePath(ParamStr(0)) + TestFileBIFF8,23); -end; - procedure TSpreadReadNumberTests.TestReadODFNumber0; begin TestReadNumber(ExtractFilePath(ParamStr(0)) + TestFileODF,0); @@ -515,11 +507,6 @@ begin TestReadNumber(ExtractFilePath(ParamStr(0)) + TestFileODF,22); end; -procedure TSpreadReadNumberTests.TestReadODFNumber23; -begin - TestReadNumber(ExtractFilePath(ParamStr(0)) + TestFileODF,23); -end; - initialization // Register so these tests are included in a full run diff --git a/components/fpspreadsheet/tests/spreadtestgui.lpi b/components/fpspreadsheet/tests/spreadtestgui.lpi index 597f7735e..a519b4425 100644 --- a/components/fpspreadsheet/tests/spreadtestgui.lpi +++ b/components/fpspreadsheet/tests/spreadtestgui.lpi @@ -103,6 +103,7 @@ + diff --git a/components/fpspreadsheet/tests/stringtests.pas b/components/fpspreadsheet/tests/stringtests.pas index 56efbf37e..9a6660e4d 100644 --- a/components/fpspreadsheet/tests/stringtests.pas +++ b/components/fpspreadsheet/tests/stringtests.pas @@ -30,7 +30,7 @@ uses var // Norm to test against - list of strings that should occur in spreadsheet - SollStrings: array[0..14] of string; //"Soll" is a German word in Dutch accountancy jargon meaning "normative value to check against". There ;) + SollStrings: array[0..13] of string; //"Soll" is a German word in Dutch accountancy jargon meaning "normative value to check against". There ;) // Initializes Soll*/normative variables. // Useful in test setup procedures to make sure the norm is correct. procedure InitSollStrings; @@ -63,7 +63,6 @@ type procedure TestReadString11; procedure TestReadString12; procedure TestReadString13; - procedure TestReadString14; procedure TestReadODFString0; //OpenDocument/LibreOffice format empty string procedure TestReadODFString1; procedure TestReadODFString2; @@ -78,7 +77,6 @@ type procedure TestReadODFString11; procedure TestReadODFString12; procedure TestReadODFString13; - procedure TestReadODFString14; end; { TSpreadWriteReadStringTests } @@ -124,10 +122,9 @@ begin SollStrings[8]:=FormatFloat('0.00', 35.36)+'%'; // 0.3536 formatted as percentage, 2 decimals SollStrings[9]:=FormatFloat('#,##0', 59000000.1234); // 59 million + 0.1234 formatted with thousand separator, no decimals SollStrings[10]:=FormatFloat('#,##0.00', 59000000.1234); // 59 million + 0.1234 formatted with thousand separator, 2 decimals - SollStrings[11]:=SciFloat(-59000000.1234, 1); // minus 59 million + 0.1234, formatted as "scientific" with 1 decimal - SollStrings[12]:=FormatFloat('0.00E+00', -59000000.1234); // minus 59 million + 0.1234, formatted as "exp" with 2 decimals - SollStrings[13]:=FormatFloat('#,##0.00 "EUR";(#,##0.00 "EUR")', 59000000.1234); // 59 million + 0.1234, formatted as "currencyRed" with 2 decimals, brackets and EUR - SollStrings[14]:=FormatFloat('#,##0.00 "EUR";(#,##0.00 "EUR")', -59000000.1234); // minus 59 million + 0.1234, formatted as "currencyRed" with 2 decimals, brackets and EUR + SollStrings[11]:=FormatFloat('0.00E+00', -59000000.1234); // minus 59 million + 0.1234, formatted as "exp" with 2 decimals + SollStrings[12]:=FormatFloat('#,##0.00 "EUR";(#,##0.00 "EUR")', 59000000.1234); // 59 million + 0.1234, formatted as "currencyRed" with 2 decimals, brackets and EUR + SollStrings[13]:=FormatFloat('#,##0.00 "EUR";(#,##0.00 "EUR")', -59000000.1234); // minus 59 million + 0.1234, formatted as "currencyRed" with 2 decimals, brackets and EUR end; { TSpreadWriteReadStringTests } @@ -415,11 +412,6 @@ begin TestReadString(ExtractFilePath(ParamStr(0)) + TestFileBIFF8,13); end; -procedure TSpreadReadStringTests.TestReadString14; -begin - TestReadString(ExtractFilePath(ParamStr(0)) + TestFileBIFF8,14); -end; - procedure TSpreadReadStringTests.TestReadODFString0; begin TestReadString(ExtractFilePath(ParamStr(0)) + TestFileODF,0); @@ -490,10 +482,6 @@ begin TestReadString(ExtractFilePath(ParamStr(0)) + TestFileODF,13); end; -procedure TSpreadReadStringTests.TestReadODFString14; -begin - TestReadString(ExtractFilePath(ParamStr(0)) + TestFileODF,14); -end; initialization // Register so these tests are included in a full run diff --git a/components/fpspreadsheet/tests/testbiff8_1899.xls b/components/fpspreadsheet/tests/testbiff8_1899.xls index 361c4d2432832ed52d50e05773faa56b9ccc4e0b..c73994677d980620dfa2e2590fea93a5b2671663 100644 GIT binary patch delta 2254 zcmY+FZA@Eb6vxkPF9iye7A_QM3#C8>iVlXw#lWCLl=9jRhAfkqjIu024deY~V};FS zF5ANLEHTD7aYn-ulacg`f{7#U!{Rc7QQRk&>0Dflaf>F)mT2%i_n!Nhd;8&}=bYdF zocsLmZR1O(_>yV$4cW4KMOj!o&qn1_QcB{f{K%Xne;8jopZGK7u_Qs_zV)d+@z`}% zvb+k(;jceUoc5e4^dET<@JfcRi8l`pjgGwy8PL$qQ%TS}G(D~+w)V4K_0i-*$%ipt zn^>y9XObG&o#sH|eq+BJoZAVIjulo5P$~gD!T1iwrRM>PO#o{_fOa#$Cv^aIGQjt{ z0Ag(PrDIknQa`l-yx*3%6^o>?aKtM)6K{<~CAJ!0$U7`WLK_1rlUJ6+6=#u!_nC`>MAq^)~E;Q#kH(R=4k2RvX)4!)bWoX6nU8 zVl9e7UjTSD+6U0GpWTXGO1#%MYn5!cOR>bl2k)qBR1Ruuv<(h-_ZikmSJ$XP3A4$D zM+hM58+^?SwT|Zmx4{cv;-Scr8{ANdV|jG5{(befG_3Re((nV@xL=R8WeDKn1iw7RU;Z;X4XF6+bk2UR|a)hy<+k@*bfo2XD1a^b_GO)keCHZT#Tw1v`} z2QYnYqLI?cy5eh?c1$+XpcGvM(|plX8f0N7CaVZdrJe{!Hzu12wNg4xXd0qE)-dHJ zEKRT9MQnJgldyDMjcy(A)+^YPDW#?e4|m?gjRR-}*jj0lOcvEf?NBpxK_b&t^3b$W z<0XW4l5xGiahD!c`WHFK4*)~CD3Nn+(yF}Vla?gws@DXXph%YZ~YZs)Q zNc*03wlLEiD4~7_#tzZ%5d9A7Z^HhE*k4NhSr|J-KY#bMd&;8z5$qqBZ4Q)Ce>TRb z=+74Y+0?&`{WoWuB|poY^N>{v)mFGwyXQjH+zS3y};79-7o%hf?<)*Gp>T+RMIr|%D=tg>Q zMl003{=(XC+YcMLksdL_Lp0pewsfm>?6?y2(uRc?!v?xg(1k>o6YWvyEu?j8`3`Lc zx=7GPL`R4&ROy%FisWMl;x{YW4V+hSUgF}!6{+0R!vD_l=^X~TSkT2puMq83=?4o+ zweBi^!a5DKPtZQ1H<4yli=k@WNd8cD8K4pYl@J8KBJ{yx=(r!7x%+%#H(d2!$~h-r zSDh(UA^sOuszT3ThxSdzf1nED_|R_n@p|I&h4^18S0=bJl`AK%40P@`*x@t$BaFcj QHgMu&7JucN%O~ak04*EQ3;+NC delta 2427 zcmY+FU2Icj7{{M;+O?(I+HN~tyVY)O*Nu&hWYtxPBBOLD`|4Kq;ev{6W5fx=vF{DG z6$K3f5qxh+Isf19 zyzl#*)22>|sZ-*`eP&Dlpj2x+&IZhH3D@Km^Rg-Jz_D$%SUHqW)ZoW zNS8vkj!ThZBD1k3bdtZ%>T0uz>?blyAwM6N%1emM!Ma^1b96F?$a7m56DH!(wV30t zrGzyGWyx>PeX;e|P5kU7oso!{!x8ec$x(-&&1NQ~Xn+K6thEM#TNk)Vus{My6_iBz z6#3nAvDO*jTpi9O7;dU}cWZZl^Pm*vW7WRP>J0+^U8WwEhXkc0$khb@pny-83;6eL zgF)cc1zr*~lfa`1dJaiZK4dP)!`f&NGt*@MYgWq|B{ zthEq%<*CbeSO2W$4~Sp4`w42wj%a9gF(_KL)Y)Qmw;J{JG!{STihMm1j?<{#LkRohw$jGUAHyr%2_l Tg9|Q*0K4G;V{e?FJ!1YJb}ax~ diff --git a/components/fpspreadsheet/tests/testbiff8_1904.xls b/components/fpspreadsheet/tests/testbiff8_1904.xls index 6850c06378116c48344c0b00c6cd183aee329099..3e01305a8d52a444daf4f26186445a0ef9a43dde 100644 GIT binary patch delta 1829 zcmZvddrVt(6vxl6EtHG26u9M4ptL|sSvMyr!;n3om6-4-Z84iulK~yhWg4+io*k>N zWifLBkI`frUCf9$6F0nnOcz4L5H<0I8eRNjN@57cxEQxT+@_TA}D zF~hUEixbK%!*7g%vYu@zQcSi-SoS{n=+!rw@?`nPcK6Iql7kTfu_p!wqr=aG2@Ksl zl?4wBj*qB`*B{1xEg|Eh#>Xgsw{pJa7aiM$p|&lGe)oPu)9HHv@~CpL380<bQxfR%dzb{GJzwE!H%S>I8qgj8qm2N+OF{AG8r5~U&VJyUGKR~XxZ z3!y6J#cx7Kti4eC;XmN6KG}8~a-k3yGXN7i*514cF4gnk(1!$VUQvZ-*i2P*IUpN%O z^`R5`pm7O@o|=^s#wC3+nF9>_qUGp{HtH`><)LW1l-P)<6+N=y8vH!EO+UX8!M~$> zCC}AVC5tMRI5zCm2d_r(wc-1ugs2#)Qh|n%yQF!M$x$==N8Hk?$Vika#$-GX&y6_r zp2!@2Inrtk1AKeBH5%`lRSU{Okv_U&LyMpwJ2g9Bon0^{ z*VPEVfMOuS7YM#JC&gP-J~k#hYXx6Ou_wb93Vt|4nA*KW z?cO>mo4D6pQ${g>{gYP7BbZW^d3#cJaep~uqu8VAYfC@h;2f?^`b^woPL@*);`XWH zoEwX(SvIve4Xz+03=IdGFKs0M*uxF35Y2+u7xBF*pEqr5C6OH(vNHWRH&fj! zs5}2M6rw*ayV54xN!O{-*@bTP#v;b!KCgoazAm5Cr6H@*yNC&PF~QBM(S7vivThCS z5VYs!BHE{Y-n6NFlYoYFrb*$8BeS_O{h!hNAbT{rYN4B_3BOPK?o1oaCj~WRjUW?Z z0x37q9(Uo>$K=Cl^ZAK};Jo_{>x+h!J^TljmwCG|hC`zfKXXEto%rMh`m<9`Jr~XT7aAYPCjbBd delta 1947 zcmY+EZERCz6vxlgTe`KQ-6*%+M)%T=buX07jSbYv*xHSVY^?37u*4;MbHKRFZ0+7( zD{6wGWN=IrKj>m2naL7fZek*SVTMFaj7&q|8x)Aq7@|b|aMn*d-V9*^^13o_}i^ZJv<>y3uG%riHc7svIl z>Ry>q|J47?7*l^RfQVZGjxGvXQNj=;`pMd?3cZy0vXUTKHNVTj=TmZ?*%=k7m^h(%AFmRlFkCTw8Y_LT5HJF8fqzj2kPM(4fG8v zomE)fS8L%kP(7FTF&ysOkJ~~fV>TsMy8v#fRiXSWmQ5q>W_Y|OMsq#a%jX*ERTk6d z+MJk+I{6r8ibs-Tc=_CBt-edtE&s1;V_LL&9p&#p2|VULvH~c89blnweR!d> zI*YHvAHWP2JkfX9bo&_X$s|C08mmJiYD52BvjmUq=omRIU2k5)izDZx#}pezLz2_G zhD)RSjnJh?@pvXkxD+nL&+)Bc-b9P5R8U(e`cu>vK{XRKN0eJoR*HcXWfjy2 zQL%}Z@^V3Kr5H?6TLrc3OH63hCX7tV?lR)|yCzc%L1pmU<*)Ytw2%8Ji=LGp1y?71 z9;YB}6x-5Bn;_T4Hpv*Awc`_0WghM-SGQAaPwTOZ9*8Bj)+TzaxH2WXxzEWD@laY{ zuIQ_XDR_U%XXXxblAj74STk)i283d6N^xRZc5{z8mrv23rkF1jqojCg+GplIb8;KS z0B%g%Gw&xx%ufln&d6?VG{+nigK2^eA-Hm1!TA}VCuwm3ks+w9~Y;DLf|v%ZwYMMSozkwt>6c#xRCP*ac1%I2iK#nf{M z*_=I7AVnJT?3~QDWB1&>;*K;yrx1(^x>%!6&Hv{v7xt(1xJ1tijr;k$&y#eQzZrov zvP6*ULfxgQ2Nq;=(rUifK^$GMN$^m?Hx_)0No)D;cEY8y4{UGiZ#40fvQ$&6pnT#^ zPiI0U-SI8sGjzi38|p{rqQ7ahTcdXf+D#`*f@V4c@YOr%Jhj4EI!~wIn7FE;8V@dh Zs$Op0!>&e86M1EJSLPY@?5Xgke*x1g%ZmU2 diff --git a/components/fpspreadsheet/tests/testodf_1899.ods b/components/fpspreadsheet/tests/testodf_1899.ods index adbbb8a41333f092ce67534148289089a12c488d..15f28cf6ba9109debb98724a8ddf5cf614e148c3 100644 GIT binary patch delta 13170 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 Ad;kCd delta 14335 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