From 2a61d87225ab1e7dc09668fd6aebda6e0c7b2078 Mon Sep 17 00:00:00 2001 From: wp_xxyyzz Date: Sun, 19 Oct 2014 21:20:57 +0000 Subject: [PATCH] fpspreadsheet: Add detection of currency values to the csv reader. Spready: add configuration dialogs for csv parameters and formatsettings. git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@3665 8e941d3f-bd1b-0410-a28a-d453659cc2b4 --- .../examples/spready/mainform.lfm | 29 + .../examples/spready/mainform.pas | 49 +- .../examples/spready/scsvparamsform.lfm | 533 ++++++++++++++++ .../examples/spready/scsvparamsform.pas | 593 ++++++++++++++++++ .../fpspreadsheet/examples/spready/sctrls.pas | 264 ++++++++ .../examples/spready/sformatsettingsform.lfm | 378 +++++++++++ .../examples/spready/sformatsettingsform.pas | 443 +++++++++++++ .../examples/spready/spready.lpi | 33 +- .../examples/spready/spready.lpr | 4 +- components/fpspreadsheet/fpscsv.pas | 76 ++- .../fpspreadsheet/fpspreadsheetgrid.pas | 31 +- components/fpspreadsheet/fpsstrings.pas | 2 +- components/fpspreadsheet/fpsutils.pas | 268 +++++++- 13 files changed, 2670 insertions(+), 33 deletions(-) create mode 100644 components/fpspreadsheet/examples/spready/scsvparamsform.lfm create mode 100644 components/fpspreadsheet/examples/spready/scsvparamsform.pas create mode 100644 components/fpspreadsheet/examples/spready/sctrls.pas create mode 100644 components/fpspreadsheet/examples/spready/sformatsettingsform.lfm create mode 100644 components/fpspreadsheet/examples/spready/sformatsettingsform.pas diff --git a/components/fpspreadsheet/examples/spready/mainform.lfm b/components/fpspreadsheet/examples/spready/mainform.lfm index 7e6d8e979..ec2cad9ca 100644 --- a/components/fpspreadsheet/examples/spready/mainform.lfm +++ b/components/fpspreadsheet/examples/spready/mainform.lfm @@ -596,6 +596,9 @@ object MainFrm: TMainFrm 0036000000360000002000000002FFFFFF00FFFFFF00FFFFFF00 } end + object MenuItem76: TMenuItem + Caption = '-' + end object mnuOpen: TMenuItem Action = AcOpen Bitmap.Data = { @@ -927,6 +930,12 @@ object MainFrm: TMainFrm end object mnuFormat: TMenuItem Caption = 'Format' + object MenuItem75: TMenuItem + Action = AcFormatSettings + end + object MenuItem77: TMenuItem + Caption = '-' + end object MnuFOnt: TMenuItem Action = AcFont Bitmap.Data = { @@ -1399,6 +1408,12 @@ object MainFrm: TMainFrm AutoCheck = True end end + object MnuSettings: TMenuItem + Caption = 'Settings' + object MnuCSVParams: TMenuItem + Action = AcCSVParams + end + end end object ImageList: TImageList left = 272 @@ -3174,6 +3189,20 @@ object MainFrm: TMainFrm ImageIndex = 37 OnExecute = AcDeleteRowExecute end + object AcCSVParams: TAction + Category = 'Settings' + Caption = 'CSV parameters...' + OnExecute = AcCSVParamsExecute + end + object AcFormatSettings: TAction + Category = 'File' + Caption = 'Number format settings...' + Hint = 'Modify the global settings for number and date/time formatting' + OnExecute = AcFormatSettingsExecute + end + object Action1: TAction + Caption = 'Action1' + end end object FontDialog: TFontDialog MinFontSize = 0 diff --git a/components/fpspreadsheet/examples/spready/mainform.pas b/components/fpspreadsheet/examples/spready/mainform.pas index 8bf78a752..186414928 100644 --- a/components/fpspreadsheet/examples/spready/mainform.pas +++ b/components/fpspreadsheet/examples/spready/mainform.pas @@ -77,6 +77,9 @@ type AcShowGridlines: TAction; AcDeleteColumn: TAction; AcDeleteRow: TAction; + AcCSVParams: TAction; + AcFormatSettings: TAction; + Action1: TAction; AcViewInspector: TAction; AcWordwrap: TAction; AcVAlignDefault: TAction; @@ -166,6 +169,11 @@ type MenuItem72: TMenuItem; MenuItem73: TMenuItem; MenuItem74: TMenuItem; + MenuItem75: TMenuItem; + MenuItem76: TMenuItem; + MenuItem77: TMenuItem; + MnuCSVParams: TMenuItem; + MnuSettings: TMenuItem; mnuInspector: TMenuItem; mnuView: TMenuItem; MnuFmtDateTimeMSZ: TMenuItem; @@ -267,11 +275,13 @@ type procedure AcAddRowExecute(Sender: TObject); procedure AcBorderExecute(Sender: TObject); procedure AcCopyFormatExecute(Sender: TObject); + procedure AcCSVParamsExecute(Sender: TObject); procedure AcDeleteColumnExecute(Sender: TObject); procedure AcDeleteRowExecute(Sender: TObject); procedure AcEditExecute(Sender: TObject); procedure AcFontExecute(Sender: TObject); procedure AcFontStyleExecute(Sender: TObject); + procedure AcFormatSettingsExecute(Sender: TObject); procedure AcHorAlignmentExecute(Sender: TObject); procedure AcIncDecDecimalsExecute(Sender: TObject); procedure AcMergeCellsExecute(Sender: TObject); @@ -334,7 +344,8 @@ implementation uses TypInfo, LCLIntf, LCLType, - fpcanvas, fpsutils; + fpcanvas, fpsutils, fpscsv, + sFormatSettingsForm, sCSVParamsForm; const DROPDOWN_COUNT = 24; @@ -489,6 +500,20 @@ begin end; end; +procedure TMainFrm.AcCSVParamsExecute(Sender: TObject); +var + F: TCSVParamsForm; +begin + F := TCSVParamsForm.Create(nil); + try + F.SetParams(fpscsv.CSVParams); + if F.ShowModal = mrOK then + F.GetParams(fpscsv.CSVParams); + finally + F.Free; + end; +end; + procedure TMainFrm.AcDeleteColumnExecute(Sender: TObject); var c: Integer; @@ -535,6 +560,26 @@ begin end; end; +procedure TMainFrm.AcFormatSettingsExecute(Sender: TObject); +var + F: TFormatSettingsForm; +begin + if WorksheetGrid.Workbook = nil then + exit; + + F := TFormatSettingsForm.Create(nil); + try + F.FormatSettings := WorksheetGrid.Workbook.FormatSettings; + if F.ShowModal = mrOK then + begin + WorksheetGrid.Workbook.FormatSettings := F.FormatSettings; + WorksheetGrid.Invalidate; + end; + finally + F.Free; + end; +end; + procedure TMainFrm.AcHorAlignmentExecute(Sender: TObject); var hor_align: TsHorAlignment; @@ -848,6 +893,8 @@ begin CbBackgroundColor.ItemHeight := FontCombobox.ItemHeight; CbBackgroundColor.ColorRectWidth := CbBackgroundColor.ItemHeight - 6; // to get a square box... + InspectorPageControl.ActivePageIndex := 0; + // Populate font combobox FontCombobox.Items.Assign(Screen.Fonts); diff --git a/components/fpspreadsheet/examples/spready/scsvparamsform.lfm b/components/fpspreadsheet/examples/spready/scsvparamsform.lfm new file mode 100644 index 000000000..a1285661f --- /dev/null +++ b/components/fpspreadsheet/examples/spready/scsvparamsform.lfm @@ -0,0 +1,533 @@ +object CSVParamsForm: TCSVParamsForm + Left = 638 + Height = 528 + Top = 250 + Width = 470 + BorderStyle = bsDialog + Caption = 'Parameters for comma-delimited files' + ClientHeight = 528 + ClientWidth = 470 + OnCloseQuery = FormCloseQuery + OnCreate = FormCreate + Position = poMainFormCenter + LCLVersion = '1.3' + object ButtonPanel: TButtonPanel + Left = 6 + Height = 34 + Top = 488 + Width = 458 + OKButton.Name = 'OKButton' + OKButton.DefaultCaption = True + HelpButton.Name = 'HelpButton' + HelpButton.DefaultCaption = True + CloseButton.Name = 'CloseButton' + CloseButton.DefaultCaption = True + CancelButton.Name = 'CancelButton' + CancelButton.DefaultCaption = True + TabOrder = 0 + ShowButtons = [pbOK, pbCancel] + end + object PageControl: TPageControl + Left = 8 + Height = 472 + Top = 8 + Width = 454 + ActivePage = PgNumberParams + Align = alClient + BorderSpacing.Around = 8 + TabIndex = 1 + TabOrder = 1 + object PgGeneralParams: TTabSheet + Caption = 'General' + ClientHeight = 454 + ClientWidth = 446 + object LblQuoteChar: TLabel + Left = 16 + Height = 15 + Top = 84 + Width = 88 + Caption = 'Quote character:' + FocusControl = CbQuoteChar + ParentColor = False + end + object CbQuoteChar: TComboBox + Left = 156 + Height = 23 + Top = 80 + Width = 155 + ItemHeight = 15 + ItemIndex = 0 + Items.Strings = ( + 'none' + 'double ( " )' + 'single ( '' )' + ) + Style = csDropDownList + TabOrder = 2 + Text = 'none' + end + object CbDelimiter: TComboBox + Left = 156 + Height = 23 + Top = 16 + Width = 155 + ItemHeight = 15 + ItemIndex = 4 + Items.Strings = ( + 'Comma ( , )' + 'Semicolon ( ; )' + 'Colon ( : )' + 'Bar ( | )' + 'TAB' + ) + Style = csDropDownList + TabOrder = 0 + Text = 'TAB' + end + object Label3: TLabel + Left = 16 + Height = 15 + Top = 19 + Width = 96 + Caption = 'Column delimiter:' + FocusControl = CbDelimiter + ParentColor = False + end + object Label4: TLabel + Left = 16 + Height = 15 + Top = 51 + Width = 65 + Caption = 'Line ending:' + FocusControl = CbLineEnding + ParentColor = False + end + object CbLineEnding: TComboBox + Left = 156 + Height = 23 + Top = 48 + Width = 155 + ItemHeight = 15 + ItemIndex = 0 + Items.Strings = ( + 'System' + 'CR+LF (Windows)' + 'CR (Unix/Linux)' + 'LF (Mac)' + ) + Style = csDropDownList + TabOrder = 1 + Text = 'System' + end + object RgDetectContentType: TRadioGroup + Left = 19 + Height = 80 + Top = 128 + Width = 292 + AutoFill = True + Caption = 'Conversion of strings after reading' + ChildSizing.LeftRightSpacing = 6 + ChildSizing.EnlargeHorizontal = crsHomogenousChildResize + ChildSizing.EnlargeVertical = crsHomogenousChildResize + ChildSizing.ShrinkHorizontal = crsScaleChilds + ChildSizing.ShrinkVertical = crsScaleChilds + ChildSizing.Layout = cclLeftToRightThenTopToBottom + ChildSizing.ControlsPerLine = 1 + ClientHeight = 60 + ClientWidth = 288 + ItemIndex = 1 + Items.Strings = ( + 'Do not convert, strings are sufficient' + 'Try to convert strings to content types' + ) + TabOrder = 3 + end + end + object PgNumberParams: TTabSheet + Caption = 'Number cells' + ClientHeight = 444 + ClientWidth = 446 + object CbAutoDetectNumberFormat: TCheckBox + Left = 16 + Height = 19 + Top = 16 + Width = 200 + Caption = 'Try to auto-detect number format' + Checked = True + State = cbChecked + TabOrder = 0 + end + object EdNumFormat: TEdit + Left = 232 + Height = 23 + Top = 140 + Width = 194 + TabOrder = 3 + end + object LblNumFormat: TLabel + Left = 17 + Height = 15 + Top = 144 + Width = 182 + Caption = 'Format string for writing numbers:' + FocusControl = EdNumFormat + ParentColor = False + end + object LblNumFormatInfo: TLabel + Left = 232 + Height = 80 + Top = 176 + Width = 194 + AutoSize = False + BorderSpacing.Left = 8 + BorderSpacing.Right = 8 + BorderSpacing.Around = 8 + Caption = 'If empty, numbers are written in the same format as they appear in the worksheet.' + FocusControl = EdNumFormat + ParentColor = False + WordWrap = True + end + object LblDecimalSeparator: TLabel + Left = 16 + Height = 15 + Top = 59 + Width = 98 + Caption = 'Decimal separator:' + FocusControl = CbDecimalSeparator + ParentColor = False + end + object CbDecimalSeparator: TComboBox + Left = 232 + Height = 23 + Top = 56 + Width = 194 + ItemHeight = 15 + ItemIndex = 0 + Items.Strings = ( + 'like spreadsheet' + 'Dot ( . )' + 'Comma ( , )' + ) + TabOrder = 1 + Text = 'like spreadsheet' + end + object LblThousandSeparator: TLabel + Left = 16 + Height = 15 + Top = 91 + Width = 108 + Caption = 'Thousand separator:' + FocusControl = CbThousandSeparator + ParentColor = False + end + object CbThousandSeparator: TComboBox + Left = 232 + Height = 23 + Top = 88 + Width = 194 + ItemHeight = 15 + ItemIndex = 0 + Items.Strings = ( + 'like spreadsheet' + 'Dot ( . )' + 'Comma ( , )' + 'Space ( )' + ) + TabOrder = 2 + Text = 'like spreadsheet' + end + end + object PgCurrency: TTabSheet + Caption = 'Currency cells' + ClientHeight = 444 + ClientWidth = 446 + object LblCurrencySymbol: TLabel + Left = 16 + Height = 15 + Top = 20 + Width = 93 + Caption = 'Currency symbol:' + FocusControl = EdCurrencySymbol + ParentColor = False + end + object EdCurrencySymbol: TEdit + Left = 232 + Height = 23 + Top = 16 + Width = 194 + OnEnter = DateTimeFormatChange + TabOrder = 0 + Text = 'like spreadsheet' + end + end + object PgDateTimeParams: TTabSheet + Caption = 'Date/time cells' + ClientHeight = 444 + ClientWidth = 446 + object LblNumFormat1: TLabel + Left = 16 + Height = 15 + Top = 20 + Width = 128 + Caption = 'Long date format string:' + ParentColor = False + end + object LblNumFormat2: TLabel + Left = 16 + Height = 15 + Top = 52 + Width = 129 + Caption = 'Short date format string:' + ParentColor = False + end + object LblDecimalSeparator1: TLabel + Left = 16 + Height = 15 + Top = 83 + Width = 79 + Caption = 'Date separator:' + FocusControl = CbDateSeparator + ParentColor = False + end + object CbDateSeparator: TComboBox + Left = 232 + Height = 23 + Top = 80 + Width = 194 + ItemHeight = 15 + ItemIndex = 0 + Items.Strings = ( + 'like spreadsheet' + 'Dot ( . )' + 'Dash ( - )' + 'Slash ( / )' + ) + OnChange = DateTimeFormatChange + OnEnter = DateTimeFormatChange + TabOrder = 2 + Text = 'like spreadsheet' + end + object LblNumFormat3: TLabel + Left = 16 + Height = 15 + Top = 268 + Width = 129 + Caption = 'Long time format string:' + ParentColor = False + end + object LblNumFormat4: TLabel + Left = 16 + Height = 15 + Top = 300 + Width = 130 + Caption = 'Short time format string:' + ParentColor = False + end + object LblDecimalSeparator2: TLabel + Left = 16 + Height = 15 + Top = 331 + Width = 82 + Caption = 'Time separator:' + FocusControl = CbTimeSeparator + ParentColor = False + end + object CbTimeSeparator: TComboBox + Left = 232 + Height = 23 + Top = 328 + Width = 194 + ItemHeight = 15 + ItemIndex = 0 + Items.Strings = ( + 'like spreadsheet' + 'Dot ( . )' + 'Dash ( - )' + 'Slash ( / )' + 'Colon ( : )' + ) + OnChange = DateTimeFormatChange + OnEnter = DateTimeFormatChange + TabOrder = 5 + Text = 'like spreadsheet' + end + object LblLongMonthNames: TLabel + Left = 16 + Height = 15 + Top = 116 + Width = 107 + Caption = 'Long month names:' + ParentColor = False + end + object LblShortMonthNames: TLabel + Left = 16 + Height = 15 + Top = 148 + Width = 108 + Caption = 'Short month names:' + ParentColor = False + end + object LblLongDayNames: TLabel + Left = 16 + Height = 15 + Top = 180 + Width = 90 + Caption = 'Long day names:' + ParentColor = False + end + object LblShortDayNames: TLabel + Left = 16 + Height = 15 + Top = 212 + Width = 91 + Caption = 'Short day names:' + ParentColor = False + end + object CbLongDateFormat: TComboBox + Left = 232 + Height = 23 + Top = 16 + Width = 194 + ItemHeight = 15 + ItemIndex = 0 + Items.Strings = ( + 'like spreadsheet' + 'ddd, d/mm/yyyy' + 'ddd, d/mmm/yyyy' + 'dddd, d/mm/yyyy' + 'dddd, d/mmm/yyyy' + 'd/mm/yyyy' + 'dd/mm/yyyy' + 'dddd, mm/d/yyyy' + 'dddd, mmm/d/yyyy' + 'mm/d/yyyy' + 'mm/dd/yyyy' + 'yyyy/mm/dd' + 'yyyy/mm/d' + 'yyyy/mmm/d' + 'yyyy/mmmm/d' + ) + OnChange = DateTimeFormatChange + OnEnter = DateTimeFormatChange + TabOrder = 0 + Text = 'like spreadsheet' + end + object CbShortDateFormat: TComboBox + Left = 232 + Height = 23 + Top = 48 + Width = 194 + ItemHeight = 15 + ItemIndex = 0 + Items.Strings = ( + 'like spreadsheet' + 'd/m/yy' + 'd/mm/yy' + 'd/mm/yyyy' + 'm/d/yy' + 'mm/d/yy' + 'mm/d/yyyy' + 'yy/m/d' + 'yy/mm/d' + 'yyyy/mm/d' + ) + OnChange = DateTimeFormatChange + OnEnter = DateTimeFormatChange + TabOrder = 1 + Text = 'like spreadsheet' + end + object CbLongTimeFormat: TComboBox + Left = 232 + Height = 23 + Top = 264 + Width = 194 + ItemHeight = 15 + ItemIndex = 0 + Items.Strings = ( + 'like spreadsheet' + 'h:n:s' + 'h:nn:ss' + 'hh:nn:ss' + ) + OnChange = DateTimeFormatChange + OnEnter = DateTimeFormatChange + TabOrder = 3 + Text = 'like spreadsheet' + end + object CbShortTimeFormat: TComboBox + Left = 232 + Height = 23 + Top = 296 + Width = 194 + ItemHeight = 15 + ItemIndex = 0 + Items.Strings = ( + 'like spreadsheet' + 'h:n' + 'h:nn' + 'hh:nn' + ) + OnChange = DateTimeFormatChange + OnEnter = DateTimeFormatChange + TabOrder = 4 + Text = 'like spreadsheet' + end + object GroupBox1: TGroupBox + Left = 17 + Height = 58 + Top = 366 + Width = 409 + Caption = 'Sample' + ClientHeight = 38 + ClientWidth = 405 + TabOrder = 6 + object LblDateTimeSample: TLabel + Left = 7 + Height = 20 + Top = 2 + Width = 388 + Alignment = taCenter + Anchors = [akTop, akLeft, akRight] + AutoSize = False + Caption = 'sample' + ParentColor = False + end + end + end + object PgBoolParams: TTabSheet + Caption = 'Boolean cells' + ClientHeight = 454 + ClientWidth = 446 + object EdTRUE: TEdit + Left = 16 + Height = 23 + Top = 45 + Width = 131 + TabOrder = 0 + end + object EdFALSE: TEdit + Left = 176 + Height = 23 + Top = 45 + Width = 131 + TabOrder = 1 + end + object Label1: TLabel + Left = 19 + Height = 15 + Top = 16 + Width = 81 + Caption = 'Text for "TRUE"' + ParentColor = False + end + object Label2: TLabel + Left = 179 + Height = 15 + Top = 16 + Width = 85 + Caption = 'Text for "FALSE"' + ParentColor = False + end + end + end +end diff --git a/components/fpspreadsheet/examples/spready/scsvparamsform.pas b/components/fpspreadsheet/examples/spready/scsvparamsform.pas new file mode 100644 index 000000000..d867015cf --- /dev/null +++ b/components/fpspreadsheet/examples/spready/scsvparamsform.pas @@ -0,0 +1,593 @@ +unit sCSVParamsForm; + +{$mode objfpc}{$H+} + +interface + +uses + Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs, + ButtonPanel, ExtCtrls, ComCtrls, StdCtrls, + fpsCSV, + sCtrls, sFormatsettingsForm; + +type + + { TCSVParamsForm } + + TCSVParamsForm = class(TForm) + ButtonPanel: TButtonPanel; + CbAutoDetectNumberFormat: TCheckBox; + CbLongDateFormat: TComboBox; + CbLongTimeFormat: TComboBox; + EdCurrencySymbol: TEdit; + CbShortTimeFormat: TComboBox; + CbShortDateFormat: TComboBox; + CbDecimalSeparator: TComboBox; + CbDateSeparator: TComboBox; + CbTimeSeparator: TComboBox; + CbThousandSeparator: TComboBox; + CbLineEnding: TComboBox; + CbQuoteChar: TComboBox; + CbDelimiter: TComboBox; + EdTRUE: TEdit; + EdFALSE: TEdit; + EdNumFormat: TEdit; + GroupBox1: TGroupBox; + Label1: TLabel; + Label2: TLabel; + Label3: TLabel; + Label4: TLabel; + LblDateTimeSample: TLabel; + LblDecimalSeparator: TLabel; + LblDecimalSeparator1: TLabel; + LblDecimalSeparator2: TLabel; + LblCurrencySymbol: TLabel; + LblShortMonthNames: TLabel; + LblLongDayNames: TLabel; + LblShortDayNames: TLabel; + LblNumFormat1: TLabel; + LblNumFormat2: TLabel; + LblNumFormat3: TLabel; + LblNumFormat4: TLabel; + LblLongMonthNames: TLabel; + LblThousandSeparator: TLabel; + LblNumFormat: TLabel; + LblQuoteChar: TLabel; + LblNumFormatInfo: TLabel; + PageControl: TPageControl; + PgGeneralParams: TTabSheet; + PgNumberParams: TTabSheet; + PgDateTimeParams: TTabSheet; + PgBoolParams: TTabSheet; + RgDetectContentType: TRadioGroup; + PgCurrency: TTabSheet; + procedure DateTimeFormatChange(Sender: TObject); + procedure FormCloseQuery(Sender: TObject; var CanClose: boolean); + procedure FormCreate(Sender: TObject); + private + { private declarations } + FSampleDateTime: TDateTime; + FDateFormatSample: String; + FTimeFormatSample: String; + FEdLongMonthNames: TMonthDayNamesEdit; + FEdShortMonthNames: TMonthDayNamesEdit; + FEdLongDayNames: TMonthDayNamesEdit; + FEdShortDayNames: TMonthDayNamesEdit; + procedure DateSeparatorToFormatSettings(var ASettings: TFormatSettings); + procedure DecimalSeparatorToFormatSettings(var ASettings: TFormatSettings); +// function GetCurrencySymbol: String; + procedure ThousandSeparatorToFormatSettings(var ASettings: TFormatSettings); + procedure TimeSeparatorToFormatSettings(var ASettings: TFormatSettings); + public + { public declarations } + procedure GetParams(var AParams: TsCSVParams); + procedure SetParams(const AParams: TsCSVParams); + end; + +var + CSVParamsForm: TCSVParamsForm; + +implementation + +uses + fpsUtils; + +resourcestring + rsLikeSpreadsheet = 'like spreadsheet'; + + { +const + CURR_VALUE = 100.0; + } + +var + CSVParamsPageIndex: Integer = 0; + + +{ TCSVParamsForm } + +procedure TCSVParamsForm.DateSeparatorToFormatSettings(var ASettings: TFormatSettings); +begin + case CbDateSeparator.ItemIndex of + 0: ASettings.DateSeparator := #0; + 1: ASettings.DateSeparator := '.'; + 2: ASettings.DateSeparator := '-'; + 3: ASettings.DateSeparator := '/'; + else ASettings.DateSeparator := CbDateSeparator.Text[1]; + end; +end; + +procedure TCSVParamsForm.DecimalSeparatorToFormatSettings(var ASettings: TFormatSettings); +begin + case CbDecimalSeparator.ItemIndex of + 0: ASettings.DecimalSeparator := #0; + 1: ASettings.DecimalSeparator := '.'; + 2: ASettings.DecimalSeparator := ','; + else ASettings.DecimalSeparator := CbDecimalSeparator.Text[1]; + end; +end; + +procedure TCSVParamsForm.DateTimeFormatChange(Sender: TObject); +var + fs: TFormatSettings; + ctrl: TWinControl; + dt: TDateTime; + arr: Array[1..12] of String; + i: Integer; +begin + fs := DefaultFormatSettings; + if CbLongDateFormat.ItemIndex <> 0 then + fs.LongDateFormat := CbLongDateFormat.Text; + if CbShortDateFormat.ItemIndex <> 0 then + fs.ShortDateFormat := CbShortDateFormat.Text; + if CbLongTimeFormat.ItemIndex <> 0 then + fs.LongTimeFormat := CbLongTimeFormat.Text; + if CbShortTimeFormat.ItemIndex <> 0 then + fs.ShortTimeFormat := CbShortTimeFormat.Text; + if CbDateSeparator.ItemIndex <> 0 then + DateSeparatorToFormatSettings(fs); + if CbTimeSeparator.ItemIndex <> 0 then + TimeSeparatorToFormatSettings(fs); + + if FEdLongMonthNames.Text <> rsLikeSpreadsheet then begin + arr[1] := ''; // to silence the compiler + FEdLongMonthNames.GetNames(arr); + for i:=1 to 12 do + if arr[i] <> '' then fs.LongMonthNames[i] := arr[i]; + end; + if FEdShortMonthNames.Text <> rsLikeSpreadsheet then begin + FEdShortMonthNames.GetNames(arr); + for i:=1 to 12 do + if arr[i] <> '' then fs.ShortMonthNames[i] := arr[i]; + end; + if FEdLongDayNames.Text <> rsLikeSpreadsheet then begin + FEdLongDayNames.GetNames(arr); + for i:=1 to 7 do + if arr[i] <> '' then fs.LongDayNames[i] := arr[i]; + end; + if FEdShortDayNames.Text <> rsLikeSpreadsheet then begin + FEdShortDayNames.GetNames(arr); + for i:=1 to 7 do + if arr[i] <> '' then fs.ShortDayNames[i] := arr[i]; + end; + + dt := FSampleDateTime; + ctrl := ActiveControl; + if (ctrl = CbLongDateFormat) then + begin + FDateFormatSample := fs.LongDateFormat; + LblDateTimeSample.Caption := FormatDateTime(FDateFormatSample, dt, fs); + end + else + if (ctrl = CbShortDateFormat) then + begin + FDateFormatSample := fs.ShortDateFormat; + LblDateTimeSample.Caption := FormatDateTime(FDateFormatSample, dt, fs); + end + else + if (ctrl = CbDateSeparator) then + LblDateTimeSample.Caption := FormatDateTime(FDateFormatSample, dt, fs) + else + if (ctrl = CbLongTimeFormat) then + begin + FTimeFormatSample := fs.LongTimeFormat; + LblDateTimeSample.Caption := FormatDateTime(FTimeFormatSample, dt, fs); + end + else + if (ctrl = CbShortTimeFormat) then + begin + FTimeFormatSample := fs.ShortTimeFormat; + LblDateTimeSample.Caption := FormatDateTime(FTimeFormatSample, dt, fs); + end + else + if (ctrl = CbTimeSeparator) then + LblDateTimeSample.Caption := FormatDateTime(FTimeFormatSample, dt, fs) + else + LblDateTimeSample.Caption := FormatDateTime('c', dt, fs); + + Application.ProcessMessages; +end; + +procedure TCSVParamsForm.FormCloseQuery(Sender: TObject; var CanClose: boolean); +begin + Unused(Sender, CanClose); + CSVParamsPageIndex := PageControl.ActivePageIndex; +end; + +(* +procedure TCSVParamsForm.EdCurrencySymbolChange(Sender: TObject); +var + sel: Integer; +begin + sel := CbPosCurrencyFormat.ItemIndex; + CbPosCurrencyFormat.Items.BeginUpdate; + try + CbPosCurrencyFormat.Items.Clear; + BuildCurrencyFormatList(CbPosCurrencyFormat.Items, true, CURR_VALUE, GetCurrencySymbol); + CbPosCurrencyFormat.Items.Insert(0, rsLikeSpreadsheet); + CbPosCurrencyFormat.ItemIndex := sel; + finally + CbPosCurrencyFormat.Items.EndUpdate; + end; + + sel := CbNegCurrencyFormat.ItemIndex; + CbNegCurrencyFormat.Items.BeginUpdate; + try + CbNegCurrencyFormat.Items.Clear; + BuildCurrencyFormatList(CbNegCurrencyFormat.Items, false, CURR_VALUE, GetCurrencySymbol); + CbNegCurrencyFormat.Items.Insert(0, rsLikeSpreadsheet); + CbNegCurrencyFormat.ItemIndex := sel; + finally + CbNegCurrencyFormat.Items.EndUpdate; + end; +end; +*) + +procedure TCSVParamsForm.FormCreate(Sender: TObject); +begin + PageControl.ActivePageIndex := CSVParamsPageIndex; + +// CbNegCurrencyFormat.DropdownCount := 32; + + FEdLongMonthNames := TMonthDayNamesEdit.Create(self); + with FEdLongMonthNames do + begin + Parent := PgDateTimeParams; + Left := CbDateSeparator.Left; + Top := CbDateSeparator.Top + 32; + Width := CbDateSeparator.Width; + OnChange := @DateTimeFormatChange; + OnEnter := @DateTimeFormatChange; + TabOrder := CbDateSeparator.TabOrder + 1; + end; + LblLongMonthNames.FocusControl := FEdLongMonthNames; + + FEdShortMonthNames := TMonthDayNamesEdit.Create(self); + with FEdShortMonthNames do + begin + Parent := PgDateTimeParams; + Left := CbDateSeparator.Left; + Top := CbDateSeparator.Top + 32*2; + Width := CbDateSeparator.Width; + TabOrder := CbDateSeparator.TabOrder + 2; + OnChange := @DateTimeFormatChange; + OnEnter := @DateTimeFormatChange; + end; + LblShortMonthNames.FocusControl := FEdShortMonthNames; + + FEdLongDayNames := TMonthDayNamesEdit.Create(self); + with FEdLongDayNames do + begin + Parent := PgDateTimeParams; + Left := CbDateSeparator.Left; + Top := CbDateSeparator.Top + 32*3; + Width := CbDateSeparator.Width; + TabOrder := CbDateSeparator.TabOrder + 3; + OnChange := @DateTimeFormatChange; + OnEnter := @DateTimeFormatChange; + end; + LblLongDayNames.FocusControl := FEdLongDayNames; + + FEdShortDayNames := TMonthDayNamesEdit.Create(self); + with FEdShortDayNames do + begin + Parent := PgDateTimeParams; + Left := CbDateSeparator.Left; + Top := CbDateSeparator.Top + 32*4; + Width := CbDateSeparator.Width; + TabOrder := CbDateSeparator.TabOrder + 4; + OnChange := @DateTimeFormatChange; + OnEnter := @DateTimeFormatChange; + end; + LblShortDayNames.FocusControl := FEdShortDayNames; + + FDateFormatSample := DefaultFormatSettings.LongDateFormat; + FTimeFormatSample := DefaultFormatSettings.LongTimeFormat; + FSampleDateTime := now(); +end; + { +function TCSVParamsForm.GetCurrencySymbol: String; +begin + if EdCurrencySymbol.Text = rsLikeSpreadsheet then + Result := AnsiToUTF8(DefaultFormatSettings.CurrencyString) + else + Result := EdCurrencySymbol.Text; +end; + } +procedure TCSVParamsForm.GetParams(var AParams: TsCSVParams); +begin + // Line endings + case CbLineEnding.ItemIndex of + 0: AParams.LineEnding := leSystem; + 1: AParams.LineEnding := leCRLF; + 2: AParams.LineEnding := leCR; + 3: AParams.LineEnding := leLF; + end; + + // Column delimiter + case CbDelimiter.ItemIndex of + 0: AParams.Delimiter := ','; + 1: AParams.Delimiter := ';'; + 2: AParams.Delimiter := ':'; + 3: AParams.Delimiter := '|'; + 4: AParams.Delimiter := #9; + end; + + // Quote character + case CbQuoteChar.ItemIndex of + 0: AParams.QuoteChar := #0; + 1: AParams.QuoteChar := '"'; + 2: AParams.QuoteChar := ''''; + end; + + // Detect content type and convert + AParams.DetectContentType := RgDetectContentType.ItemIndex <> 0; + + // Auto-detect number format + AParams.AutoDetectNumberFormat := CbAutoDetectNumberFormat.Checked; + + // Number format + AParams.NumberFormat := EdNumFormat.Text; + + // Decimal separator + DecimalSeparatorToFormatSettings(AParams.FormatSettings); + + // Thousand separator + ThousandSeparatorToFormatSettings(AParams.FormatSettings); + + // Currency symbol + if (EdCurrencySymbol.Text = '') or (EdCurrencySymbol.Text = rsLikeSpreadsheet) then + AParams.FormatSettings.CurrencyString := '' + else + AParams.FormatSettings.CurrencyString := UTF8ToAnsi(EdCurrencySymbol.Text); + + { + // Pos currency format + if CbPosCurrencyFormat.ItemIndex = 0 then + AParams.FormatSettings.CurrencyFormat := byte(-1) + else + AParams.FormatSettings.CurrencyFormat := CbPosCurrencyFormat.ItemIndex-1; + + // Neg currency format + if CbNegCurrencyFormat.ItemIndex = 0 then + AParams.FormatSettings.NegCurrFormat := byte(-1) + else + AParams.FormatSettings.NegCurrFormat := CbNegCurrencyFormat.ItemIndex-1; + } + + // Long date format string + if (CbLongDateFormat.ItemIndex = 0) or (CbLongDateFormat.Text = '') then + AParams.FormatSettings.LongDateFormat := '' + else + AParams.FormatSettings.LongDateFormat := CbLongDateFormat.Text; + + // Short date format string + if (CbShortDateFormat.ItemIndex = 0) or (CbShortDateFormat.Text = '') then + AParams.FormatSettings.ShortDateFormat := '' + else + AParams.FormatSettings.ShortDateFormat := CbShortDateFormat.Text; + + // Date separator + DateSeparatorToFormatSettings(AParams.FormatSettings); + + // Long month names + FEdLongMonthNames.GetNames(AParams.FormatSettings.LongMonthNames); + + // Short month names + FEdShortMonthNames.GetNames(AParams.FormatSettings.ShortMonthNames); + + // Long day names + FEdLongDayNames.GetNames(AParams.FormatSettings.LongDayNames); + + // Short day names + FEdShortDayNames.GetNames(AParams.FormatSettings.ShortDayNames); + + // Long time format string + if CbLongTimeFormat.ItemIndex = 0 then + AParams.FormatSettings.LongTimeFormat := '' + else + AParams.FormatSettings.LongTimeFormat := CbLongTimeFormat.Text; + + // Short time format string + if CbShortTimeFormat.ItemIndex = 0 then + AParams.FormatSettings.ShortTimeFormat := '' + else + AParams.FormatSettings.ShortTimeFormat := CbShortTimeFormat.Text; + + // Time separator + TimeSeparatorToFormatSettings(AParams.FormatSettings); + + // Text for "TRUE" + AParams.TrueText := EdTRUE.Text; + + // Test for "FALSE" + AParams.FalseText := EdFALSE.Text; +end; + +procedure TCSVParamsForm.SetParams(const AParams: TsCSVParams); +begin + // Line endings + case AParams.LineEnding of + leSystem: CbLineEnding.ItemIndex := 0; + leCRLF : CbLineEnding.ItemIndex := 1; + leCR : CbLineEnding.ItemIndex := 2; + leLF : CbLineEnding.ItemIndex := 3; + end; + + // Column delimiter + case AParams.Delimiter of + ',' : CbDelimiter.ItemIndex := 0; + ';' : CbDelimiter.ItemIndex := 1; + ':' : CbDelimiter.ItemIndex := 2; + '|' : CbDelimiter.ItemIndex := 3; + #9 : CbDelimiter.ItemIndex := 4; + end; + + // Quote character + case AParams.QuoteChar of + #0 : CbQuoteChar.ItemIndex := 0; + '"' : CbQuoteChar.ItemIndex := 1; + '''' : CbQuoteChar.ItemIndex := 2; + end; + + // Detect content type + RgDetectContentType.ItemIndex := ord(AParams.DetectContentType); + + // Auto-detect number format + CbAutoDetectNumberFormat.Checked := AParams.AutoDetectNumberFormat; + + // Number format + EdNumFormat.Text := AParams.NumberFormat; + + // Decimal separator + case AParams.FormatSettings.DecimalSeparator of + #0 : CbDecimalSeparator.ItemIndex := 0; + '.' : CbDecimalSeparator.ItemIndex := 1; + ',' : CbDecimalSeparator.ItemIndex := 2; + else CbDecimalSeparator.Text := AParams.FormatSettings.DecimalSeparator; + end; + + // Thousand separator + case AParams.FormatSettings.ThousandSeparator of + #0 : CbThousandSeparator.ItemIndex := 0; + '.' : CbThousandSeparator.ItemIndex := 1; + ',' : CbThousandSeparator.ItemIndex := 2; + ' ' : CbThousandSeparator.ItemIndex := 3; + else CbThousandSeparator.Text := AParams.FormatSettings.ThousandSeparator; + end; + + // Currency symbol + if AParams.FormatSettings.CurrencyString = '' then + EdCurrencySymbol.Text := rsLikeSpreadsheet + else + EdCurrencySymbol.Text := AnsiToUTF8(AParams.FormatSettings.CurrencyString); +(* + // Positive currency format + BuildCurrencyFormatList(CbPosCurrencyFormat.Items, true, CURR_VALUE, GetCurrencySymbol); + CbPosCurrencyFormat.Items.Insert(0, rsLikeSpreadsheet); + if AParams.FormatSettings.CurrencyFormat = byte(-1) then + CbPosCurrencyformat.ItemIndex := 0 + else + CbPoscurrencyFormat.ItemIndex := AParams.FormatSettings.CurrencyFormat+1; + + // Negative currency format + BuildCurrencyFormatList(CbNegCurrencyFormat.Items, false, CURR_VALUE, GetCurrencySymbol); + CbNegCurrencyFormat.Items.Insert(0, rsLikeSpreadsheet); + if AParams.FormatSettings.NegCurrFormat = byte(-1) then + CbNegCurrencyformat.ItemIndex := 0 + else + CbNegcurrencyFormat.ItemIndex := AParams.FormatSettings.NegCurrFormat+1; +*) + // Long date format + if AParams.FormatSettings.LongDateFormat = '' then + CbLongDateFormat.ItemIndex := 0 + else + CbLongDateFormat.Text := AParams.FormatSettings.LongDateFormat; + + // Short date format + if AParams.FormatSettings.ShortDateFormat = '' then + CbShortDateFormat.ItemIndex := 0 + else + CbShortDateFormat.Text := AParams.FormatSettings.ShortDateFormat; + + // Date separator + case AParams.FormatSettings.DateSeparator of + #0 : CbDateSeparator.ItemIndex := 0; + '.' : CbDateSeparator.ItemIndex := 1; + '-' : CbDateSeparator.ItemIndex := 2; + '/' : CbDateSeparator.ItemIndex := 3; + else CbDateSeparator.Text := AParams.FormatSettings.DateSeparator; + end; + + // Long month names + FEdLongMonthNames.SetNames(AParams.FormatSettings.LongMonthNames, 12, false, rsLikeSpreadsheet); + + // Short month names + FEdShortMonthNames.SetNames(AParams.FormatSettings.ShortMonthNames, 12, true, rsLikeSpreadsheet); + + // Long day names + FEdLongDayNames.SetNames(AParams.FormatSettings.LongDayNames, 7, false, rsLikeSpreadsheet); + + // Short month names + FEdShortDayNames.SetNames(AParams.FormatSettings.ShortDayNames, 7, true, rsLikeSpreadsheet); + + // Long time format + if AParams.FormatSettings.LongTimeFormat = '' then + CbLongTimeFormat.ItemIndex := 0 + else + CbLongTimeFormat.Text := AParams.FormatSettings.LongTimeFormat; + + // Short time format + if AParams.FormatSettings.ShortTimeFormat = '' then + CbShortTimeFormat.ItemIndex := 0 + else + CbShortTimeFormat.Text := AParams.FormatSettings.ShortTimeFormat; + + // Time separator + case AParams.FormatSettings.TimeSeparator of + #0 : CbTimeSeparator.ItemIndex := 0; + '.' : CbTimeSeparator.ItemIndex := 1; + '-' : CbTimeSeparator.ItemIndex := 2; + '/' : CbTimeSeparator.ItemIndex := 3; + ':' : CbTimeSeparator.ItemIndex := 4; + else CbTimeSeparator.Text := AParams.FormatSettings.TimeSeparator; + end; + + // Text for "TRUE" + EdTRUE.Text := AParams.TrueText; + + // Test for "FALSE" + EdFALSE.Text := AParams.FalseText; + + // Update date/time sample display + DateTimeFormatChange(nil); +end; + +procedure TCSVParamsForm.ThousandSeparatorToFormatSettings(var ASettings: TFormatSettings); +begin + case CbThousandSeparator.ItemIndex of + 0: ASettings.ThousandSeparator := #0; + 1: ASettings.ThousandSeparator := '.'; + 2: ASettings.ThousandSeparator := ','; + 3: ASettings.ThousandSeparator := ' '; + else ASettings.ThousandSeparator := CbThousandSeparator.Text[1]; + end; +end; + +procedure TCSVParamsForm.TimeSeparatorToFormatSettings(var ASettings: TFormatSettings); +begin + case CbTimeSeparator.ItemIndex of + 0: ASettings.TimeSeparator := #0; + 1: ASettings.TimeSeparator := '.'; + 2: ASettings.TimeSeparator := '-'; + 3: ASettings.TimeSeparator := '/'; + 4: ASettings.TimeSeparator := ':'; + else ASettings.TimeSeparator := CbTimeSeparator.Text[1]; + end; +end; + +initialization + {$I scsvparamsform.lrs} + +end. + diff --git a/components/fpspreadsheet/examples/spready/sctrls.pas b/components/fpspreadsheet/examples/spready/sctrls.pas new file mode 100644 index 000000000..768c86cf0 --- /dev/null +++ b/components/fpspreadsheet/examples/spready/sctrls.pas @@ -0,0 +1,264 @@ +unit sCtrls; + +{$mode objfpc}{$H+} + +interface + +uses + Classes, SysUtils, Controls, StdCtrls, Grids, EditBtn, Forms; + +type + { TMonthDayNamesEdit } + TMonthDayNamesEdit = class(TEditButton) + private + FEmptyString: String; + FCount: Integer; + FShortnames: Boolean; + procedure ButtonClick(Sender: TObject); + function CreateMonthDayNamesEditor(var AGrid: TStringGrid): TForm; + protected + public + constructor Create(AOwner: TComponent); override; + procedure GetNames(var ANamesArray); + procedure SetNames(const ANamesArray; ACount: Integer; IsShortNames: Boolean; + const AEmptyString: String); + end; + + { TFormatSeparatorCombo } + TFormatSeparatorKind = (skDecimal, skThousand, skDate, skTime, skList); + + TFormatSeparatorCombo = class(TCombobox) + private + FKind: TFormatSeparatorKind; + function GetSeparator: Char; + procedure SetSeparator(AValue: Char); + procedure SetSeparatorKind(AValue: TFormatSeparatorKind); + public + property Separator: Char read GetSeparator write SetSeparator; + property SeparatorKind: TFormatSeparatorKind read FKind write SetSeparatorKind; + end; + + +implementation + +uses + Math, ButtonPanel, fpsUtils; + + +{ TMonthDayNamesEdit } + +constructor TMonthDayNamesEdit.Create(AOwner: TComponent); +begin + inherited Create(AOwner); + Button.Caption := '...'; + //ButtonCaption := '...'; + OnButtonClick := @ButtonClick; +end; + +procedure TMonthDayNamesEdit.ButtonClick(Sender: TObject); +var + List: TStringList; + F: TForm; + i, j: Integer; + s: String; + isEmpty: Boolean; + grid: TStringGrid; + names: TMonthNameArray; // can hold day and month names as well +begin + F := CreateMonthDayNamesEditor(grid); + try + if F.ShowModal = mrOK then + begin + for i:=1 to 12 do + names[i] := ''; + for i:=1 to grid.RowCount-1 do + names[i] := grid.Cells[1, i]; + SetNames(names, FCount, FShortNames, FEmptyString); + end; + finally + F.Free; + end; +end; + +function TMonthDayNamesEdit.CreateMonthDayNamesEditor(var AGrid: TStringGrid): TForm; +var + btnPanel: TButtonPanel; + i: Integer; + R: TRect; + Pt: TPoint; + w: Integer; + names: TMonthNameArray; // has space for both months and days... +begin + Result := TForm.Create(nil); + btnPanel := TButtonPanel.Create(Result); + with btnPanel do begin + Parent := Result; + ShowButtons := [pbOK, pbCancel]; + end; + AGrid := TStringGrid.Create(Result); + with AGrid do begin + Parent := Result; + Align := alClient; + BorderSpacing.Around := 8; + TitleStyle := tsNative; + Options := Options + [goEditing, goAlwaysShowEditor] - [goVertLine]; + DefaultColWidth := 150; + AutoFillColumns := true; + ColCount := 2; + RowCount := FCount+1; + if FCount = 12 then + begin + Cells[0, 1] := 'January'; + Cells[0, 2] := 'February'; + Cells[0, 3] := 'March'; + Cells[0, 4] := 'April'; + Cells[0, 5] := 'May'; + Cells[0, 6] := 'June'; + Cells[0, 7] := 'July'; + Cells[0, 8] := 'August'; + Cells[0, 9] := 'September'; + Cells[0,10] := 'October'; + Cells[0,11] := 'November'; + Cells[0,12] := 'December'; + if FShortNames then + Cells[1, 0] := 'Short month names' + else + Cells[1, 0] := 'Long month names'; + end else + begin + Cells[0, 1] := 'Sunday'; + Cells[0, 2] := 'Monday'; + Cells[0, 3] := 'Tuesday'; + Cells[0, 4] := 'Wesdnesday'; + Cells[0, 5] := 'Thursday'; + Cells[0, 6] := 'Friday'; + Cells[0, 7] := 'Saturday'; + if FShortNames then + Cells[1, 0] := 'Short day names' + else + Cells[1, 0] := 'Long day names'; + end; + GetNames(names); + w := 0; + for i:=1 to FCount do + begin + Cells[1, i] := TMonthNameArray(names)[i]; + w := Max(w, Canvas.TextWidth(Cells[0, i])); + end; + ColWidths[0] := w + 16; + ColWidths[1] := 2*w; + R := CellRect(ColCount-1, RowCount-1); + end; + Pt := Result.ScreenToClient(AGrid.ClientToScreen(R.BottomRight)); + Result.Width := AGrid.width + AGrid.BorderSpacing.Around*2 + 5; + Result.Height := Pt.Y + btnPanel.Height + AGrid.BorderSpacing.Around*2 - 6; + Result.Position := poMainFormCenter; + Result.ActiveControl := AGrid; +end; + +procedure TMonthDayNamesEdit.GetNames(var ANamesArray); +{ Not very nice code here: will crash if a TWeekNameArray is passed as ANameArray, + but the edit stores month data! Watch out... } +var + L: TStringList; + i: Integer; +begin + for i:=1 to FCount do + TMonthNameArray(ANamesArray)[i] := ''; + if Text <> FEmptyString then + begin + L := TStringList.Create; + try + L.Delimiter := DefaultFormatSettings.ListSeparator; + L.DelimitedText := Text; + for i:=0 to L.Count-1 do + if i < L.Count then + TMonthNameArray(ANamesArray)[i+1] := UTF8ToAnsi(L[i]); + finally + L.Free; + end; + end; +end; + +procedure TMonthDayNamesEdit.SetNames(const ANamesArray; ACount: Integer; + IsShortNames: Boolean; const AEmptyString: String); +begin + if not ACount in [7, 12] then + raise Exception.Create('[TMonthDayNameEdit] Array length can only be 7 or 12.'); + + FCount := ACount; + FEmptyString := AEmptyString; + FShortNames := IsShortNames; + + case FCount of + 7 : Text := AnsiToUTF8(DayNamesToString(TWeekNameArray(ANamesArray), AEmptyString)); + 12: Text := AnsiToUTF8(MonthNamesToString(TMonthNameArray(ANamesArray), AEmptyString)); + else raise Exception.Create('[TMonthDayNameEdit] Array length can only be 7 or 12.'); + end; +end; + + +{ TFormatSeparatorCombo } + +function TFormatSeparatorCombo.GetSeparator: Char; +begin + if ItemIndex = -1 then + begin + if Text = '' then + Result := #0 + else + Result := Text[1]; + end else + Result := Char(PtrInt(items.Objects[ItemIndex])); +end; + +procedure TFormatSeparatorCombo.SetSeparator(AValue: Char); +var + i: Integer; +begin + i := Items.IndexOfObject(TObject(PtrInt(ord(AValue)))); + if i = -1 then + Text := AValue + else + ItemIndex := i; +end; + +procedure TFormatSeparatorCombo.SetSeparatorKind(AValue: TFormatSeparatorKind); +begin + FKind := AValue; + Items.BeginUpdate; + try + case FKind of + skDecimal, skThousand: + begin + Items.AddObject('Dot ( . )', TObject(PtrInt(ord('.')))); + Items.AddObject('Comma ( , )', TObject(PtrInt(ord(',')))); + if FKind = skThousand then + Items.AddObject('Space ( )', TObject(PtrInt(ord(' ')))); + end; + skDate, skTime: + begin + Items.AddObject('Dot ( . )', TObject(PtrInt(ord('.')))); + Items.AddObject('Dash ( - )', TObject(PtrInt(ord('-')))); + Items.AddObject('Slash ( / )', TObject(PtrInt(ord('/')))); + if FKind = skTime then + Items.AddObject('Colon ( : )', TObject(PtrInt(ord(':')))); + end; + skList: + begin + Items.AddObject('Dot ( . )', TObject(PtrInt(ord('.')))); + Items.AddObject('Comma ( , )', TObject(PtrInt(ord(',')))); + Items.AddObject('Semicolon ( ; )', TObject(PtrInt(ord(';')))); + Items.AddObject('Colon ( : )', TObject(PtrInt(ord(':')))); + Items.AddObject('Bar ( | )', TObject(PtrInt(ord('|')))); + Items.AddObject('Slash ( / )', TObject(PtrInt(ord('/')))); + Items.AddObject('Backslash ( \ )', TObject(PtrInt(ord('\')))); + end; + end; + finally + Items.EndUpdate; + end; +end; + +end. + diff --git a/components/fpspreadsheet/examples/spready/sformatsettingsform.lfm b/components/fpspreadsheet/examples/spready/sformatsettingsform.lfm new file mode 100644 index 000000000..99dfbf1b3 --- /dev/null +++ b/components/fpspreadsheet/examples/spready/sformatsettingsform.lfm @@ -0,0 +1,378 @@ +object FormatSettingsForm: TFormatSettingsForm + Left = 417 + Height = 476 + Top = 229 + Width = 417 + BorderStyle = bsDialog + Caption = 'Workbook format settings' + ClientHeight = 476 + ClientWidth = 417 + OnCloseQuery = FormCloseQuery + OnCreate = FormCreate + Position = poMainFormCenter + LCLVersion = '1.3' + object PageControl: TPageControl + Left = 8 + Height = 420 + Top = 8 + Width = 401 + ActivePage = PgDateTime + Align = alClient + BorderSpacing.Around = 8 + TabIndex = 2 + TabOrder = 0 + OnChange = PageControlChange + object PgNumber: TTabSheet + Caption = 'Number' + ClientHeight = 392 + ClientWidth = 393 + object LblDecimalSeparator: TLabel + Left = 16 + Height = 15 + Top = 19 + Width = 98 + Caption = 'Decimal separator:' + ParentColor = False + end + object LblThousandSeparator: TLabel + Left = 16 + Height = 15 + Top = 51 + Width = 108 + Caption = 'Thousand separator:' + ParentColor = False + end + object Label1: TLabel + Left = 4 + Height = 15 + Top = 373 + Width = 385 + Align = alBottom + BorderSpacing.Around = 4 + Caption = 'The current workbook is automatically updated to these settings.' + ParentColor = False + WordWrap = True + end + object Bevel3: TBevel + Left = 0 + Height = 3 + Top = 366 + Width = 393 + Align = alBottom + Shape = bsBottomLine + end + end + object PgCurrency: TTabSheet + Caption = 'Currency' + ClientHeight = 392 + ClientWidth = 393 + object LblCurrencySymbol: TLabel + Left = 16 + Height = 15 + Top = 20 + Width = 93 + Caption = 'Currency symbol:' + FocusControl = EdCurrencySymbol + ParentColor = False + end + object EdCurrencySymbol: TEdit + Left = 200 + Height = 23 + Top = 16 + Width = 178 + OnChange = EdCurrencySymbolChange + TabOrder = 0 + end + object LblCurrencySymbol1: TLabel + Left = 16 + Height = 15 + Top = 52 + Width = 132 + Caption = 'Currency decimal places:' + FocusControl = EdCurrencyDecimals + ParentColor = False + end + object EdCurrencyDecimals: TSpinEdit + Left = 200 + Height = 23 + Top = 48 + Width = 66 + Alignment = taRightJustify + TabOrder = 1 + end + object LblPosCurrencyFormat: TLabel + Left = 16 + Height = 15 + Top = 84 + Width = 135 + Caption = 'Format of positive values:' + FocusControl = CbPosCurrencyFormat + ParentColor = False + end + object CbPosCurrencyFormat: TComboBox + Left = 200 + Height = 23 + Top = 80 + Width = 178 + ItemHeight = 15 + Style = csDropDownList + TabOrder = 2 + end + object LblNegCurrencyFormat: TLabel + Left = 16 + Height = 15 + Top = 116 + Width = 139 + Caption = 'Format of negative values:' + FocusControl = CbNegCurrencyFormat + ParentColor = False + end + object CbNegCurrencyFormat: TComboBox + Left = 200 + Height = 23 + Top = 112 + Width = 178 + ItemHeight = 15 + Style = csDropDownList + TabOrder = 3 + end + object Label2: TLabel + Left = 4 + Height = 15 + Top = 373 + Width = 385 + Align = alBottom + BorderSpacing.Around = 4 + Caption = 'These settings are only respected in new cells.' + ParentColor = False + WordWrap = True + end + object Bevel2: TBevel + Left = 0 + Height = 3 + Top = 366 + Width = 393 + Align = alBottom + Shape = bsBottomLine + end + end + object PgDateTime: TTabSheet + Caption = 'Date/time' + ClientHeight = 392 + ClientWidth = 393 + object LblNumFormat1: TLabel + Left = 16 + Height = 15 + Top = 20 + Width = 128 + Caption = 'Long date format string:' + ParentColor = False + end + object CbLongDateFormat: TComboBox + Left = 200 + Height = 23 + Top = 16 + Width = 178 + ItemHeight = 15 + ItemIndex = 0 + Items.Strings = ( + 'ddd, d/mm/yyyy' + 'ddd, d/mmm/yyyy' + 'dddd, d/mm/yyyy' + 'dddd, d/mmm/yyyy' + 'd/mm/yyyy' + 'dd/mm/yyyy' + 'dddd, mm/d/yyyy' + 'dddd, mmm/d/yyyy' + 'mm/d/yyyy' + 'mm/dd/yyyy' + 'yyyy/mm/dd' + 'yyyy/mm/d' + 'yyyy/mmm/d' + 'yyyy/mmmm/d' + ) + OnChange = DateTimeFormatChange + OnEnter = DateTimeFormatChange + TabOrder = 0 + Text = 'ddd, d/mm/yyyy' + end + object LblNumFormat2: TLabel + Left = 16 + Height = 15 + Top = 52 + Width = 129 + Caption = 'Short date format string:' + ParentColor = False + end + object CbShortDateFormat: TComboBox + Left = 200 + Height = 23 + Top = 48 + Width = 178 + ItemHeight = 15 + ItemIndex = 0 + Items.Strings = ( + 'd/m/yy' + 'd/mm/yy' + 'd/mm/yyyy' + 'm/d/yy' + 'mm/d/yy' + 'mm/d/yyyy' + 'yy/m/d' + 'yy/mm/d' + 'yyyy/mm/d' + ) + OnChange = DateTimeFormatChange + OnEnter = DateTimeFormatChange + TabOrder = 1 + Text = 'd/m/yy' + end + object LblDateSeparator: TLabel + Left = 16 + Height = 15 + Top = 83 + Width = 79 + Caption = 'Date separator:' + ParentColor = False + end + object LblLongMonthNames: TLabel + Left = 16 + Height = 15 + Top = 116 + Width = 107 + Caption = 'Long month names:' + ParentColor = False + end + object LblShortMonthNames: TLabel + Left = 16 + Height = 15 + Top = 148 + Width = 108 + Caption = 'Short month names:' + ParentColor = False + end + object LblLongDayNames: TLabel + Left = 16 + Height = 15 + Top = 180 + Width = 90 + Caption = 'Long day names:' + ParentColor = False + end + object LblShortDayNames: TLabel + Left = 16 + Height = 15 + Top = 212 + Width = 91 + Caption = 'Short day names:' + ParentColor = False + end + object LblNumFormat3: TLabel + Left = 16 + Height = 15 + Top = 252 + Width = 129 + Caption = 'Long time format string:' + ParentColor = False + end + object LblNumFormat4: TLabel + Left = 16 + Height = 15 + Top = 284 + Width = 130 + Caption = 'Short time format string:' + ParentColor = False + end + object LblTimeSeparator: TLabel + Left = 16 + Height = 15 + Top = 315 + Width = 82 + Caption = 'Time separator:' + ParentColor = False + end + object CbLongTimeFormat: TComboBox + Left = 200 + Height = 23 + Top = 248 + Width = 178 + ItemHeight = 15 + ItemIndex = 1 + Items.Strings = ( + 'h:n:s' + 'h:nn:ss' + 'hh:nn:ss' + ) + OnChange = DateTimeFormatChange + OnEnter = DateTimeFormatChange + TabOrder = 2 + Text = 'h:nn:ss' + end + object CbShortTimeFormat: TComboBox + Left = 200 + Height = 23 + Top = 280 + Width = 178 + ItemHeight = 15 + ItemIndex = 1 + Items.Strings = ( + 'h:n' + 'h:nn' + 'hh:nn' + ) + OnChange = DateTimeFormatChange + OnEnter = DateTimeFormatChange + TabOrder = 3 + Text = 'h:nn' + end + object Label3: TLabel + Left = 4 + Height = 30 + Top = 358 + Width = 385 + Align = alBottom + BorderSpacing.Around = 4 + Caption = 'Only the date and time separator are automatically respected by the workbook; the other settings are considered only for new cells.' + ParentColor = False + WordWrap = True + end + object Bevel1: TBevel + Left = 0 + Height = 3 + Top = 351 + Width = 393 + Align = alBottom + Shape = bsBottomLine + end + end + end + object ButtonPanel: TButtonPanel + Left = 6 + Height = 34 + Top = 436 + Width = 405 + OKButton.Name = 'OKButton' + OKButton.DefaultCaption = True + OKButton.OnClick = OKButtonClick + HelpButton.Name = 'HelpButton' + HelpButton.DefaultCaption = True + CloseButton.Name = 'CloseButton' + CloseButton.DefaultCaption = True + CancelButton.Name = 'CancelButton' + CancelButton.DefaultCaption = True + TabOrder = 1 + ShowButtons = [pbOK, pbCancel] + object LblDateTimeSample: TLabel + Left = 6 + Height = 36 + Top = 2 + Width = 234 + Anchors = [akTop, akLeft, akRight] + AutoSize = False + Caption = 'sample' + Layout = tlCenter + ParentColor = False + WordWrap = True + end + end +end diff --git a/components/fpspreadsheet/examples/spready/sformatsettingsform.pas b/components/fpspreadsheet/examples/spready/sformatsettingsform.pas new file mode 100644 index 000000000..b8265289c --- /dev/null +++ b/components/fpspreadsheet/examples/spready/sformatsettingsform.pas @@ -0,0 +1,443 @@ +unit sFormatsettingsForm; + +{$mode objfpc}{$H+} + +interface + +uses + Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs, + Grids, ButtonPanel, ComCtrls, StdCtrls, Spin, ExtCtrls, sCtrls; + +type + { TFormatSettingsForm } + + TFormatSettingsForm = class(TForm) + Bevel1: TBevel; + Bevel2: TBevel; + Bevel3: TBevel; + ButtonPanel: TButtonPanel; + CbLongDateFormat: TComboBox; + CbLongTimeFormat: TComboBox; + CbPosCurrencyFormat: TComboBox; + CbNegCurrencyFormat: TComboBox; + CbShortDateFormat: TComboBox; + CbShortTimeFormat: TComboBox; + EdCurrencySymbol: TEdit; + EdCurrencyDecimals: TSpinEdit; + Label1: TLabel; + Label2: TLabel; + Label3: TLabel; + LblCurrencySymbol: TLabel; + LblCurrencySymbol1: TLabel; + LblDateTimeSample: TLabel; + LblDecimalSeparator: TLabel; + LblDateSeparator: TLabel; + LblTimeSeparator: TLabel; + LblLongDayNames: TLabel; + LblLongMonthNames: TLabel; + LblNumFormat1: TLabel; + LblNumFormat2: TLabel; + LblNumFormat3: TLabel; + LblNumFormat4: TLabel; + LblPosCurrencyFormat: TLabel; + LblNegCurrencyFormat: TLabel; + LblShortDayNames: TLabel; + LblShortMonthNames: TLabel; + LblThousandSeparator: TLabel; + PageControl: TPageControl; + PgCurrency: TTabSheet; + PgDateTime: TTabSheet; + PgNumber: TTabSheet; + procedure DateTimeFormatChange(Sender: TObject); + procedure EdCurrencySymbolChange(Sender: TObject); + procedure FormCloseQuery(Sender: TObject; var CanClose: boolean); + procedure FormCreate(Sender: TObject); + procedure OKButtonClick(Sender: TObject); + procedure PageControlChange(Sender: TObject); + private + FSampleDateTime: TDateTime; + FDateFormatSample: String; + FTimeFormatSample: String; + FEdLongMonthNames: TMonthDayNamesEdit; + FEdShortMonthNames: TMonthDayNamesEdit; + FEdLongDayNames: TMonthDayNamesEdit; + FEdShortDayNames: TMonthDayNamesEdit; + FCbDecimalSeparator: TFormatSeparatorCombo; + FCbThousandSeparator: TFormatSeparatorCombo; + FCbDateSeparator: TFormatSeparatorCombo; + FCbTimeSeparator: TFormatSeparatorCombo; + function GetFormatSettings: TFormatSettings; + procedure SetFormatSettings(const AValue: TFormatSettings); + function ValidData(out AControl: TWinControl; out AMsg: String): Boolean; + public + { public declarations } + property FormatSettings: TFormatSettings read GetFormatSettings write SetFormatSettings; + end; + +var + FormatSettingsForm: TFormatSettingsForm; + + +implementation + +uses + fpsUtils; + +const + CURR_VALUE = 100.0; + +var + PageIndex: Integer = 0; // stores the previously selected page index (to open the form always with previously used page) + + +{ TFormatSettingsForm } + +procedure TFormatSettingsForm.DateTimeFormatChange(Sender: TObject); +var + fs: TFormatSettings; + ctrl: TWinControl; + dt: TDateTime; + arr: Array[1..12] of String; + i: Integer; + s: String; +begin + fs := GetFormatSettings; + dt := FSampleDateTime; + ctrl := ActiveControl; + + if (ctrl = CbLongDateFormat) then + begin + FDateFormatSample := fs.LongDateFormat; + s := AnsiToUTF8(FormatDateTime(FDateFormatSample, dt, fs)); + LblDateTimeSample.Caption := 'Sample date:'#13 + s; + end + else + if (ctrl = CbShortDateFormat) then + begin + FDateFormatSample := fs.ShortDateFormat; + s := AnsiToUTF8(FormatDateTime(FDateFormatSample, dt, fs)); + LblDateTimeSample.Caption := 'Sample date:'#13 + s; + end + else + if (ctrl = FCbDateSeparator) then begin + s := AnsiToUTF8(FormatDateTime(FDateFormatSample, dt, fs)); + LblDateTimeSample.Caption := 'Sample date:'#13 + s; + end + else + if (ctrl = CbLongTimeFormat) then + begin + FTimeFormatSample := fs.LongTimeFormat; + s := AnsiToUTF8(FormatDateTime(FTimeFormatSample, dt, fs)); + LblDateTimeSample.Caption := 'Sample time:'#13 + s; + end + else + if (ctrl = CbShortTimeFormat) then + begin + FTimeFormatSample := fs.ShortTimeFormat; + s := AnsiToUTF8(FormatDateTime(FTimeFormatSample, dt, fs)); + LblDateTimeSample.Caption := 'Sample time:'#13 + s; + end + else + if (ctrl = FCbTimeSeparator) then + begin + s := AnsiToUTF8(FormatDateTime(FTimeFormatSample, dt, fs)); + LblDateTimeSample.Caption := 'Sample time:'#13 + s; + { + end + else + begin + s := AnsiToUTF8(FormatDateTime('c', dt, fs)); + LblDateTimeSample.Caption := 'Sample date/time:'#13 + s; + } + end; + + LblDateTimeSample.Visible := (PageControl.Activepage = PgDateTime) and + ((FDateFormatSample <> '') or (FTimeFormatSample <> '')); +// Application.ProcessMessages; +end; + +procedure TFormatSettingsForm.EdCurrencySymbolChange(Sender: TObject); +var + currSym: String; +begin + currSym := EdCurrencySymbol.Text; + BuildCurrencyFormatList(CbPosCurrencyFormat.Items, true, CURR_VALUE, currSym); + BuildCurrencyFormatList(CbNegCurrencyFormat.Items, false, CURR_VALUE, currSym); +end; + +procedure TFormatSettingsForm.FormCloseQuery(Sender: TObject; + var CanClose: boolean); +begin + Unused(Sender, CanClose); + PageIndex := PageControl.ActivePageIndex; +end; + +procedure TFormatSettingsForm.FormCreate(Sender: TObject); +const + DROPDOWN_COUNT = 32; +begin + PageControl.ActivePageIndex := PageIndex; + + CbLongDateFormat.DropdownCount := DROPDOWN_COUNT; + CbShortDateFormat.DropdownCount := DROPDOWN_COUNT; + CbLongTimeFormat.DropdownCount := DROPDOWN_COUNT; + CbShortTimeFormat.DropdownCount := DROPDOWN_COUNT; + CbPosCurrencyFormat.DropdownCount := DROPDOWN_COUNT; + CbNegCurrencyFormat.DropdownCount := DROPDOWN_COUNT; + + FCbDecimalSeparator := TFormatSeparatorCombo.Create(self); + with FCbDecimalSeparator do + begin + Parent := PgNumber; + Left := CbLongDateFormat.Left; + Width := CbLongDateFormat.Width; + Top := CbLongDateFormat.Top; + TabOrder := 0; + SeparatorKind := skDecimal; + end; + LblDecimalSeparator.FocusControl := FCbDecimalSeparator; + + FCbThousandSeparator := TFormatSeparatorCombo.Create(self); + with FCbThousandSeparator do + begin + Parent := PgNumber; + Left := FCbDecimalSeparator.Left; + Width := FCbDecimalSeparator.Width; + Top := FCBDecimalSeparator.Top + 32; + TabOrder := FCbDecimalSeparator.TabOrder + 1; + SeparatorKind := skThousand; + end; + LblThousandSeparator.FocusControl := FCbThousandSeparator; + + FCbDateSeparator := TFormatSeparatorCombo.Create(self); + with FCbDateSeparator do + begin + Parent := PgDateTime; + Left := CbShortDateFormat.Left; + Width := CbShortDateFormat.Width; + Top := CbShortDateFormat.Top + 32; + TabOrder := CbShortDateFormat.TabOrder + 1; + SeparatorKind := skDate; + OnChange := @DateTimeFormatChange; + OnEnter := @DateTimeFormatChange; + end; + LblDateSeparator.FocusControl := FCbDateSeparator; + + FEdLongMonthNames := TMonthDayNamesEdit.Create(self); + with FEdLongMonthNames do + begin + Parent := PgDateTime; + Left := CbShortDateFormat.Left; + Width := CbShortDateFormat.Width; + Top := CbShortDateFormat.Top + 32*2; + OnChange := @DateTimeFormatChange; + OnEnter := @DateTimeFormatChange; + TabOrder := CbShortDateFormat.TabOrder + 2; + end; + LblLongMonthNames.FocusControl := FEdLongMonthNames; + + FEdShortMonthNames := TMonthDayNamesEdit.Create(self); + with FEdShortMonthNames do + begin + Parent := PgDateTime; + Left := CbShortDateFormat.Left; + Width := CbShortdateFormat.Width; + Top := CbShortDateFormat.Top + 32*3; + TabOrder := CbShortDateFormat.TabOrder + 3; + OnChange := @DateTimeFormatChange; + OnEnter := @DateTimeFormatChange; + end; + LblShortMonthNames.FocusControl := FEdShortMonthNames; + + FEdLongDayNames := TMonthDayNamesEdit.Create(self); + with FEdLongDayNames do + begin + Parent := PgDateTime; + Left := CbShortDateformat.Left; + Width := CbShortDateFormat.Width; + Top := CbShortDateFormat.Top + 32*4; + TabOrder := CbShortDateFormat.TabOrder + 4; + OnChange := @DateTimeFormatChange; + OnEnter := @DateTimeFormatChange; + end; + LblLongDayNames.FocusControl := FEdLongDayNames; + + FEdShortDayNames := TMonthDayNamesEdit.Create(self); + with FEdShortDayNames do + begin + Parent := PgDateTime; + Left := CbShortDateFormat.Left; + Width := CbShortDateFormat.Width; + Top := CbShortDateFormat.Top + 32*5; + TabOrder := CbShortDateFormat.TabOrder + 5; + OnChange := @DateTimeFormatChange; + OnEnter := @DateTimeFormatChange; + end; + LblShortDayNames.FocusControl := FEdShortDayNames; + + FCbTimeSeparator := TFormatSeparatorCombo.Create(self); + with FCbTimeSeparator do + begin + Parent := PgDateTime; + Left := CbShortTimeFormat.Left; + Width := CbShortTimeFormat.Width; + Top := CbShortTimeFormat.Top + 32; + TabOrder := CbShortTimeFormat.TabOrder + 1; + SeparatorKind := skTime; + OnChange := @DateTimeFormatChange; + OnEnter := @DateTimeFormatChange; + end; + LblTimeSeparator.FocusControl := FCbTimeSeparator; + + FDateFormatSample := ''; + FTimeFormatSample := ''; + FSampleDateTime := now(); + + LblDateTimeSample.Visible := false; +end; + +procedure TFormatSettingsForm.OKButtonClick(Sender: TObject); +var + msg: String; + C: TWinControl; + cParent: TWinControl; +begin + if not ValidData(C, msg) then + begin + cParent := C.Parent; + while (cParent <> nil) and not (cParent is TTabSheet) do + cParent := cParent.Parent; + PageControl.ActivePage := cParent as TTabSheet; + if C.CanFocus then C.SetFocus; + MessageDlg(msg, mtError, [mbOK], 0); + ModalResult := mrNone; + end; +end; + +procedure TFormatSettingsForm.PageControlChange(Sender: TObject); +begin + LblDateTimeSample.Visible := (PageControl.Activepage = PgDateTime) and + ((FDateFormatSample <> '') or (FTimeFormatSample <> '')); +end; + +function TFormatSettingsForm.GetFormatSettings: TFormatSettings; +begin + Result := DefaultFormatSettings; + + // --- Number format parameters -- + // Decimal separator + Result.DecimalSeparator := FCbDecimalSeparator.Separator; + // Thousand separator + Result.ThousandSeparator := FCbThousandSeparator.Separator; + + // --- Currency format parameters --- + // Currency symbol + Result.CurrencyString := UTF8ToAnsi(EdCurrencySymbol.Text); + // Currency decimal places + Result.CurrencyDecimals := EdCurrencyDecimals.Value; + // Positive currency format + Result.CurrencyFormat := CbPosCurrencyFormat.ItemIndex; + // Negative currency format + Result.NegCurrFormat := CbNegCurrencyFormat.ItemIndex; + + // --- Date format parameters --- + // Long date format string + Result.LongDateFormat := CbLongDateFormat.Text; + // Short date format string + Result.ShortDateFormat := CbShortDateFormat.Text; + // Date separator + Result.DateSeparator := FCbDateSeparator.Separator; + // Long month names + FEdLongMonthNames.GetNames(Result.LongMonthNames); + // Short month names + FEdShortMonthNames.GetNames(Result.ShortMonthNames); + // Long day names + FEdLongDayNames.GetNames(Result.LongDayNames); + // Short day names + FEdShortDayNames.GetNames(Result.ShortDayNames); + + // --- Time format parameters --- + // Long time format string + Result.LongTimeFormat := CbLongTimeFormat.Text; + // Short time format string + Result.ShortTimeFormat := CbShortTimeFormat.Text; + // Time separator + Result.TimeSeparator := FCbTimeSeparator.Separator; +end; + +procedure TFormatSettingsForm.SetFormatSettings(const AValue: TFormatSettings); +var + i: Integer; +begin + // --- Number format parameters --- + FCbDecimalSeparator.Separator := AValue.DecimalSeparator; + FCbThousandSeparator.Separator := AValue.ThousandSeparator; + + // --- Currency format parameters --- + // Currency symbol + EdCurrencySymbol.Text := AnsiToUTF8(AValue.CurrencyString); + // Currency decimal places + EdCurrencyDecimals.Value := AValue.CurrencyDecimals; + // Positive currency format + CbPosCurrencyFormat.ItemIndex := AValue.CurrencyFormat; + // Negative currency format + CbNegCurrencyFormat.ItemIndex := AValue.NegCurrFormat; + + // --- Date format parameters --- + // Long date format string + i := CbLongDateFormat.Items.IndexOf(AValue.LongDateFormat); + if i = -1 then + CbLongDateFormat.ItemIndex := CbLongDateFormat.Items.Add(AValue.LongDateFormat) + else + CbLongDateFormat.ItemIndex := i; + // Short date format string + i := CbShortDateFormat.Items.IndexOf(AValue.ShortDateFormat); + if i = -1 then + CbShortDateFormat.ItemIndex := CbShortDateFormat.items.Add(AValue.ShortDateFormat) + else + CbShortDateFormat.ItemIndex := i; + // Date separator + FCbDateSeparator.Separator := AValue.DateSeparator; + // Long month names + FEdLongMonthNames.SetNames(AValue.LongMonthNames, 12, false, 'Error'); + // Short month names + FEdShortMonthNames.SetNames(AValue.ShortMonthNames, 12, true, 'Error'); + // Long day names + FEdLongDayNames.SetNames(AValue.LongDayNames, 7, false, 'Error'); + // Short month names + FEdShortDayNames.SetNames(AValue.ShortDayNames, 7, true, 'Error'); + + // --- Time format parameters --- + + // Long time format string + i := CbLongTimeFormat.items.IndexOf(AValue.LongTimeFormat); + if i = -1 then + CbLongTimeFormat.ItemIndex := CbLongTimeFormat.Items.Add(AValue.LongTimeFormat) + else + CbLongTimeFormat.ItemIndex := i; + // Short time format string + i := cbShortTimeFormat.Items.IndexOf(AValue.ShortTimeFormat); + if i = -1 then + CbShortTimeFormat.itemIndex := CbShortTimeFormat.Items.Add(AValue.ShortTimeFormat); + // Time separator + FCbTimeSeparator.Separator := AValue.TimeSeparator; +end; + +function TFormatSettingsForm.ValidData(out AControl: TWinControl; + out AMsg: String): Boolean; +begin + Result := false; + if FCbDecimalSeparator.Separator = FCbThousandSeparator.Separator then + begin + AControl := FCbDecimalSeparator; + AMsg := 'Decimal and thousand separators cannot be the same.'; + exit; + end; + Result := true; +end; + +initialization + {$I sformatsettingsform.lrs} + +end. + diff --git a/components/fpspreadsheet/examples/spready/spready.lpi b/components/fpspreadsheet/examples/spready/spready.lpi index 8e8e4e982..e16146a05 100644 --- a/components/fpspreadsheet/examples/spready/spready.lpi +++ b/components/fpspreadsheet/examples/spready/spready.lpi @@ -28,11 +28,6 @@ - - - - - @@ -97,7 +92,7 @@ - + @@ -110,6 +105,27 @@ + + + + + + + + + + + + + + + + + + + + + @@ -123,11 +139,6 @@ - - - - - diff --git a/components/fpspreadsheet/examples/spready/spready.lpr b/components/fpspreadsheet/examples/spready/spready.lpr index 47f53bac9..dc9365b38 100644 --- a/components/fpspreadsheet/examples/spready/spready.lpr +++ b/components/fpspreadsheet/examples/spready/spready.lpr @@ -4,7 +4,8 @@ program spready; uses Interfaces, // this includes the LCL widgetset - Forms, mainform, laz_fpspreadsheet_visual; + Forms, mainform, laz_fpspreadsheet_visual, +sCSVParamsForm, sCtrls, sFormatSettingsForm; {$R *.res} @@ -12,6 +13,7 @@ begin Application.Initialize; Application.CreateForm(TMainFrm, MainFrm); MainFrm.BeforeRun; + Application.CreateForm(TFormatSettingsForm, FormatSettingsForm); Application.Run; end. diff --git a/components/fpspreadsheet/fpscsv.pas b/components/fpspreadsheet/fpscsv.pas index cc2fc6d0f..4a1f57bac 100644 --- a/components/fpspreadsheet/fpscsv.pas +++ b/components/fpspreadsheet/fpscsv.pas @@ -14,7 +14,8 @@ type FWorksheetName: String; function IsBool(AText: String; out AValue: Boolean): Boolean; function IsDateTime(AText: String; out ADateTime: TDateTime): Boolean; - function IsNumber(AText: String; out ANumber: Double): Boolean; + function IsNumber(AText: String; out ANumber: Double; + out ACurrencySymbol, AWarning: String): Boolean; function IsQuotedText(var AText: String): Boolean; procedure ReadCellValue(ARow, ACol: Cardinal; AText: String); protected @@ -60,9 +61,9 @@ type LineEnding: TsCSVLineEnding; // W: Specification for line ending to be written Delimiter: Char; // RW: Column delimiter QuoteChar: Char; // RW: Character for quoting texts + DetectContentType: Boolean; // R: try to convert strings to content types NumberFormat: String; // W: if empty write numbers like in sheet, otherwise use this format - DateTimeAsText: Boolean; // R: if false tries to convert text to date/time values - BoolAsText: Boolean; // R: if false tries to convert text to boolean values + AutoDetectNumberFormat: Boolean; // R: automatically detects decimal/thousand separator used in numbers TrueText: String; // RW: String for boolean TRUE FalseText: String; // RW: String for boolean FALSE FormatSettings: TFormatSettings; // RW: add'l parameters for conversion @@ -74,9 +75,9 @@ var LineEnding: leSystem; Delimiter: ';'; QuoteChar: '"'; + DetectContentType: true; NumberFormat: ''; - DateTimeAsText: false; - BoolAsText: false; + AutoDetectNumberFormat: true; TrueText: 'TRUE'; FalseText: 'FALSE'; ); @@ -205,9 +206,40 @@ begin Result := TryStrToDateTime(AText, ADateTime, CSVParams.FormatSettings); end; -function TsCSVReader.IsNumber(AText: String; out ANumber: Double): Boolean; +function TsCSVReader.IsNumber(AText: String; out ANumber: Double; + out ACurrencySymbol, AWarning: String): Boolean; +var + p: Integer; begin - Result := TryStrToFloat(AText, ANumber, CSVParams.FormatSettings); + AWarning := ''; + + // To detect whether the text is a currency value we look for the currency + // string. If we find it, we delete it and convert the remaining string to + // a number. + ACurrencySymbol := IfThen(CSVParams.FormatSettings.CurrencyString = '', + FWorkbook.FormatSettings.CurrencyString, + CSVParams.FormatSettings.CurrencyString); + p := pos(ACurrencySymbol, AText); + if p > 0 then begin + Delete(AText, p, Length(ACurrencySymbol)); + AText := Trim(AText); + if AText = '' then begin + Result := false; + ACurrencySymbol := ''; + exit; + end; + // Negative financial values are often enclosed by parenthesis + if ((AText[1] = '(') and (AText[Length(AText)] = ')')) then + AText := '-' + Trim(Copy(AText, 2, Length(AText)-2)); + end else + ACurrencySymbol := ''; + + if CSVParams.AutoDetectNumberFormat then + Result := TryStrToFloatAuto(AText, ANumber, AWarning) + else + Result := TryStrToFloat(AText, ANumber, CSVParams.FormatSettings); + + if not Result then ACurrencySymbol := ''; end; function TsCSVReader.IsQuotedText(var AText: String): Boolean; @@ -233,34 +265,54 @@ var dblValue: Double; dtValue: TDateTime; boolValue: Boolean; + currSym: string; + warning: String; begin // Empty strings are blank cells -- nothing to do if AText = '' then exit; + // Do not try to interpret the strings. --> everything is a LABEL cell. + if not CSVParams.DetectContentType then + begin + FWorksheet.WriteUTF8Text(ARow, aCol, AText); + exit; + end; + + // Remove quotes + if (AText[1] = CSVParams.QuoteChar) and (AText[Length(AText)] = CSVParams.QuoteChar) then + Delete(AText, 2, Length(AText)-2); + + { // Quoted text is a TEXT cell if IsQuotedText(AText) then begin FWorksheet.WriteUTF8Text(ARow, ACol, AText); exit; end; + } - // Check for a NUMBER cell - if IsNumber(AText, dblValue) then + // Check for a NUMBER or CURRENCY cell + if IsNumber(AText, dblValue, currSym, warning) then begin - FWorksheet.WriteNumber(ARow, ACol, dblValue); + if currSym <> '' then + FWorksheet.WriteCurrency(ARow, ACol, dblValue, nfCurrency, 2, currSym) + else + FWorksheet.WriteNumber(ARow, ACol, dblValue); + if warning <> '' then + FWorkbook.AddErrorMsg('Cell %s: %s', [GetCellString(ARow, ACol), warning]); exit; end; // Check for a DATE/TIME cell - if not CSVParams.DateTimeAsText and IsDateTime(AText, dtValue) then + if IsDateTime(AText, dtValue) then begin FWorksheet.WriteDateTime(ARow, ACol, dtValue); exit; end; // Check for a BOOLEAN cell - if not CSVParams.BoolAsText and IsBool(AText, boolValue) then + if IsBool(AText, boolValue) then begin FWorksheet.WriteBoolValue(ARow, aCol, boolValue); exit; diff --git a/components/fpspreadsheet/fpspreadsheetgrid.pas b/components/fpspreadsheet/fpspreadsheetgrid.pas index 9c1d95dd0..92be0beda 100644 --- a/components/fpspreadsheet/fpspreadsheetgrid.pas +++ b/components/fpspreadsheet/fpspreadsheetgrid.pas @@ -315,7 +315,7 @@ type TsWorksheetGrid is a grid which displays spreadsheet data along with formatting. As it is linked to an instance of TsWorkbook, it provides methods for reading data from or writing to spreadsheet files. It has the - same funtionality as TsCustomWorksheetGrid, but publishes has all properties. + same funtionality as TsCustomWorksheetGrid, but has published all properties. } TsWorksheetGrid = class(TsCustomWorksheetGrid) published @@ -2242,6 +2242,25 @@ var fnt: TsFont; begin Result := nil; + if (FWorkbook <> nil) then + begin + fnt := FWorkbook.GetDefaultFont; + if FWorksheet <> nil then + begin + cell := FWorksheet.FindCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol)); + if (cell <> nil) then + begin + if (uffBold in cell^.UsedFormattingFields) then + fnt := FWorkbook.GetFont(1) + else + if (uffFont in cell^.UsedFormattingFields) then + fnt := FWorkbook.GetFont(cell^.FontIndex); + end; + end; + Convert_sFont_to_Font(fnt, FCellFont); + Result := FCellFont; + end; + { if (FWorkbook <> nil) and (FWorksheet <> nil) then begin cell := FWorksheet.FindCell(GetWorksheetRow(ARow), GetWorksheetCol(ACol)); @@ -2254,11 +2273,17 @@ begin fnt := FWorkbook.GetFont(cell^.FontIndex) else fnt := FWorkbook.GetDefaultFont; -// fnt := FWorkbook.GetFont(cell^.FontIndex); Convert_sFont_to_Font(fnt, FCellFont); Result := FCellFont; end; end; + if Result = nil then + begin + fnt := FWorkbook.GetDefaultFont; + Convert_sFont_to_Font(fnt, FCellFont); + Result := FCellFont; + end; + } end; {@@ ---------------------------------------------------------------------------- @@ -2276,7 +2301,7 @@ var cell: PCell; begin Result := GetCellFont(ARect.Left, ARect.Top); - sDefFont := FWorkbook.GetFont(0); // Default font + sDefFont := FWorkbook.GetDefaultFont; // Default font for c := ARect.Left to ARect.Right do for r := ARect.Top to ARect.Bottom do begin diff --git a/components/fpspreadsheet/fpsstrings.pas b/components/fpspreadsheet/fpsstrings.pas index 37c124481..75cf9c818 100644 --- a/components/fpspreadsheet/fpsstrings.pas +++ b/components/fpspreadsheet/fpsstrings.pas @@ -48,7 +48,7 @@ resourcestring rsUTF8TextExpectedButANSIFoundInCell = 'Expected UTF8 text but probably ANSI '+ 'text found in cell %s.'; rsIndexInSSTOutOfRange = 'Index %d in SST out of range (0-%d).'; - + rsAmbiguousDecThouSeparator = 'Assuming usage of decimal separator in "%s".'; rsTRUE = 'TRUE'; // wp: Do we really want to translate these strings? diff --git a/components/fpspreadsheet/fpsutils.pas b/components/fpspreadsheet/fpsutils.pas index 32e9a3d93..a18469786 100644 --- a/components/fpspreadsheet/fpsutils.pas +++ b/components/fpspreadsheet/fpsutils.pas @@ -93,6 +93,8 @@ function IsDateTimeFormat(AFormatStr: String): Boolean; overload; function IsTimeFormat(AFormat: TsNumberFormat): Boolean; overload; function IsTimeFormat(AFormatStr: String): Boolean; overload; +procedure BuildCurrencyFormatList(AList: TStrings; + APositive: Boolean; AValue: Double; const ACurrencySymbol: String); function BuildCurrencyFormatString(ADialect: TsNumFormatDialect; ANumberFormat: TsNumberFormat; const AFormatSettings: TFormatSettings; ADecimals, APosCurrFormat, ANegCurrFormat: Integer; ACurrencySymbol: String): String; @@ -106,13 +108,19 @@ function AddAMPM(const ATimeFormatString: String; function StripAMPM(const ATimeFormatString: String): String; function CountDecs(AFormatString: String; ADecChars: TsDecsChars = ['0']): Byte; function AddIntervalBrackets(AFormatString: String): String; +function DayNamesToString(const ADayNames: TWeekNameArray; + const AEmptyStr: String): String; function MakeLongDateFormat(ADateFormat: String): String; function MakeShortDateFormat(ADateFormat: String): String; +function MonthNamesToString(const AMonthNames: TMonthNameArray; + const AEmptyStr: String): String; function SpecialDateTimeFormat(ACode: String; const AFormatSettings: TFormatSettings; ForWriting: Boolean): String; procedure SplitFormatString(const AFormatString: String; out APositivePart, ANegativePart, AZeroPart: String); + + procedure MakeTimeIntervalMask(Src: String; var Dest: String); // These two functions are copies of fpc trunk until they are available in stable fpc. @@ -121,6 +129,9 @@ function FormatDateTime(const FormatStr: string; DateTime: TDateTime; function FormatDateTime(const FormatStr: string; DateTime: TDateTime; const FormatSettings: TFormatSettings; Options : TFormatDateTimeOptions = []): string; +function TryStrToFloatAuto(AText: String; out ANumber: Double; + out AWarning: String): Boolean; + function TwipsToPts(AValue: Integer): Single; function PtsToTwips(AValue: Single): Integer; function cmToPts(AValue: Double): Double; @@ -162,11 +173,38 @@ var implementation uses - Math, lazutf8; + Math, lazutf8, fpsStrings; type TRGBA = record r, g, b, a: byte end; +const + POS_CURR_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_CURR_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 $) + ); + {******************************************************************************} { Endianess helper functions } {******************************************************************************} @@ -864,6 +902,49 @@ begin end; end; +{@@ ---------------------------------------------------------------------------- + Builds a string list with samples of the predefined currency formats + + @param AList String list in which the format samples are stored + @param APositive If true, samples are built for positive currency + values, otherwise for negative values + @param AValue Currency value to be used when calculating the sample + strings + @param ACurrencySymbol Currency symbol string to be used in the samples +-------------------------------------------------------------------------------} +procedure BuildCurrencyFormatList(AList: TStrings; + APositive: Boolean; AValue: Double; const ACurrencySymbol: String); +var + valueStr: String; + i: Integer; + sel: Integer; +begin + valueStr := Format('%.0n', [AValue]); + AList.BeginUpdate; + try + if AList.Count = 0 then + begin + if APositive then + for i:=0 to High(POS_CURR_FMT) do + AList.Add(Format(POS_CURR_FMT[i], [valueStr, ACurrencySymbol])) + else + for i:=0 to High(NEG_CURR_FMT) do + AList.Add(Format(NEG_CURR_FMT[i], [valueStr, ACurrencySymbol])); + end else + begin + if APositive then + for i:=0 to High(POS_CURR_FMT) do + AList[i] := Format(POS_CURR_FMT[i], [valueStr, ACurrencySymbol]) + else + for i:=0 to High(NEG_CURR_FMT) do + AList[i] := Format(NEG_CURR_FMT[i], [valueStr, ACurrencySymbol]); + end; + finally + AList.EndUpdate; + end; +end; + + {@@ ---------------------------------------------------------------------------- Builds a currency format string. The presentation of negative values (brackets, or minus signs) is taken from the provided format settings. The format string @@ -893,6 +974,7 @@ end; function BuildCurrencyFormatString(ADialect: TsNumFormatDialect; ANumberFormat: TsNumberFormat; const AFormatSettings: TFormatSettings; ADecimals, APosCurrFormat, ANegCurrFormat: Integer; ACurrencySymbol: String): String; +{ const POS_FMT: array[0..3] of string = ( // Format parameter 0 is "value", parameter 1 is "currency symbol" @@ -919,6 +1001,7 @@ const ('("%1:s" %0:s)'), // 14: ($ 1) ('(%0:s "%1:s")') // 15: (1 $) ); + } var decs: String; pcf, ncf: Byte; @@ -930,13 +1013,15 @@ begin if (ADecimals < 0) then ADecimals := AFormatSettings.CurrencyDecimals; if ACurrencySymbol = '?' then - ACurrencySymbol := AnsiToUTF8(AFormatSettings.CurrencyString); + ACurrencySymbol := AnsiToUTF8(AFormatSettings.CurrencyString); // is this correct? fpspreadsheet should be kept clean of string conversions! + if ACurrencySymbol <> '' then + ACurrencySymbol := '"' + ACurrencySymbol + '"'; decs := DupeString('0', ADecimals); if ADecimals > 0 then decs := '.' + decs; negRed := (ANumberFormat = nfCurrencyRed); - p := POS_FMT[pcf]; // Format mask for positive values - n := NEG_FMT[ncf]; // Format mask for negative values + p := POS_CURR_FMT[pcf]; // Format mask for positive values + n := NEG_CURR_FMT[ncf]; // Format mask for negative values // add extra space for the sign of the number for perfect alignment in Excel if ADialect = nfdExcel then case ncf of @@ -1110,6 +1195,39 @@ begin end; end; +{@@ ---------------------------------------------------------------------------- + Concatenates the day names specified in ADayNames to a single string. If all + daynames are empty AEmptyStr is returned + + @param ADayNames Array[1..7] of day names as used in the Formatsettings + @param AEmptyStr Is returned if all day names are empty + @return String having all day names concatenated and separated by the + DefaultFormatSettings.ListSeparator +-------------------------------------------------------------------------------} +function DayNamesToString(const ADayNames: TWeekNameArray; + const AEmptyStr: String): String; +var + i: Integer; + isEmpty: Boolean; +begin + isEmpty := true; + for i:=1 to 7 do + if ADayNames[i] <> '' then + begin + isEmpty := false; + break; + end; + + if isEmpty then + Result := AEmptyStr + else + begin + Result := ADayNames[1]; + for i:=2 to 7 do + Result := Result + DefaultFormatSettings.ListSeparator + ' ' + ADayNames[i]; + end; +end; + {@@ ---------------------------------------------------------------------------- Creates a long date format string out of a short date format string. Retains the order of year-month-day and the separators, but uses 4 digits @@ -1181,6 +1299,39 @@ begin end; end; +{@@ ---------------------------------------------------------------------------- + Concatenates the month names specified in AMonthNames to a single string. + If all month names are empty AEmptyStr is returned + + @param AMonthNames Array[1..12] of month names as used in the Formatsettings + @param AEmptyStr Is returned if all month names are empty + @return String having all month names concatenated and separated by the + DefaultFormatSettings.ListSeparator +-------------------------------------------------------------------------------} +function MonthNamesToString(const AMonthNames: TMonthNameArray; + const AEmptyStr: String): String; +var + i: Integer; + isEmpty: Boolean; +begin + isEmpty := true; + for i:=1 to 12 do + if AMonthNames[i] <> '' then + begin + isEmpty := false; + break; + end; + + if isEmpty then + Result := AEmptyStr + else + begin + Result := AMonthNames[1]; + for i:=2 to 12 do + Result := Result + DefaultFormatSettings.ListSeparator + ' ' + AMonthNames[i]; + end; +end; + {@@ ---------------------------------------------------------------------------- Creates the formatstrings for the date/time codes "dm", "my", "ms" and "msz" out of the formatsettings. @@ -1325,6 +1476,115 @@ begin end; end; +{@@ ---------------------------------------------------------------------------- + Converts a string to a floating point number. No assumption on decimal and + thousand separator are made. + Is needed for reading CSV files. +-------------------------------------------------------------------------------} +function TryStrToFloatAuto(AText: String; out ANumber: Double; + out AWarning: String): Boolean; +var + i, j: Integer; + testSep: Char; + testSepPos: Integer; + decsep: Char; + isPercent: Boolean; + fs: TFormatSettings; + done: Boolean; +begin + Result := false; + AWarning := ''; + if AText = '' then + exit; + + fs := DefaultFormatSettings; + + // We scan the string starting from its end. If we find a point or a comma, + // we have a candidate for the decimal or thousand separator. If we find + // the same character again it was a thousand separator, if not it was + // a decimal separator. + + // There is one amgiguity: Using a thousand separator for number < 1.000.000, + // but no decimal separator misinterprets the thousand separator as a + // decimal separator. + + done := false; // Indicates that both decimal and thousand separators are found + testSep := #0; // Separator candidate to be tested + testSepPos := 0; // Position of this separator chandidate in the string + i := Length(AText); // Start at end... + while i >= 1 do // ...and search towards start + begin + if AText[i] in ['.', ','] then + begin + if testSep = #0 then begin + testSep := AText[i]; + testSepPos := i; + end; + // This is the right-most separator candidate in the text + // It can be a decimal or a thousand separator. + dec(i); + while i >= 1 do + begin + // If we find the testSep character again it must be a thousand separator. + if AText[i] = testSep then + begin + fs.ThousandSeparator := testSep; + // The decimal separator is the "other" character. + if testSep = '.' then + fs.DecimalSeparator := ',' + else + fs.DecimalSeparator := '.'; + done := true; + i := 0; + end + else + // If we find the "other" separator character, then testSep was a + // decimal separator and the current character is a thousand separator. + if AText[i] in ['.',','] then + begin + fs.DecimalSeparator := testSep; + fs.ThousandSeparator := AText[i]; + done := true; + i := 0; + end; + dec(i); + end; + end; + dec(i); + end; + + // Only one separator candicate found, we assume it is a decimal separator + if (testSep <> #0) and not done then + begin + // Warning in case of ambiguous detection of separator. If only one separator + // type is found and it is at the third position from the string's end it + // might by a thousand separator or a decimal separator. We assume the + // latter case, but create a warning. + if Length(AText) - testSepPos = 3 then + AWarning := Format(rsAmbiguousDecThouSeparator, [AText]); + fs.DecimalSeparator := testSep; + // Make sure that the thousand separator is different from the decimal sep. + if testSep = '.' then fs.ThousandSeparator := ',' else fs.ThousandSeparator := '.'; + end; + + // Delete all thousand separators from the string - StrToFloat does not like them... + AText := StringReplace(AText, fs.ThousandSeparator, '', [rfReplaceAll]); + + // Is the last character a percent sign? + isPercent := AText[Length(AText)] = '%'; + if isPercent then + while (Length(AText) > 0) and (AText[Length(AText)] in ['%', ' ']) do + Delete(AText, Length(AText), 1); + + // Try string-to-number conversion + Result := TryStrToFloat(AText, ANumber, fs); + + // If successful take care of the percentage sign + if Result and isPercent then + ANumber := ANumber * 0.01; +end; + + {@@ ---------------------------------------------------------------------------- Excel's unit of row heights is "twips", i.e. 1/20 point. Converts Twips to points.