From 6ffccb0884ad299ad4ac17e72ecc8bbd73fad568 Mon Sep 17 00:00:00 2001 From: wp_xxyyzz Date: Fri, 22 May 2020 08:12:36 +0000 Subject: [PATCH] exctrls: Initial commit. git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@7464 8e941d3f-bd1b-0410-a28a-d453659cc2b4 --- .../exctrls/examples/CheckCtrlsEx/demo.lpi | 80 + .../exctrls/examples/CheckCtrlsEx/demo.lpr | 25 + .../exctrls/examples/CheckCtrlsEx/main.lfm | 314 +++ .../exctrls/examples/CheckCtrlsEx/main.pas | 73 + .../examples/CurrSpinEditEx/InterestCalc.lpi | 83 + .../examples/CurrSpinEditEx/InterestCalc.lpr | 25 + .../exctrls/examples/CurrSpinEditEx/main.lfm | 156 ++ .../exctrls/examples/CurrSpinEditEx/main.pas | 48 + components/exctrls/exctrlspkg.lpk | 51 + components/exctrls/exctrlspkg.pas | 22 + components/exctrls/images/imagelist.txt | 15 + components/exctrls/images/make_res.bat | 1 + components/exctrls/images/tcheckboxex.png | Bin 0 -> 695 bytes components/exctrls/images/tcheckboxex_150.png | Bin 0 -> 776 bytes components/exctrls/images/tcheckboxex_200.png | Bin 0 -> 957 bytes components/exctrls/images/tcheckgroupex.png | Bin 0 -> 754 bytes .../exctrls/images/tcheckgroupex_150.png | Bin 0 -> 672 bytes .../exctrls/images/tcheckgroupex_200.png | Bin 0 -> 879 bytes components/exctrls/images/tcurrspineditex.png | Bin 0 -> 759 bytes .../exctrls/images/tcurrspineditex_150.png | Bin 0 -> 1133 bytes .../exctrls/images/tcurrspineditex_200.png | Bin 0 -> 1428 bytes components/exctrls/images/tradiobuttonex.png | Bin 0 -> 768 bytes .../exctrls/images/tradiobuttonex_150.png | Bin 0 -> 1175 bytes .../exctrls/images/tradiobuttonex_200.png | Bin 0 -> 1491 bytes components/exctrls/images/tradiogroupex.png | Bin 0 -> 670 bytes .../exctrls/images/tradiogroupex_150.png | Bin 0 -> 785 bytes .../exctrls/images/tradiogroupex_200.png | Bin 0 -> 923 bytes components/exctrls/source/checkctrlsreg.res | Bin 0 -> 14848 bytes components/exctrls/source/excheckctrls.pas | 2177 +++++++++++++++++ components/exctrls/source/exctrlsreg.pas | 31 + components/exctrls/source/exctrlsreg.res | Bin 0 -> 14848 bytes components/exctrls/source/exeditctrls.pas | 404 +++ 32 files changed, 3505 insertions(+) create mode 100644 components/exctrls/examples/CheckCtrlsEx/demo.lpi create mode 100644 components/exctrls/examples/CheckCtrlsEx/demo.lpr create mode 100644 components/exctrls/examples/CheckCtrlsEx/main.lfm create mode 100644 components/exctrls/examples/CheckCtrlsEx/main.pas create mode 100644 components/exctrls/examples/CurrSpinEditEx/InterestCalc.lpi create mode 100644 components/exctrls/examples/CurrSpinEditEx/InterestCalc.lpr create mode 100644 components/exctrls/examples/CurrSpinEditEx/main.lfm create mode 100644 components/exctrls/examples/CurrSpinEditEx/main.pas create mode 100644 components/exctrls/exctrlspkg.lpk create mode 100644 components/exctrls/exctrlspkg.pas create mode 100644 components/exctrls/images/imagelist.txt create mode 100644 components/exctrls/images/make_res.bat create mode 100644 components/exctrls/images/tcheckboxex.png create mode 100644 components/exctrls/images/tcheckboxex_150.png create mode 100644 components/exctrls/images/tcheckboxex_200.png create mode 100644 components/exctrls/images/tcheckgroupex.png create mode 100644 components/exctrls/images/tcheckgroupex_150.png create mode 100644 components/exctrls/images/tcheckgroupex_200.png create mode 100644 components/exctrls/images/tcurrspineditex.png create mode 100644 components/exctrls/images/tcurrspineditex_150.png create mode 100644 components/exctrls/images/tcurrspineditex_200.png create mode 100644 components/exctrls/images/tradiobuttonex.png create mode 100644 components/exctrls/images/tradiobuttonex_150.png create mode 100644 components/exctrls/images/tradiobuttonex_200.png create mode 100644 components/exctrls/images/tradiogroupex.png create mode 100644 components/exctrls/images/tradiogroupex_150.png create mode 100644 components/exctrls/images/tradiogroupex_200.png create mode 100644 components/exctrls/source/checkctrlsreg.res create mode 100644 components/exctrls/source/excheckctrls.pas create mode 100644 components/exctrls/source/exctrlsreg.pas create mode 100644 components/exctrls/source/exctrlsreg.res create mode 100644 components/exctrls/source/exeditctrls.pas diff --git a/components/exctrls/examples/CheckCtrlsEx/demo.lpi b/components/exctrls/examples/CheckCtrlsEx/demo.lpi new file mode 100644 index 000000000..bdbf8ad31 --- /dev/null +++ b/components/exctrls/examples/CheckCtrlsEx/demo.lpi @@ -0,0 +1,80 @@ + + + + + + + + + <Scaled Value="True"/> + <ResourceType Value="res"/> + <UseXPManifest Value="True"/> + <XPManifest> + <DpiAware Value="True"/> + </XPManifest> + <Icon Value="0"/> + </General> + <BuildModes> + <Item Name="Default" Default="True"/> + </BuildModes> + <PublishOptions> + <Version Value="2"/> + <UseFileFilters Value="True"/> + </PublishOptions> + <RunParams> + <FormatVersion Value="2"/> + </RunParams> + <RequiredPackages> + <Item> + <PackageName Value="ExCtrlsPkg"/> + </Item> + <Item> + <PackageName Value="LCL"/> + </Item> + </RequiredPackages> + <Units> + <Unit> + <Filename Value="demo.lpr"/> + <IsPartOfProject Value="True"/> + </Unit> + <Unit> + <Filename Value="main.pas"/> + <IsPartOfProject Value="True"/> + <ComponentName Value="MainForm"/> + <HasResources Value="True"/> + <ResourceBaseClass Value="Form"/> + </Unit> + </Units> + </ProjectOptions> + <CompilerOptions> + <Version Value="11"/> + <PathDelim Value="\"/> + <Target> + <Filename Value="demo"/> + </Target> + <SearchPaths> + <IncludeFiles Value="$(ProjOutDir)"/> + <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/> + </SearchPaths> + <Linking> + <Options> + <Win32> + <GraphicApplication Value="True"/> + </Win32> + </Options> + </Linking> + </CompilerOptions> + <Debugging> + <Exceptions> + <Item> + <Name Value="EAbort"/> + </Item> + <Item> + <Name Value="ECodetoolError"/> + </Item> + <Item> + <Name Value="EFOpenError"/> + </Item> + </Exceptions> + </Debugging> +</CONFIG> diff --git a/components/exctrls/examples/CheckCtrlsEx/demo.lpr b/components/exctrls/examples/CheckCtrlsEx/demo.lpr new file mode 100644 index 000000000..5e4f6d42b --- /dev/null +++ b/components/exctrls/examples/CheckCtrlsEx/demo.lpr @@ -0,0 +1,25 @@ +program demo; + +{$mode objfpc}{$H+} + +uses + {$IFDEF UNIX} + cthreads, + {$ENDIF} + {$IFDEF HASAMIGA} + athreads, + {$ENDIF} + Interfaces, // this includes the LCL widgetset + Forms, main + { you can add units after this }; + +{$R *.res} + +begin + RequireDerivedFormResource:=True; + Application.Scaled:=True; + Application.Initialize; + Application.CreateForm(TMainForm, MainForm); + Application.Run; +end. + diff --git a/components/exctrls/examples/CheckCtrlsEx/main.lfm b/components/exctrls/examples/CheckCtrlsEx/main.lfm new file mode 100644 index 000000000..4302a978b --- /dev/null +++ b/components/exctrls/examples/CheckCtrlsEx/main.lfm @@ -0,0 +1,314 @@ +object MainForm: TMainForm + Left = 353 + Height = 295 + Top = 132 + Width = 735 + Caption = 'Extended Check Controls Demo' + ClientHeight = 295 + ClientWidth = 735 + OnCreate = FormCreate + LCLVersion = '2.1.0.0' + object CheckBoxEx1: TCheckBoxEx + AnchorSideLeft.Control = Label1 + AnchorSideTop.Control = Label1 + AnchorSideTop.Side = asrBottom + Left = 16 + Height = 19 + Top = 35 + Width = 412 + BorderSpacing.Top = 4 + Caption = 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy' + TabOrder = 0 + TabStop = True + end + object Label1: TLabel + AnchorSideLeft.Control = Owner + AnchorSideTop.Control = Owner + Left = 16 + Height = 15 + Top = 16 + Width = 142 + BorderSpacing.Left = 16 + BorderSpacing.Top = 16 + Caption = 'AutoSize + no WordWrap' + Font.Style = [fsBold] + ParentColor = False + ParentFont = False + end + object Label2: TLabel + AnchorSideLeft.Control = CheckBoxEx1 + AnchorSideTop.Control = CheckBoxEx1 + AnchorSideTop.Side = asrBottom + Left = 16 + Height = 15 + Top = 78 + Width = 125 + BorderSpacing.Top = 24 + Caption = 'AutoSize + WordWrap' + Font.Style = [fsBold] + ParentColor = False + ParentFont = False + end + object CheckBoxEx2: TCheckBoxEx + AnchorSideLeft.Control = CheckBoxEx1 + AnchorSideTop.Control = Label2 + AnchorSideTop.Side = asrBottom + Left = 16 + Height = 49 + Top = 97 + Width = 172 + BorderSpacing.Top = 4 + Caption = 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy' + TabOrder = 1 + TabStop = True + WordWrap = True + end + object Label3: TLabel + AnchorSideLeft.Control = CheckBoxEx1 + AnchorSideTop.Control = CheckBoxEx2 + AnchorSideTop.Side = asrBottom + Left = 16 + Height = 15 + Top = 170 + Width = 199 + BorderSpacing.Top = 24 + Caption = 'AutoSize + WordWrap + Top Layout' + Font.Style = [fsBold] + ParentColor = False + ParentFont = False + end + object CheckBoxEx3: TCheckBoxEx + AnchorSideLeft.Control = CheckBoxEx1 + AnchorSideTop.Control = Label3 + AnchorSideTop.Side = asrBottom + Left = 16 + Height = 49 + Top = 189 + Width = 172 + BorderSpacing.Top = 4 + ButtonLayout = tlTop + Caption = 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy' + TabOrder = 2 + TabStop = True + WordWrap = True + end + object Label4: TLabel + AnchorSideTop.Control = Label2 + Left = 282 + Height = 15 + Top = 78 + Width = 171 + Caption = 'Item colors by code at runtime' + Font.Style = [fsBold] + ParentColor = False + ParentFont = False + end + object RadioGroupEx1: TRadioGroupEx + AnchorSideLeft.Control = Label4 + AnchorSideTop.Control = Label4 + AnchorSideTop.Side = asrBottom + Left = 282 + Height = 105 + Top = 101 + Width = 198 + BorderSpacing.Top = 8 + Caption = 'RadioGroupEx1' + ChildSizing.LeftRightSpacing = 6 + ChildSizing.EnlargeHorizontal = crsHomogenousChildResize + ChildSizing.EnlargeVertical = crsHomogenousChildResize + ChildSizing.ShrinkHorizontal = crsScaleChilds + ChildSizing.ShrinkVertical = crsScaleChilds + ChildSizing.Layout = cclLeftToRightThenTopToBottom + ChildSizing.ControlsPerLine = 1 + ItemIndex = 1 + Items.Strings = ( + 'This item is red.' + 'The second item is green.' + 'The third item is blue.' + 'The last item is disabled.' + ) + TabOrder = 3 + end + object Label5: TLabel + AnchorSideTop.Control = Label4 + Left = 536 + Height = 15 + Top = 78 + Width = 159 + Caption = 'User-provided button glyphs' + Font.Style = [fsBold] + ParentColor = False + ParentFont = False + end + object RadioButtonEx1: TRadioButtonEx + Left = 536 + Height = 19 + Top = 108 + Width = 83 + Caption = 'First option' + Checked = True + Images = ImageList1 + State = cbChecked + TabOrder = 4 + TabStop = True + OnGetImageIndex = RadioButtonEx1GetImageIndex + end + object RadioButtonEx2: TRadioButtonEx + Left = 536 + Height = 19 + Top = 137 + Width = 100 + Caption = 'Second option' + Images = ImageList1 + TabOrder = 5 + TabStop = True + OnGetImageIndex = RadioButtonEx1GetImageIndex + end + object RadioButtonEx3: TRadioButtonEx + Left = 536 + Height = 19 + Top = 168 + Width = 88 + Caption = 'Third option' + Images = ImageList1 + TabOrder = 6 + TabStop = True + OnGetImageIndex = RadioButtonEx1GetImageIndex + end + object Label6: TLabel + AnchorSideLeft.Control = RadioGroupEx1 + AnchorSideTop.Control = RadioGroupEx1 + AnchorSideTop.Side = asrBottom + Left = 282 + Height = 15 + Top = 214 + Width = 34 + BorderSpacing.Top = 8 + Caption = 'Label6' + ParentColor = False + end + object ImageList1: TImageList + Scaled = True + left = 496 + top = 16 + Bitmap = { + 4C7A020000001000000010000000E60300000000000078DA8D935D6C536518C7 + DF33ED9D3743442643C39041748CB181C292F1D1CE2D6CDD706E6C7183D94264 + C446091B048D892E5E60480DEB5205D62E5E907061160C423B865AF6413B9833 + 284847BB6EDE80EB8A4E34125CA1F3EFF3BCDDE9DAD92DBEC92F7DCEF3FC7FEF + 7B4E4E8F10094BD9A03DE9341DBE85DDFBBDA837DD44EDBE9FB063CF0D54197F + 44F6C6CF9C9C11C917B9271CC76C21ACD0FE22C9D4455959C88CA1CE1440D6CB + 9F3A92ECA1ACDF7CDC613E1142567110D92541AC290D22671AAEB9B77ADB38AA + 1BFC7861BD357E0F25AFE0B8E3A316CAEA43C8A68CF5D45FF2775D794812EB95 + 8C23B7FC2EB61B7C58B52EBAC7C27443F3879FDCC14B15BF228F66EEC13078F5 + 0E84915B7657C2352F9E716643E56F28A9F562D1B3C6E6279718CC6F7F308E82 + EA096CAC9C4047E7DF50D7A52B6189BA78C6994D3513287A7D947D339D6F36BD + 3F0E6DED3D68EBEE21BFEA77CA4D62F6E21ECF38C3B0FFF47351BFE1DD208AEA + FF4431C1BFF9957F602C341573B9E69E9A9139D55F6A30BF793088D2DDF72545 + BBEEE3CAB5C87FCEE71ECF38A3275EA9F959FA4F2D359A8D8D637875EF03EA3F + 4870BFEC7A2451977B3022331594D555ABBEE1C84ED30876BC154645C3249CDD + 51DFD53F85B23D9392DEABD167E9A41967AA4D61E4EB6FB07F84DEE1E2557916 + 774DC3086ADF7984CA7D0FD1763A42FB3DA4EB285C73AF8A6675FB232828F722 + 23FB989BDDE9FF51FACADC16F76BC600DE689AC2AE0311D43726C23D43D33FD8 + B47D4875D3E3FF837CBD22A7C5ADDFE9C7DEF790942D15495D11BFC7F339164F + 9ECE8D5CED65ACDDD287359B7BB1BA204A46768B670E377E0F7EA6AC39583C8F + 2BFD4587F5CED2EE5694765B66D10A9ECD77F6C243DB9C65DE4E08A71DC2614B + 847A3CE34CB2675F40FD62AF03A28BDCAFDB9373B11DC5430E2C48DC4349A56B + EDD05710DF92EB6A9F13CD792B1E3F67C556CAA6AA7B142E6FDEEA3F07D14D6E + CFDC280E0BFAEE8CA29750CE5B201D72852EC3BCD64767F7D9A1B9609588CB76 + 792DA15A715AA4A7AEA3DFB990E327875CE645FF59A474B5CAFD19A5CB02D16F + 97701DEF72CDD9ACE1B3313F33700647075C0919E51B8B64B6CB3D316047E6F0 + 9998BF6CA4032997287B7B26DB737B44127369A6B8C8FD9EDEE5A00DCB021DD3 + FE72F392D12F207EB041E9493C2FE15C9A71465C8B221D72898F9F193E05719D + 9EF73AEDD197781FF25CEAF16C063BA4432EBDC3344D63A127D5F739C44D9B44 + 71D3BB228FE15AEDAB70961D76D5EFEEB1469DE7091FDD83AF0DE2561B34575B + 255CC7A01967389BECFB4F39A0F368FC272102B61986A7A19A679C99EFFB174D + 85FD4AE721A4380F26C03D9EFD8FEF3F6D9EEF3F6DB6FB2F39580CCE + } + BitmapAdv = { + 4C69010000004C7A020000002000000020000000430A00000000000078DACD97 + 795054D915C65F25A19C542A3553339615AD4A659BD1191718371050947D5790 + 551041C57D1F1DC744333571665CE2246A748406CC188D8995983F26A9718786 + 6E166D7014DC978C0BB6343474E3C6A6F0E59CFBBAE98547D3A07FE4557DF56E + DF7BCEF73BEFDEF76EBF27497D1E1EEFFA96D48FF029C1089F62BC3BA944E83D + 5F8BFC4A30D24EA3FC591A8C9E5C52CFB9D2CB1D1EEF4C28D2EFDD7F17855A13 + 0E1E35E2AB230DC83B6CC09707EAB03BFF21FE90ABC7B63DF7F1E91F6BF1F18E + 7BF8F5E777F0C98EFF227B753546FA16EB5FA2068FB789AD3A7017278ACC181E + F4003F9BF200BF08204D7D805FB2A6B1F4F8152B508FB703E5F388103DD67D72 + 1B59CB2FD29C0CA8866E76A1B619A342F5181EACC7C8308BC29535CA727E2FF4 + 2146853DC4AA4D3791B1E4427F6B1073BE27EF0E4EAA4D1813F150F88D897C08 + AF28F7E449B1A323EAE85C87A51B6E2075C179776BF07867A25AFFC597B7F1F5 + C926F2AAA36BAA13674F8BDE8FAEC3D81865F198354ECE31602CF52D587B0D49 + 5955745FBAACC163B8B75ABF6DD72DFCE36B23F9D5D3351BC8D38051742DDEF1 + 06216E8F8D3160FC7447719F73DCFB3106E1336E7A3D32575C415C46253D178A + 35788C20F6E6DFDFC0A1A3F59830A341E48DA73C4FAAC18BAEA3F971979017FD + E6BE0934E61D276B82352ED22E2ECA16C77E1367D4236DF16544A7EAF8D9B4AF + C183D6A661D396EB28385C079FB8068CA7786F3A8FE3DA490D8D9DB01E0DC64E + 9AD37A9A977A11C3E236F7F158771CE588FCD87AF8C4535CBC1193661A9138B7 + 066189E7E01950D260A9E18D11B49F1CFD8F1E3E331B3191E27C13E458CFA806 + 3C7E6AF3B41EF5E43D2196B831B2B85DDFD8338E73D983BDFC12C937B1097E09 + 4D48C8ACC298295A309B3484F7B6BFFDEB3E26C699E09FD88829C98DE23C2ED6 + 086D653B940E03F1C64619850C0A6C3E38973DAC9E53929B3039B919F1193AE2 + 6B983F44F0E9FA0F1EB98749096604A434616AAA2C3FAA774C841185656D8AFE + 77F59D424A07E7702E7B58FD5801A98F303DFDACF5FA059FF7F3FC4377E09FD4 + 8C69B34C084C93C56DFF2413CD61238ACA95E741E9E058CEE15C7BBFC0343302 + D31FD33D5841EBEFC8DFB7FF3BAAEF1182D3CD08996D13FF0E4831D3F3D084A2 + 8ABE6BE0188EE51C67AF90D9CD08C97882F024273EFD8FEDCAB945B53D416846 + 33C2E6388AFBA6A69AE97932E198BAA357368F710CC72AF984CD7984F0ACA708 + 4D2873E0F3FFE8F6DD37A9BEA708A798884C4785653CC2D49466BA7F9AF1A0EE + 45AF7C1EE3188EE51C679F88CCC7889CFB0C41339CF8F4BFFDD917D768BC0551 + 739F20DA4E91994F109C46F332FB311A9ABAFA9C7F8EE158CEE1DC6807BFA788 + 9DDF86809852073EBF377CBCE52AB15B11339F636445CF7B4AD7C173F2048DA6 + 2EB7EF3F8EE51CCE658FD86ECF6798B1B01D7E111A27BE061B7E7709B1D9ED98 + BE8062487C8ECC7A8628CAEF8D7D4AFB5C48B10673976047653DEBF69CB1A005 + F18B3BE01D5A02AFA9767C7A5F5ABDB106718B3A3073512BA905710B5B1031A7 + 050FEBBB7AD95B68AD139F09715BE9B87EBB93D68098E4C59E098B5B91B4EC05 + C606163BF0F97D6DD987D5485ADA8EC4252CAA8162A3693D74D53DBD4B89179C + D682E9D9AD42DC56AAA1B2A6537830973D9397B52365F9736233BFB49B3F7A72 + 91292EA30AF3565DA7F1768AEBA03ADB10BFA88DD6B015E5E76DDEA666D073DD + 4AF3D346F5CAE236CF9BC96C63730EE7CE248F64F24A5DD181D4E534F76195C4 + D6C02B406DB2F0F93F60E248BF53C6D8341D32975F45DAAA0ECC5AF19CE2692E + 16B7D31CB6A1AABA13CF5A40D7D1868445ED626CD60A59DC9EB9A85D8C710CC7 + 720EE7F278FAAAE7485BF91CDEE13A6297D0DE7BC6C84C0B9BFF038792FCB886 + C8D473F43F7D0519AB5F209D94B6B20329CBF8BEE8A075EC40F252AA8DFAD257 + 398AFB788C63383665B9DC9FB1E60566AFE924365D7700B34F33DBCFC2EC7E07 + B0FCF61DE97FCA189E7816C9F32F61CE079D42196B9E532DB26693F8B79266AF + B6C5F1EFCCB59DA42E67B6AF135B72AE6114D5101C5F81F8CC1ACC5DD785791F + 76216BED8B7E8973E6AD037C68BD3DFB662BD610105B8698F46A64AF07B23F02 + E6AFEF724BD91B20727C22FAC556A8E1B4D13FB214E1C917B1F837C0924D0AFA + ADACA576E2D801B27BD4307AF269A34F7829FCA80EDF082D268569E013AA8177 + 4831C6D33E326E9A9AEE693571D418ED5F44EF9645A2CDCFD800D9CE35F8794E + 519B79BFEE4DBC97F55080DAAC709F4B03AC819FD52052B09B0AB2E40C7D15DF + C0D6F7D47EEA8D57C096F9DBC31BA4AD2190B60643DA12E45A1CC3B19CF32AAE + FDF3E0BA59E70F635FDB5DEC6BF90EFB5AFB10C7502CE770EE4BAD3DE52755FD + 051F18BE85F4CD7E48FF56B9278AE51CCE1D600D821D5F75002B0D95908E13FB + 9B3C48C7F2DD13C7520EE7B2473F6B10ECE9BAAFB0D4A0837482D8C7C9EF6441 + FFC43994CB1EECE5660D821DADFB33161ACE413A45EC13E473AA60603A497371 + 5AF662CF3E6A10EC08DD7ECC3554403AC31EF9F279202AB4E80C7BC89EECDD4B + 0DC40EA90BD515604E5DB99C739AEEA3C2FC97900A83F76EC25B7B37925FAEE8 + CB30948319CC72F8EFDF1E6E0CAA2CC0AC3A7A2754D37811CDB93A7F602AE673 + 2E5EDBF551F7BBD8A05DEBC933478CA712239058CCECDEDBB68662D9637AC68A + 2DECE2FC81A984CFB918B47B7D8F7751EEE3BA98B19459C4ECDE5769BFCA7E44 + F77A91CAE6A321692DD258FAFA9432DB7ABCC96B51AC9259BC475AF76A6A6799 + E99E2B51C9BC5296BC7E2C6E8B3E6D2F1263B9629E5D1DE25ED0AA90D95CD183 + 9F6EA6FB4E93277B9539AE1FB7B94F2AB3D666A73239BE2FB6F0E0EBD0E621BD + B9DC891F8A44137D0F9411BF42E5C076B8872AA8860AE2955BC4ED8ABED9DDB9 + 9C438C2466C9EBDFCD8F336928260F6FEDD9E8DAE72CF99CCDB7C84DB635A742 + E6C7994A7AF0A31AD590742A97FC6EBFCA1C21B7D83A8AD5D1BC9EB3E8AC4A66 + 39F1431B0B055FAACC559C7FFB43DAB956C8257BA7B5CE3C996FD53995CC72E0 + 8761AAF114A42A1A3F4FFA3647E40FF4106CF2105E554EA27A048B98F67C3FE3 + 493B3EE9C2C06A106CCA151EE715440CC172E24F301EA7BC3C9B2E92AAFB5EE3 + 1EEB4D3922F742EF122C077E38BC1A8E41AA299073ADAA26D5B857836053ACC8 + B9E842C4102C62DAF1CD43CB77E0E7B547E51AAAF36CAA215D72BD1662CE2946 + C456BB1079338359CCB4FFFE973687340DD16EC34F6BFF09E932D57029CFA62B + 2CE51A049BC644CC2517224FF66606B39CBEFF87896F15EA7F53BB153FA93D02 + E92AED1597F36C62FFAB39786DA7DDBE4C6DEE1363975D88BCD893BD2D6C3F0B + D3F9FB5FD4F0BA760B06D7FE1DD235AAE1AACAA6EBA41B3918FCA74D42DC167D + 575D883CD88B3DEDD8AEBEFF450D3FD67E86D7EFD33BFC4DAAFF9ACA26E6DDB4 + E8BACA71CC5994CB1EECD5075BB1861F6A3EC58FEE1D82748B6AB8A1B2C9CABF + E14294C3B9ECE1265BB1060FCD660CAAA5EF88DBB416B7F3E45A5C8963289673 + 38B79F6C851A429BBEA7DD8CEFEBFF8A1FD41E744B1CCB399CFB12DFE0B61AB6 + 44344BDB22E43DC31D712CE7BC9AEFFF612FF1FD3FECFFFDFBFF7F3E2F3290 + } + end +end diff --git a/components/exctrls/examples/CheckCtrlsEx/main.pas b/components/exctrls/examples/CheckCtrlsEx/main.pas new file mode 100644 index 000000000..b977486dc --- /dev/null +++ b/components/exctrls/examples/CheckCtrlsEx/main.pas @@ -0,0 +1,73 @@ +unit main; + +{$mode objfpc}{$H+} + +interface + +uses + Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, ExtCtrls, + ExCheckCtrls; + +type + + { TMainForm } + + TMainForm = class(TForm) + CheckBoxEx1: TCheckBoxEx; + CheckBoxEx2: TCheckBoxEx; + CheckBoxEx3: TCheckBoxEx; + ImageList1: TImageList; + Label1: TLabel; + Label2: TLabel; + Label3: TLabel; + Label4: TLabel; + Label5: TLabel; + Label6: TLabel; + RadioButtonEx1: TRadioButtonEx; + RadioButtonEx2: TRadioButtonEx; + RadioButtonEx3: TRadioButtonEx; + RadioGroupEx1: TRadioGroupEx; + procedure CheckBox1Change(Sender: TObject); + procedure FormCreate(Sender: TObject); + procedure RadioButtonEx1GetImageIndex(Sender: TObject; AHover, APressed, + AEnabled: Boolean; AState: TCheckboxState; var AImgIndex: Integer); + private + + public + + end; + +var + MainForm: TMainForm; + +implementation + +{$R *.lfm} + +{ TMainForm } + +procedure TMainForm.CheckBox1Change(Sender: TObject); +begin + CheckboxEx1.AutoSize := true; +end; + +procedure TMainForm.FormCreate(Sender: TObject); +begin + RadioGroupEx1.Buttons[0].ThemedCaption := false; + RadioGroupEx1.Buttons[1].ThemedCaption := false; + RadioGroupEx1.Buttons[2].ThemedCaption := false; + RadioGroupEx1.Buttons[0].Font.Color := clRed; + RadioGroupEx1.Buttons[1].Font.Color := clGreen; + RadioGroupEx1.Buttons[2].Font.Color := clBlue; + RadioGroupEx1.Buttons[3].Enabled := false; + Label6.Caption := 'This box contains ' + IntToStr(RadioGroupEx1.ButtonCount) + ' buttons'; +end; + +procedure TMainForm.RadioButtonEx1GetImageIndex(Sender: TObject; AHover, + APressed, AEnabled: Boolean; AState: TCheckboxState; var AImgIndex: Integer); +begin + if AState = cbChecked then AImgIndex := 1 else AImgIndex := 0; +end; + +end. + diff --git a/components/exctrls/examples/CurrSpinEditEx/InterestCalc.lpi b/components/exctrls/examples/CurrSpinEditEx/InterestCalc.lpi new file mode 100644 index 000000000..071c4c0d7 --- /dev/null +++ b/components/exctrls/examples/CurrSpinEditEx/InterestCalc.lpi @@ -0,0 +1,83 @@ +<?xml version="1.0" encoding="UTF-8"?> +<CONFIG> + <ProjectOptions> + <Version Value="12"/> + <PathDelim Value="\"/> + <General> + <SessionStorage Value="InProjectDir"/> + <Title Value="InterestCalc"/> + <Scaled Value="True"/> + <ResourceType Value="res"/> + <UseXPManifest Value="True"/> + <XPManifest> + <DpiAware Value="True"/> + </XPManifest> + <Icon Value="0"/> + </General> + <BuildModes> + <Item Name="Default" Default="True"/> + </BuildModes> + <PublishOptions> + <Version Value="2"/> + <UseFileFilters Value="True"/> + </PublishOptions> + <RunParams> + <FormatVersion Value="2"/> + </RunParams> + <RequiredPackages> + <Item> + <PackageName Value="LazControls"/> + </Item> + <Item> + <PackageName Value="ExCtrlsPkg"/> + </Item> + <Item> + <PackageName Value="LCL"/> + </Item> + </RequiredPackages> + <Units> + <Unit> + <Filename Value="InterestCalc.lpr"/> + <IsPartOfProject Value="True"/> + </Unit> + <Unit> + <Filename Value="main.pas"/> + <IsPartOfProject Value="True"/> + <ComponentName Value="MainForm"/> + <HasResources Value="True"/> + <ResourceBaseClass Value="Form"/> + </Unit> + </Units> + </ProjectOptions> + <CompilerOptions> + <Version Value="11"/> + <PathDelim Value="\"/> + <Target> + <Filename Value="InterestCalc"/> + </Target> + <SearchPaths> + <IncludeFiles Value="$(ProjOutDir)"/> + <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/> + </SearchPaths> + <Linking> + <Options> + <Win32> + <GraphicApplication Value="True"/> + </Win32> + </Options> + </Linking> + </CompilerOptions> + <Debugging> + <Exceptions> + <Item> + <Name Value="EAbort"/> + </Item> + <Item> + <Name Value="ECodetoolError"/> + </Item> + <Item> + <Name Value="EFOpenError"/> + </Item> + </Exceptions> + </Debugging> +</CONFIG> diff --git a/components/exctrls/examples/CurrSpinEditEx/InterestCalc.lpr b/components/exctrls/examples/CurrSpinEditEx/InterestCalc.lpr new file mode 100644 index 000000000..3273c4d36 --- /dev/null +++ b/components/exctrls/examples/CurrSpinEditEx/InterestCalc.lpr @@ -0,0 +1,25 @@ +program InterestCalc; + +{$mode objfpc}{$H+} + +uses + {$IFDEF UNIX} + cthreads, + {$ENDIF} + {$IFDEF HASAMIGA} + athreads, + {$ENDIF} + Interfaces, // this includes the LCL widgetset + Forms, main, lazcontrols + { you can add units after this }; + +{$R *.res} + +begin + RequireDerivedFormResource:=True; + Application.Scaled:=True; + Application.Initialize; + Application.CreateForm(TMainForm, MainForm); + Application.Run; +end. + diff --git a/components/exctrls/examples/CurrSpinEditEx/main.lfm b/components/exctrls/examples/CurrSpinEditEx/main.lfm new file mode 100644 index 000000000..1e05f553c --- /dev/null +++ b/components/exctrls/examples/CurrSpinEditEx/main.lfm @@ -0,0 +1,156 @@ +object MainForm: TMainForm + Left = 353 + Height = 223 + Top = 132 + Width = 304 + AutoSize = True + BorderStyle = bsSingle + Caption = 'Savings calculator' + ClientHeight = 223 + ClientWidth = 304 + OnCreate = Calculate + LCLVersion = '2.1.0.0' + object sePayment: TCurrSpinEditEx + AnchorSideLeft.Control = Label1 + AnchorSideLeft.Side = asrBottom + Left = 168 + Height = 23 + Top = 24 + Width = 103 + BorderSpacing.Top = 24 + BorderSpacing.Right = 24 + MaxLength = 0 + TabOrder = 0 + OnChange = Calculate + CurrencyFormat = secfValSpaceCurr + CurrencyString = '€' + Decimals = 2 + DecimalSeparator = '.' + MaxValue = 0 + MinValue = 0 + NegCurrencyFormat = sencfMinusValSpaceCurr + NullValue = 0 + ThousandSeparator = ',' + Value = 100 + end + object Label1: TLabel + AnchorSideLeft.Control = Owner + AnchorSideTop.Control = sePayment + AnchorSideTop.Side = asrCenter + Left = 24 + Height = 15 + Top = 28 + Width = 136 + BorderSpacing.Left = 24 + BorderSpacing.Right = 8 + Caption = 'Amount saved per month' + ParentColor = False + end + object seInterestRate: TFloatSpinEditEx + AnchorSideLeft.Control = sePayment + Left = 168 + Height = 23 + Top = 57 + Width = 103 + MaxLength = 0 + TabOrder = 1 + OnChange = Calculate + Increment = 0.1 + MinValue = 0 + NullValue = 0 + Value = 2 + end + object seYears: TSpinEditEx + AnchorSideLeft.Control = sePayment + Left = 168 + Height = 23 + Top = 88 + Width = 103 + MaxLength = 0 + TabOrder = 2 + OnChange = Calculate + Value = 10 + end + object Label2: TLabel + AnchorSideTop.Control = seInterestRate + AnchorSideTop.Side = asrCenter + AnchorSideRight.Control = seInterestRate + Left = 77 + Height = 15 + Top = 61 + Width = 83 + Anchors = [akTop, akRight] + BorderSpacing.Right = 8 + Caption = 'Interest rate (%)' + ParentColor = False + end + object Label3: TLabel + AnchorSideTop.Control = seYears + AnchorSideTop.Side = asrCenter + AnchorSideRight.Control = seYears + Left = 133 + Height = 15 + Top = 92 + Width = 27 + Anchors = [akTop, akRight] + BorderSpacing.Right = 8 + Caption = 'Years' + ParentColor = False + end + object Bevel1: TBevel + AnchorSideLeft.Control = sePayment + AnchorSideTop.Control = seYears + AnchorSideTop.Side = asrBottom + AnchorSideRight.Control = sePayment + AnchorSideRight.Side = asrBottom + Left = 168 + Height = 8 + Top = 123 + Width = 103 + Anchors = [akTop, akLeft, akRight] + BorderSpacing.Top = 12 + Shape = bsBottomLine + end + object seFutureValue: TCurrSpinEditEx + AnchorSideLeft.Control = sePayment + AnchorSideTop.Control = Bevel1 + AnchorSideTop.Side = asrBottom + Left = 168 + Height = 23 + Top = 147 + Width = 103 + BorderSpacing.Top = 16 + BorderSpacing.Bottom = 24 + Font.Style = [fsBold] + MaxLength = 0 + ParentFont = False + ReadOnly = True + TabOrder = 3 + CurrencyFormat = secfValSpaceCurr + CurrencyString = '€' + Decimals = 2 + DecimalSeparator = '.' + MaxValue = 0 + MinValue = 0 + NegCurrencyFormat = sencfMinusValSpaceCurr + NullValue = 0 + ThousandSeparator = ',' + UpDownVisible = False + Value = 0 + end + object Label4: TLabel + AnchorSideTop.Control = seFutureValue + AnchorSideTop.Side = asrCenter + AnchorSideRight.Control = seFutureValue + Left = 90 + Height = 15 + Top = 151 + Width = 70 + Anchors = [akTop, akRight] + BorderSpacing.Right = 8 + Caption = 'Future value' + Font.Style = [fsBold] + ParentColor = False + ParentFont = False + end +end diff --git a/components/exctrls/examples/CurrSpinEditEx/main.pas b/components/exctrls/examples/CurrSpinEditEx/main.pas new file mode 100644 index 000000000..e0a08a8db --- /dev/null +++ b/components/exctrls/examples/CurrSpinEditEx/main.pas @@ -0,0 +1,48 @@ +unit main; + +{$mode objfpc}{$H+} + +interface + +uses + Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, ExtCtrls, + SpinEx, ExEditCtrls; + +type + + { TMainForm } + + TMainForm = class(TForm) + Bevel1: TBevel; + sePayment: TCurrSpinEditEx; + seFutureValue: TCurrSpinEditEx; + seInterestRate: TFloatSpinEditEx; + Label1: TLabel; + Label2: TLabel; + Label3: TLabel; + Label4: TLabel; + seYears: TSpinEditEx; + procedure Calculate(Sender: TObject); + private + + public + + end; + +var + MainForm: TMainForm; + +implementation + +{$R *.lfm} + +uses + Math; + +procedure TMainForm.Calculate(Sender: TObject); +begin + seFutureValue.Value := -FutureValue(seInterestRate.Value/100, seYears.Value, sePayment.Value*12, 0.0, ptEndOfPeriod); +end; + +end. + diff --git a/components/exctrls/exctrlspkg.lpk b/components/exctrls/exctrlspkg.lpk new file mode 100644 index 000000000..58d5c9e3f --- /dev/null +++ b/components/exctrls/exctrlspkg.lpk @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="UTF-8"?> +<CONFIG> + <Package Version="5"> + <PathDelim Value="\"/> + <Name Value="ExCtrlsPkg"/> + <Type Value="RunAndDesignTime"/> + <Author Value="Lazarus team"/> + <CompilerOptions> + <Version Value="11"/> + <PathDelim Value="\"/> + <SearchPaths> + <OtherUnitFiles Value="source"/> + <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/> + </SearchPaths> + </CompilerOptions> + <Description Value="Extended standard controls: +- CurrencyEdit +- TRadioButton, TCheckbox, TRadioGroup and TCheckGroup: drawn by ThemeServices/Canvas, not by widgetset, button/text layout, wordwrap, user-provided check images"/> + <License Value="LGPL with linking exception (like Lazarus LCL)."/> + <Files Count="3"> + <Item1> + <Filename Value="source\excheckctrls.pas"/> + <UnitName Value="ExCheckCtrls"/> + </Item1> + <Item2> + <Filename Value="source\exeditctrls.pas"/> + <UnitName Value="exeditctrls"/> + </Item2> + <Item3> + <Filename Value="source\exctrlsreg.pas"/> + <HasRegisterProc Value="True"/> + <UnitName Value="ExCtrlsReg"/> + </Item3> + </Files> + <RequiredPkgs Count="2"> + <Item1> + <PackageName Value="LazControls"/> + </Item1> + <Item2> + <PackageName Value="LCL"/> + </Item2> + </RequiredPkgs> + <UsageOptions> + <UnitPath Value="$(PkgOutDir)"/> + </UsageOptions> + <PublishOptions> + <Version Value="2"/> + <UseFileFilters Value="True"/> + </PublishOptions> + </Package> +</CONFIG> diff --git a/components/exctrls/exctrlspkg.pas b/components/exctrls/exctrlspkg.pas new file mode 100644 index 000000000..403753cc9 --- /dev/null +++ b/components/exctrls/exctrlspkg.pas @@ -0,0 +1,22 @@ +{ This file was automatically created by Lazarus. Do not edit! + This source is only used to compile and install the package. + } + +unit ExCtrlsPkg; + +{$warn 5023 off : no warning about unused units} +interface + +uses + ExCheckCtrls, ExEditCtrls, ExCtrlsReg, LazarusPackageIntf; + +implementation + +procedure Register; +begin + RegisterUnit('ExCtrlsReg', @ExCtrlsReg.Register); +end; + +initialization + RegisterPackage('ExCtrlsPkg', @Register); +end. diff --git a/components/exctrls/images/imagelist.txt b/components/exctrls/images/imagelist.txt new file mode 100644 index 000000000..7d6b03466 --- /dev/null +++ b/components/exctrls/images/imagelist.txt @@ -0,0 +1,15 @@ +tradiobuttonex.png +tradiobuttonex_150.png +tradiobuttonex_200.png +tcheckboxex.png +tcheckboxex_150.png +tcheckboxex_200.png +tradiogroupex.png +tradiogroupex_150.png +tradiogroupex_200.png +tcheckgroupex.png +tcheckgroupex_150.png +tcheckgroupex_200.png +tcurrspineditex.png +tcurrspineditex_150.png +tcurrspineditex_200.png diff --git a/components/exctrls/images/make_res.bat b/components/exctrls/images/make_res.bat new file mode 100644 index 000000000..8167e3d7e --- /dev/null +++ b/components/exctrls/images/make_res.bat @@ -0,0 +1 @@ +lazres ..\source\exctrlsreg.res @imagelist.txt \ No newline at end of file diff --git a/components/exctrls/images/tcheckboxex.png b/components/exctrls/images/tcheckboxex.png new file mode 100644 index 0000000000000000000000000000000000000000..a98b887befe22bf16181221f0df468f409edc549 GIT binary patch literal 695 zcmV;o0!aOdP)<h;3K|Lk000e1NJLTq000;O000;W1^@s6;CDUv00004b3#c}2nYxW zd<bNS00009a7bBm000ie000ie0hKEb8vp<R8FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H10yIfPK~zYI?Ug@h6LA#BKX>`pM2I+O*D7v8C`vGN>0&`w=}>TT zuqh-*1u3D_L4^*b7$_7<w@4RV#KomJ75XP2rRpLIMhS}GA4t>WdY9hsImCptxl5aN zaPbSr<M-~p@Avoaz2EQPKmIi|H|M~;e92J~?Uu}^Yujs&ubyu8`P~8?CDEn37wHKI zB7#y15otb+b*rsff|2{#wkcNufJhL+LH#dD!-7(Rh}LFBDOVodOTcTVt#*A~+qdpY z;4t!R*3ZaDgqfL8(|_y=R#&Bsd~PlTz~W+vjSb)NE0~?t7#<EXJ?(2F2QUnic-;1^ zz_ZEqooi&X^^k>y0GpdS`Mj?glTKGjrK-p8K_cNWIazD^TFDK=B%QAA>rGbzjij+L zn@q;q_s%d(QmIN?<GU7UBqx&=6BA}faxa07J*Xd|I?3qhF0q(RJYMr0T~`4Sc?0yd zIxD*l;b8rmDi(`4j)PK)c--lDKD;W34rNxNPp1;RE0nnTU=4duVbv6$mTpkB6_({3 zda_O1{Y&@(K7K8;W9~5+JIkZnml&Vlq9>%I6ihuU@$I+eo_zqi#<r_K1G(kTjL&az z?eaO^7s~)Vzu$jB)3m~o6uc?`*sknxC;OVAE4{pUIKY$TFZ7-Xb;>;&M5NGn>!lRF z@5svAAF^vZascN~!tV$8Rkr9)zCv&LzlB3SKIE=*s>gGZj|5Fb3Z+V|PaT+JpRN&& d1OQIdgFoVd?>ZXTY32X`002ovPDHLkV1iLoIY9sb literal 0 HcmV?d00001 diff --git a/components/exctrls/images/tcheckboxex_150.png b/components/exctrls/images/tcheckboxex_150.png new file mode 100644 index 0000000000000000000000000000000000000000..f64735a360a9975d10d6f2b78baf8efc9113bf6a GIT binary patch literal 776 zcmV+j1NZ!iP)<h;3K|Lk000e1NJLTq001Na001Ni1^@s6;Q*MJ00004b3#c}2nYxW zd<bNS00009a7bBm000ie000ie0hKEb8vp<R8FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H10)<ILK~z|U?Up}iQ&AMgzngbcOcP17h;$HjkVtSz90a#g+K6rv z+O&g9M4{BhNl|d?R1h3QN)po|h1Ok~bm<_qDNc#dE_sm*Ee>i-u(o;c9*5G`{CRnK zVs1OMAH3zAbMN`xb2;Z+;8dqN)d>@`GTwarsiFyZx*3o=Q2jW4<7`{I)nZM+lYVg( zMAWq}`s$nI0Ue!A)J+&7>J;t-0qyOXuRRu^LID^Ua3PaXjvuH{0HV=elu8avOt`ST z%;f_KVGXmrz28l0HHOJaH&#}-JfLirAslvMYYUbz2XeUxKA(^Sl+7}Xk8|X543S8W z0o4x1>m?)-zYqx2x(~Fr#xOR<QLXO(S5=0Qkuz9bb(q5R^$Da><pvl#K$e}l9*8l3 zH4Pe7Wf&cGwXE@a3GsLZe!nKK%0eL>b91}!beL>r*CegVLLnWqvwz@rn{V6+MoN_h zgBnt)ofarNFw*)^FsLD!{B1zlg^}8+gTWf&@hbZJg>4vHRYpWR02DK$rW2GVWK~rg zpwj7|;9S^(v0L^%?J~uc)(2&C0M7qy*4Y6<Ahv}3%&^gCuH8=~Uo0C=PQ`)U4|l=X z|G$qq$pHYHr3yryb8cn`l!@R;)DHmAgfQ$fVT=r!>G|6{rsg*gyLAc4rK0iw-kTo~ z!rta)QW#Y&9f(L*h89WF1s&*FGDjF(pxE>}=|WJG-LwF@rIrjmP2?~&zkvsLF5~s1 z5Euh4o_Dv*F=-+qoewWkO%wE`SSAcE(8HIXNgO7uSyD6907OKzzWLn>=IF2N`W2td zcpb$7fPujoe&5#V!nt02eRumvfEW?!urbr>#2I6{ETF$w*;<COU0G590000<MNUMn GLSTY%l}}Ls literal 0 HcmV?d00001 diff --git a/components/exctrls/images/tcheckboxex_200.png b/components/exctrls/images/tcheckboxex_200.png new file mode 100644 index 0000000000000000000000000000000000000000..063f09a56945287fbd825e76ee3be5695f420dae GIT binary patch literal 957 zcmV;u148_XP)<h;3K|Lk000e1NJLTq001xm001xu1^@s6R|5Hm00004b3#c}2nYxW zd<bNS00009a7bBm000ie000ie0hKEb8vp<R8FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H113F1WK~!jg?U+wb6G0fppV@8;0XHES&x8xn1CZze4p>Elav;Hj zfe;7^i3j5a{Q|Zh0By=eV}ODX4qiNpG0{X&IT+Ifu2@V+8q<gcHQLg7J(O;C>9pOU z-CaofB%5ac%*^lEciwqt3shBARaI40)d?1}@=(Xbr}tp!vKz)E!pd5x^J1yT6ib~s z4KFdKD%0ok!?OSYBGL~#&WEPHTV}Er0fX#@&;8nBzzKLq3^-xNHUpJ_<D8I46z<P9 z1C@dg27T!3tHaioa<XzD2Oo*}5RWSu8mdDq#!f~K7~yk@#}y0@pAtsGHUlEKRI0c+ z&Iy5l8Qy4OWJJUEHj{>zHXwpaCV~Eb71Pt6g7Cqh4|8+*@Hw@#?4z+UqqmolR3#R{ z4-Tq`Mj4_}6-0!wv2@XCb69?V3R6?NsHt%{2aNF9dq5!I#mtOa7QF2d0DzqxU|>MS z`Z_yw9|#70AR@$Ktmw4q?oJ~d-h<a$ULBA!kcA{L26#LK(QZ6zqNZpcet!y~(5?{P zktw8U97{`Sw6}Aq!GPk^#?c(mG>*l^e^Mr_z&lz8G>v0n;jbkVcEC#;5Dlhj9P{&k z(AJiz2)rwMp<*Uv;a%GcX&Of)^2=o6DDct-h)5wKPq9N$7!)Oc*C>j_?Cc&|TlXD- z2LMJy92Wmx5ovRCGY=-21ZryuUN1?*6A|t{PXPed9(R`Hk}hP2C60u1-^99su8Fr$ zJq+(AFT;3kbt942sG;7AotHQCLCb+HcN(bxz=^QBk$}R?5zBCKXFq?ZP3LaG10`0W zkKrrGyuOL?-e%KvW=I(~;emq)<bVj~PB;<nJo=28S6|Hr41v1!<Od1_$`KWe01<F6 z{KW)+`+AcjP+0)1Sa~cPfrB@ii=ybwkM}m?>HYJFEN_`Q_%PYwVT{RdODiPdGP>~Q zTORzg2N$3)hR$=Rp?Z#7HYzTm0d7brBRr5z?BgV%fzh5OD2#zIVB~5O*ztMcdD0Cq zmdyboKw%7{Jq{*p8PW~pW{q;~Maf2&+Rm6NwwuzI(S~}j<wk`nv>PgWHwwV@Wi*2$ fd|DdhYB>G_2U8Hjn=$qP00000NkvXXu0mjf%^8{* literal 0 HcmV?d00001 diff --git a/components/exctrls/images/tcheckgroupex.png b/components/exctrls/images/tcheckgroupex.png new file mode 100644 index 0000000000000000000000000000000000000000..dcdce6034c70f1999bf4b1fde2ac7c4f1c5f8f06 GIT binary patch literal 754 zcmV<O0uB9%P)<h;3K|Lk000e1NJLTq000;O000;W1^@s6;CDUv00004b3#c}2nYxW zd<bNS00009a7bBm000ie000ie0hKEb8vp<R8FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H10&ht~K~zYI)s;VJ6Hy$;KbMBf<$@J-(m`4TQ5+QMAkubl5R?u9 z$)DP-gc4}T)S+e(OR*%h%Ta{h;GobZjTxN8tsos71W7VT(jlP5r3khTxeK}HP!f{# zrKacPce?lc-S6}Lz2Cdz!T&Br77RWAxC_d?J~g}fXXE3p3{2DfC4?B>`f~2nqUqa5 z{eg;dI5#(UzP0vtCWR2g;c%Fpy>}F=Kcs+L*Deu_My0t%qd}oi819UDdtXvjmDew3 z5JI#}u`COp&(~geG}Kkwy9V&cI&mDQU64+v+1S`{1(zBKA()?^r&_I&PN%8WYQ$nO zS8$Mw2w-w@l0+iG=;$bGYis!ZenO#8UkE@hmmAz57z|P>l}M#hXqtv)S>*HiQ#(J? zh*VW&d3hO4)5v5pn5O9pE;Rs1Bob6A6*8F&wOWnY+1Y{MG9&4TCMG6GCX<YejIg@8 zimvN~!{NSNX0zG8yEtJWgdi9UQZAQSTwFxgbu7zbdwcs#owp<%k*caJEiIwzI)-6j z7{);Gfq%4xg$2sxGKOJLEEb8!<F4QUULiys@O1{)*Vh3U8yn-`;DA6NKqL~80$SSK z+>{#t+>CvoXg(mf^NpF6PXG*g6s-4;2?PSb@j<%Tg$H17|2IuX@Z|m#o=#tHC!3Bq zQFzu7b)7-#Rf(0Yuf(UWGUPeFDlE&wwry<N{_At2(U6Y_pz{5w4S$)u!`&MfP`w`B z6bdw(O=)*BDT;E~6%azyeGlJ?y4@6?D?f$fh+b&}(<SmAn}GJH(3QA+(a(N%>R;FZ kD6-GAM}9?7>TV7G04i*8HUCQ98UO$Q07*qoM6N<$f_^JcTL1t6 literal 0 HcmV?d00001 diff --git a/components/exctrls/images/tcheckgroupex_150.png b/components/exctrls/images/tcheckgroupex_150.png new file mode 100644 index 0000000000000000000000000000000000000000..8debb318672239a377669c82bd2ab80ca96dadfd GIT binary patch literal 672 zcmV;R0$=@!P)<h;3K|Lk000e1NJLTq001Na001Ni1^@s6;Q*MJ00004b3#c}2nYxW zd<bNS00009a7bBm000ie000ie0hKEb8vp<R8FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H10v$<2K~z|U?Uz4j6Hy$;KbKrgFB*ro3esJ<-9ZRAIEV(4P;kuP zawKjJ8IrohO}HWG60&6I<Wy*`0kP1bgOiyoh(Z;KAVRbd3YVt2dk&^Y{x?SNg0vsF z;obW^?!$Y(?{Np`{AWnLgIFvkycRGFqt8JeET~i}p2@`H@u7P@Hy>yiM&H=BZK~Dk z#5Jx8)oL|zxtt@TX&Uu<eSSjue4b9HLpGa5(==37C7n*sPY6KQbu7z5RaG*X%&GN5 zP3;D{u1{S%Bs8k?z2t1geip{|&?tK^c5h-+_X>E`TQj9Bl}i0lz0c>PSS-3HR4SEb zSvD0Dh34@J7%Svu{Chc1*=cx^vJ;}wD2YVk-wU0slnEg|14{s=X*v`0Oj)>lp(x5+ zBzlnnB9C`?{p>au7k;1M-Rv%JKO6wiKDKFFHa|Avgq46dLLa{#VjfwJ(B{(!tz(PQ z);??HJ%Ry0M{S#McLS$MStt~uP$<kMG>8)Pv%dX>welXTkrj5|HTve=Oxef}w9?im z9=)is8d>4xlUuGme5{ZJocu!%$^>K|%a<<@4EQ|}>N~P;KU+gG0PoB9JdtsPBngCo zYl{oKd47kR*B6of(#QqLWRhmH>7J1gqH*=Xrucr)5>vA5o=ta$4$TfX9=<{vy(wH$ zWcJtZJ4fgyk`SWNYFkTY+ZtOG3d#fnj?5KFk{V-+|B_$h(A<&kmO%0V0000<MNUMn GLSTXer67v{ literal 0 HcmV?d00001 diff --git a/components/exctrls/images/tcheckgroupex_200.png b/components/exctrls/images/tcheckgroupex_200.png new file mode 100644 index 0000000000000000000000000000000000000000..171f10351a0113a844eb8d7b01f2583cf85fe3f2 GIT binary patch literal 879 zcmV-#1CacQP)<h;3K|Lk000e1NJLTq001xm001xu1^@s6R|5Hm00004b3#c}2nYxW zd<bNS00009a7bBm000ie000ie0hKEb8vp<R8FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H10_;gdK~!jg?U_$#(@-48zb~ygL<h=Xg2VX-Jb0bM!#Y7M6B&52 z=w(ijSwVM<g5Ak59PLS}q1A)5qL;dhpiITVa5w+VlQ#vyi*=gV;hf#<&%<omxTJfp zY0?ezIVE}TmwdnP{oebv3E<=Nf5Uh?o*H}A&j@J-+9CLiPas6^U+I1<7{^7Bx8u)( zo&tlYcmU`CkOHu?j@VVPoq=>Z{Q^L!eE#5Fo1+Jv*gxwed^#G9_F6U6nt@RD$^6`R zCjy)jvTIm1g$4%)0RZDOS>(U{sO#zN+cq2b5hat!)NJQo1M)x*uBImN`RkmJfbDHt zAfN6LiV=a^KyTMxJiC3tH6isPHCupFd;<V5IywqfRU2_DVH_KfWf_X1K-08F=n=+| zfsv6BB$G)D4-X?2i#1A<5Zt?vs;ba5%@8WfvLQZ^NSOLYA`wTTa=9FLUv2}Mra@5@ z=(-L7w&J~p+dwQ9gRbjHCX)bAQ@qFsI)&Q+03;F#w6wHfVPT=^BVR_y001b8;)ag8 z@#}zJ2OP4d>OeiamtP00@aup8ejNb(I^c-)Uk8Y237kfV2#hh~I3ACqSS%tQkDK~= z{eM6L=S$wQ`0C?-#e0ph3xzLDAsCP_ajo(S@`yZz$+!6xL2a!Y@n!tD?M*$e10^Cn zpUFdF=7}iGFIoo87Jj*}jFXzV|MF%SrKb^`>)&I#&iFqoxQ+x=1QE#8NCcd}{u;L) zeK1{TYe&ER3=jbqZ@e*yKY6&r6Hy{q8Yqvv`tZGRZ6Mr<yO$4oCXAOLP)P~;n?d&` zKbl5<Al!*NayJ4J!-0KU5e!s(XR}!lnN!b8EPu=u7fZ%*wRm88VK;$ky9P@GXO8Z| z(r*G|z}aKFS8T7<3=Ith7-J>d4lZLDU~D;shyaPP6<g<8#V}A#W2tZTD%#i8jwfS> zToWaNu^L-9ZQu?n%rChjrXCm(l_2oN-h!sCfp!0h=?@O3Cuj2&lW70|002ovPDHLk FV1lHBe`^2$ literal 0 HcmV?d00001 diff --git a/components/exctrls/images/tcurrspineditex.png b/components/exctrls/images/tcurrspineditex.png new file mode 100644 index 0000000000000000000000000000000000000000..d3e94d42afe7fd62870f5406a597dac34f666f64 GIT binary patch literal 759 zcmV<T0to$yP)<h;3K|Lk000e1NJLTq000;O000;W1^@s6;CDUv00004b3#c}2nYxW zd<bNS00009a7bBm000ie000ie0hKEb8vp<R8FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H10(414K~zYI?UP$blVKdjfA3lla}-esGKmb6PKMnyAp#jLrHew$ zf|U?fvkS3O=%Oy1(Nz!~z!)DSA)^bmE~E>MgcV3Bgr|;@Xta$Hl9{?rwtpA0k9|20 zb9LA6?tA|4_dLAs`@Ha<MNj~Bw>xAxKi8iqQktesiikC#xl&W+*oUWLUsC3t#)kli zh}G?O1JK>w4S?Nl2SC#_t0~dXT|I+Ig#q~eejE-54u=E3->>U)0<L<SIa}92)x{gS zcK7}x%G@5x+#X)O9ZeYEa=C~^B19q)TrQWco8uP+!(%*o*3YrhLQa<-)b+ks@3>HX zlwCVE&%1+AD3nk)1f;KDi-_QV+D~Qq0bQT?GL5&XjlK4rxJn8!nUp?LN=<2+Ht!_^ zvn`78b7n?P)?Sff*LfNLJSid~?*n0Z@n%F0ovM@82aiQWWU0+F>|i?bjSr!ZY|Gim z*Y7_V4TQ;ClTAkYIyTtM<Zj7gd}7jIu;iM?@HVy4)BBp^M~cbYo(({4{Vf9FF;-hG z)KnfetdFnhpJ<w9H3XztR&o7O6<s}p?Ae{G>o+e~FK`I6WM*a(3<d$P*=z&?0U$w5 z0fYHc@%emMEEX&l3qGGu*XJD3aw#Y%ptG|Rfc*UYxdE}KpDc-LIxsMx&y<KPSU07V zT4=r)CK1?L(GEak`v}U}f^xR7vZyr`-?#vTp}_2et0>OmTxkvfzoLJ|-`JuWZ$H=W z4N+Z^O=<@dM>Hy;oXyfVJT4SA%bA8=iAF{4bd1Pi_mfnBh{*GyPZEuafmHp?O2G<~ pLOELugH0J}3^(mh)t`S})89(b9CUzCsm}la002ovPDHLkV1j=|Oi};< literal 0 HcmV?d00001 diff --git a/components/exctrls/images/tcurrspineditex_150.png b/components/exctrls/images/tcurrspineditex_150.png new file mode 100644 index 0000000000000000000000000000000000000000..c89b405d08ab6dcd1a2de1f97570dc89f3d76630 GIT binary patch literal 1133 zcmV-z1d{uSP)<h;3K|Lk000e1NJLTq001Na001Ni1^@s6;Q*MJ00004b3#c}2nYxW zd<bNS00009a7bBm000ie000ie0hKEb8vp<R8FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H11L{dcK~z|U?U!q4TU8vuf9Kw;vq{r+bEDf5TUXanbxK8sMY~ah z>tiir54(P;dvq8E!fctaO$Mzv#1~pnCInk&9j;UzNJ?oPb&)bhhSFjwPB*o#4?1-8 zm86a)IezGR*EFv_vLLeGm&-Z-f6o2glY9Q>T)4+Q{$~h)>bf==>w#Z8SRiaR+qjf6 zX-*<SxU5#IQ8(RDQ;{DtnVR}<0FY89S*_MN{l;dqB>^ZgvzQ}JrxT~s88#kK==#77 z28RBQnT*5Xz+$mru~=|893gXbA$x&IuaCyBzh!dL%bx8PGS`{3*yy;M-o7ENy)<D7 zS?kjx%5-#eP*PHY$KwH@xVRX*-Hyd#i7qrU>S9O533~g6xPL`5b!S`XahxXczNG-1 z|LzA~dbL)|&q!OtrRGx+h04mxaJ$`F+vD+2US7`N;9zv2(@m|Ys=~>m2QeBF$$RDr z5)zhZ@!?Sy*&El>_W3&zC1x`F#^DJ24vmhv85^H^qGd({s-nz@xkyQ#(`ueKA!le` zX#uSlE_1T>GxnDiQe=55V1`D<TwJ`|&BkXBkeOlT#LF-6<l`HH#t}lerk8Uxgb*&R zQC-(2XXEqvW%awC$n;HRa?8%c@~@jCTCBHkNOtvHlNT;_%96duWY*@r((Reh;)^M? zVON==2sUIqz#GS3<iq-NeA?8;kwbd`NH?dFPAUM#Bt6^8kMi4}gFKd%5irK0D)mep z-*>S*#fabU$L*QG>+@kuO4Q=RBjYSjF#>Su#~yU5ih1>_pm7#bsY43g93JC9)mu#b za|@kLWqbb9>?<wQ;uS~UW@6$NlU^^sT)R%qEBiIU#S&6zckvc>6y;KXwuO}|ma(&F zb3puq*ALU#a}`A>WIy^4rZvH{GaIwn%)r1vK%dEEqQAdCx)6Y>C=}#vLRHn^!Pl<B zyf(VTOd1;-DJm+$<(gVd27`fyhK8WLh<jO@DHYS|mGioBIC67yvD@wF^?LMrJ<ZL{ zLE8pIn4!BtUS1yU?d<^M<m80RiK`HV7n$bH0qypoDwZsyQVS{NysD}YLM%jXuEhPs z7!4i3y7CtOII~Ube7K^OD}Cd4l2pND>+^9DQUH2yxC1(Wt}jHMFQsbt)DG_T#r&fr zvQQ|F*LCy$SHDuZD>H5~5Ju?O$KAZyc!kPcnY?!>JFY@H03kriscY3oUDTets>uie zkEAEZ&qzus#nvY2_e=TH?<11unq=jvPU-gto!R7kOT>bOl&xP#-))1Q-qFw|aS{r8 zhvNG8gg^&Vbw<tqGHlX=sfK_$bvOCXZ5{st(O<eU1sJ;T00000NkvXXu0mjf*taDN literal 0 HcmV?d00001 diff --git a/components/exctrls/images/tcurrspineditex_200.png b/components/exctrls/images/tcurrspineditex_200.png new file mode 100644 index 0000000000000000000000000000000000000000..286e2eefe6cd558f19ec117ae34f29dde13cbabc GIT binary patch literal 1428 zcmV;F1#9|=P)<h;3K|Lk000e1NJLTq001xm001xu1^@s6R|5Hm00004b3#c}2nYxW zd<bNS00009a7bBm000ie000ie0hKEb8vp<R8FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H11rbR^K~!jg?U{RQQ)L*3pVM{>=28ZX&9H@aOSWawfXgc2vJK4` z<I)j{#2FD`AS(!>$P!VAq8R~WLb#}L6VPO6;st_Y6>x*$#wD8trXxbQD9Fv0%WlFp z=#GDsl^uH8p0?|@KlUU|+OO~X_4n)hedl|73jOrcPd~kh0PybCy<$&(=zH^X1%Q&0 zk}4p>&uOYnCR3)W`(*irzv7*_nyw?O=S>PojW<?r+b26-SXk)OJf)?j&gZ>d0a>h8 zD+YrBgTa8+YV{ba$Us9=3r#JzyG5KgtX3=8+1Xeu7AzJE+1c41O(-%@x_vj>zmqS2 zchV`ozP{c$R$pK5(S$~J^gj3OcD~$Ej=B6O?d^B@{m=7EpOntj?D3K~dq)SKe6h<V ze{5<B<5E);8!#G;67gM47>!11YHE}l5JizWOV_gh&@m#14n`aP2z4jV(%97EVxY09 zh2jlmE^{nj_?%A$LPJA+e$TCegGcHpud-15=6r57HWMB;h*#!5!^O-0y2Lg#{RcqN zqI?$2GCIcxh7M3H9-ykK%BOhnum>(*vjLD0H$pN#GA7z3wxO{ZfJcW9B|;nSeE4d> zAKX@8?Bgk@R06BkZ6PHo4#VhVx05tBJ1WrH*3P*LR~Q)+O^8OVSVY(By05scKw?5H zn~PuLn(Y?m@}umntmVx4OR}+z_m|MB54Mt?w;0_M`MkPv6RmCS-afzDU*Vm(9<M?( zjT6WkKZYeMH*oMs9Zx;`I>jsIQ;<JJ5;rP2p3)DCP^(p3x?<z=uYcy7-3J&pRLkn+ zbKTBwGMO?1Fo0JB08x<<7{(-%ln_hJ@l(9L_A|z%rtnyj&KZ*!A46h%jPv!3DVd~A zF68??)gBFW2VM<yG9o&Xx0cT0`GxOuy#9>Dz{5ljA424i!PsszAc`V_AjrlAwHrNn zq>fEnck<5_#|e1*P8&b$Jp|w){`$>E$?M9)C%9_8!KmbTx8m>9z1?V6+V9?D&Bij; zY%D{i66oj<QL9uGEy^cVpD2l4_t96JJpDJLl673O-DH2&F+wzI-dXmNA|-)zqizl4 zWTjEPYcsWVr`h?_uM8g+!HOj>5Etuu4orA*45!Xsr1J0yBDCSmn3{=c_H;(4B={uE zA3W}X#DrK96Joh^)k=8SAX)JMc{%Ci<)jBJCNndW>T3BbYeq(f>`AR?6BoXihe{Rv zpAN09tv<h}NL7S39Bp`*f2w@IY&N6U>s`k6dOc>dSvIFn{UVEsilU;TLSjO%*HckZ zK~z+fY>wh{pr`0GF>Ts3z)=-<;-L>30Eda%+FC`i(z_CF?ivJ}pmd`>M8MssAc|r@ zcjW{xy+W|*?Fs~pa}PXSR7q%vnmubDwCGuH9H8ot>n=%=+E8peruU3OFK7Vn*+nYr zY-rTd)o`cHE}L|txh?q06yk9S)qXq?`@WgXn;b7q>fL28K?TsM=i%+#A&M+mZ(-Y> zza-;*NWc3Eh$1ZBbX+2S&Lmw=Xz~eJ10ABs%j<vR(;v?{#|m<FlrBr_F=0y4fa?+} z`{jb<n=i=Kv1Pddl^|e98p6N<%J2CE8`)mS-tk~FbQKSdjop`URKW3986pnhb8~eF zf`D2jFneNLpW3t!BU=HLAW)F2>tQMWAu%9|Amn`OH7O@8ntkiCf+orroacZxEJSgV iyA!%M=<h~*oBsiEg`9O8l+j860000<MNUMnLSTZ;tfA5X literal 0 HcmV?d00001 diff --git a/components/exctrls/images/tradiobuttonex.png b/components/exctrls/images/tradiobuttonex.png new file mode 100644 index 0000000000000000000000000000000000000000..440e001cfc4a637df0d331ce80acb64acce5f894 GIT binary patch literal 768 zcmV+b1ONPqP)<h;3K|Lk000e1NJLTq000;O000;W1^@s6;CDUv00004b3#c}2nYxW zd<bNS00009a7bBm000ie000ie0hKEb8vp<R8FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H10)0tDK~zYI?UXT0Q&AL#zt=XUXdp3$LTDU}s0<bjjk|*;B5Z1$ zSQ?CRkiiAQKyH}|F-;7Ki)n;15DmD9i>s40tw|eVATTNPQ8Wsuh1$O3@JidXuVQgC z{>e+;`R}>^f6qPlUigoH9@;?(N+~xm2=oG{3-mJZ5|~s<r4MZo2n250?e?(G=WFtM zy|~?O0Mh9+i9~{UJie35<@`#ixj!hTT09gA8M>}ZEu>N@357z2QtC!s6)2_L4u@l9 zU|^uBwYBxP3(<9*a5%hev)MXEMn*O($89y{4f=e(qIfn7LqpKs4jvB-51Sl7TU#65 z-QCUEZ1$d&P%VQ#uh&})Mn_>{0#YehUx)E=liNojx33<9Q!Onm#UK)?PMDr9{TG_w zQICO?2bx8v?Dn!`k*YZd8LVz@mipD-U!73S6>?Xtgd8;t77~fX1&_xAz}T2c&dij= z1A$U<GMNN;VI@?ng2{M1zN70p08S?agRr({e!-y0fq6`0vDg+cX<31Yd<7aS2SmhX z7zP^~8`QcBx~?-jJ1hBo-mjGUWN832oWJwR8GCev`PU!0`!LSlPlIeO&yR)cOifK~ zcXf3&S3VWVWRh4cwp9>+USlWB9xQ#>A#Vt`E}h`r$`{6;E@8K67$S6bcAkqyqqn2c zXfJT4FmD-Hz<lg1S6;pqnTDP@+0EufMHYq2&&2=Wt?Y9mQnh{cpm>T_*1w@5i}<ge z<netU7tbEU>8N!E>VSx3T-W9$vzL=M?>|f4Fe)F-zpubBgvQ=dpN_g5v>$h|^rXk~ y7S{t!L^6P@ocz9<!=`C8HrN4<XquKetidm-$21%YCFE`Z0000<MNUMnLSTZ`Q%wl~ literal 0 HcmV?d00001 diff --git a/components/exctrls/images/tradiobuttonex_150.png b/components/exctrls/images/tradiobuttonex_150.png new file mode 100644 index 0000000000000000000000000000000000000000..046461b248b7f0f876342395410e2a7e1a9b1e4a GIT binary patch literal 1175 zcmV;I1Zew-P)<h;3K|Lk000e1NJLTq001Na001Ni1^@s6;Q*MJ00004b3#c}2nYxW zd<bNS00009a7bBm000ie000ie0hKEb8vp<R8FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H11Qba`K~z|U?U!F@TXhu2KfimsB`Itr3pgCCw~0g0R>sh^#XboW z+?Ea8gT&3fj20)9KKS1Bhkev#Fmw|w)ReiFF$Kf^q3F{J>GrS<%w&%BCQ+2Vq<?}W z71QHGnzpxTdi!Vf#eLxt?)jbHIiGv(?|1Gm@J!G2O#d^fjZD^Bw<x9dipWj?rPLFp z)V*jln)=UxVzHPnpU-!T$os$xHQXL6r7nobXIksxrU7ZKUk65j-Jwv3=H_NB%VPWX z?Ep+oO_9xJNu^SZkB<XnfQZ&Q`P6~5*6#pAfk41><j4^$%UaPtlgW@sB*^7*Mc|~? z`pQ!SnybuDmSuS&kqBO|cQw-}7K;oI50lMii@+PJsg&X75R1ipz(^nvSQ89@*Xtz` ziLhhG4lgi#?%X-ghJo_=d^fN=91brFlT5<dv*jzf%=7zvK8_we3eYq$G12Xoug=Xu zL{5i7A)1<+9QMgcIDQ;%+;I9IIs}(4K|{lW&9W?l!63G6p9apXlyAifX{}p;P)kco z)!1?{0N1a>i4)cK=H_OAJzDFR*A1kU+6&Ow*yyk)lgq<YbnP14zU{PCc4Z~zx`0IF zd4O%(wmIyhqbtoS>((vjeWh0cyVeb)loI#UwdOkSMWph(VobLR{RxoI=N<Nr4tJoX zK8s@fv2Gy4Fz!RS>n>J%dwDn2QaB8)txj7dhH0AjSIX%st@W=%LqoDulanHc4~u~F z3Wv*MOBIjDMQi=YEuU*20=^g@AOG04ZGyqzys@DHu3jzgrqR*zn(63({rjr}WHK3S z+eRsM{%HVx3UntD39GBCi~9O{$6#w~_3K%zLZLt+kpQs$e!n}Ah~xpcZ;UFHN+_i$ z6bkr!KDW5@Fe4))<Z`*9VHofB^z_WQ%`t$jz!sOQ&NNL7!(ec5kaRlj3X@K!85kHK zo6Q!LQtw5h(XrJ$L><5@UEg!}^Eat8%gtlo=^wcFcyhtXG$>v_hG{4!CMGiRc-#sG zgH;E#>FH@QnGC5^3fr~;Y#`Ft*LQnq;#yK7GUGk;&DN>!-o~@V*#)BU6#W+;U>b_Q zt{%g)#b9h~tl{R(o1MT1z@8d$A1S3S`2GI#t9ea}RdZ4I&B2^{E6CSBF7%<cwziq} z_VySM>+kP>sZ=WMQ%daw5RoUQY2NMa?fqpf5DtmRjOXALDgIrW?-|jreijZ~mQ$bH zk<#p}EE#t_WNk!MojMv%@zKQxbRG=y<%h3gC`IFQ^_a$n{xPjyb8AQnGyllL-=>8F zm!<PexiU4hsTFbniHO{Jn3LKpn-5gAs9$^0znS@5RR=ITEBFq7ThnP*z{l^y;i@yv pW=M(14CQXUT6vXHYw9g)(Z4`62V(FJ`I!I!002ovPDHLkV1g3DJ*@x$ literal 0 HcmV?d00001 diff --git a/components/exctrls/images/tradiobuttonex_200.png b/components/exctrls/images/tradiobuttonex_200.png new file mode 100644 index 0000000000000000000000000000000000000000..40795d3f0492a9959161dd3e9ef742027750f401 GIT binary patch literal 1491 zcmV;^1uXiBP)<h;3K|Lk000e1NJLTq001xm001xu1^@s6R|5Hm00004b3#c}2nYxW zd<bNS00009a7bBm000ie000ie0hKEb8vp<R8FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H11yD&uK~!jg?U-L^Q&$|vKj+>w9i>T~`-kgb9n&`0m#+S6L2C<Y z2D+7$>BDq5Ve@5z?9bb5a&J4>o<x+voMW&JN2MB8@FA|SW{S@0wpJ>(X&G~DbpzM6 zq~c6x?%6{Uo8V2or)ld;zYqxbch2{GfA{>({r%1l7O{v$EMgIhSj5AFm`kp<wzgW! zvYLSnz%t;OwC#7`FW@ZDWf;cKbGGPQa2STM9cTwC9nOgX2MohF1;~OHFJy9id;9ar zWb$iZEkIdW8C6wPR9059WXTe|UM~Rgc$|re35JJ<85$ZQ9*<`zF6z4eYHMrjuM6&g zVHoRyQ^1oRkB5yLH&R<$ix4wYN+}r}9HhIun~8}DfH7c)VHn@dM#roiFio>bN_hq- z^7(uO0s-7^w-U$H)D+=xm{=?ZFr{hQmS8aWy{Z;nbwYc4`*W6ModG=S)~%ztxfz$s zrAmXY>r_`)bLY+-Mn^|=DdkIzjg8@GH2RMUkEXb;NG6jVz!N^755M34z>t}dObGmb zKUGy#0FMD*q8JrQ4w$C716b?zdI<yq1&Pl<2ti9r3m%ULpx!ji7M1xb4hSh_8$eT2 zQ{I?QB;fF2sH=n0($uf64vrj2+3etUyJ>7}1VBppfr<l;U!h?btAWd9Wo7K&zu)%l zkB-9Dt#IXvJx5IqoIVZZ<@ej9lpH#Ah<H4XrfIdoV6e}LE|nc<2JrcOw&D|snZ*P2 z_rtbr_B$bj$eabz+1wFS99R#qeED))<I$rti_f6HAC4Wfx2;&QBJ+2HQ&B1olmjeX zy42Qq{J3+Xd}Fh?ZGy}mDh@0IC@n3uHD0^soapLR`{(H|5zLbV1r=R6i{@EX%6m&# zj=2=p{S#nva?;kga;0;kd}Fh?Z#d;rabOf+e0<#2xNDblqMbYKpJ#E;iv#BXMn*<# zjeGZ|zVi=4O-*X79fpU8Gk+Zybt(>Y0SpZdWyk706c?wG)J#rf5}92Rw=C<P1Dd9F zIThvD0mCr*fLJ^pr@z17c1(FW^!BF8hU`+JzCQJ=x7R)k1N8OvWiC2{!C=rqMoB@X z4wk?HptGl^hwAET+j_|DhJE`|Kc`3}5=5g>0E7_lD`--yM$>oLi<6U+bar;yhLS3z zl!U`!Zr!>Ca6S|YeWNnpB@0v`rJQwHEu|z9iDY7bOxN|@DCQg03(b8vapDA1Q&Y<G z5{U#I9UWY{bO};d%0O#t>o2N2BC`Wc??ovt(usUHT}L&%)5E!ezwev$xVM;_r(OoY zG)+oMN@!?kV9lB}X!+7?8d6FI1_tQv?#_hGm=I!nC=}{Z6%Xk{d^ahHUb%@&&kcot z6G_`iW0N`c@DI0d->#2DB3!s|A#Yn{e0-ddkr86CSaw_GJg_Si3SFO#cu4QS-K3=G z#gn*n!GDogat2sZ^5(&x_&hS*y3sYk-7~3iLI}+?&6e~{%I9$1K~jya@N9+WIFPg? zF8|3KVfn-ZIgW~~6WrF;_Ci)`@oA=8i+=z&gb-(?lwF2l*e8-%Aa5R!65cx8mm_}n zmI}W3_Ih5j^fl1YRdB9gd*Uo9dE?-Zd~$mFqp-(c!KZJpS}<Ww;R476K_(-7)%{z} zo8RNF;InsEqY1(4%13#`t=tcrH`s<y(wfdC+2R2%f4pg7lsQCd2h!aYnNEVQ`zsJa zplgEHHa*K6^snX(wmYB+!5)9b0!x`4avYEn#OBTiPTIWcDZc%vVctZkK!emF_LdYm to|G>`KMaUMt5Go*9o_;^a5eg{@joY=?@u*di<|%e002ovPDHLkV1hYVweJ7` literal 0 HcmV?d00001 diff --git a/components/exctrls/images/tradiogroupex.png b/components/exctrls/images/tradiogroupex.png new file mode 100644 index 0000000000000000000000000000000000000000..696a2d8a81fd11ca355c611eca6799ff12b3ca18 GIT binary patch literal 670 zcmV;P0%84$P)<h;3K|Lk000e1NJLTq000;O000;W1^@s6;CDUv00004b3#c}2nYxW zd<bNS00009a7bBm000ie000ie0hKEb8vp<R8FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H10vkz0K~zYI)s;Vp6G0Tlf0Ij62u83FY_t$GyN#Oxn{b^$1;NIc zMAS-1Ay#R$6C2U$8mr?f!FahOg`E~Ff?Ojmh)S}YHYb9eV6F(+m3<a5l3XVL?ri+3 zH*aRX@4auB8Tj9!Ovx+v-)w@)PS~?qzqh=+CxO+~)t@4gdiMU{)J<2Noymksb;{-P zp}{ylJSifnLZQIxFVFe#`MZ(e^vR=?N+si57=|>P&D3x%zI#zJndHfXYlz6O@XXjW z28fv|`B&s%=s-m9JdakZg>Bntt@liB6aZ+q+pMhQs8n3E*5vc~i39`GYPI<VEX$%& zaj`555uskM&#ZhQi?r5gt>?C7VT(M^qup*}+cud@W^QtmkWs+*eR8=RuIu7B4uwKt zVub+f>+6eIWLXxj>yl2V0od5sSh(`39b_^Y9LE_GUL>9>;CUXt?_*ik*cmL^MO;LB zKw>zV&1T7F`;Te2+of168aEH4TCJJ|fS7&rrTVmXj_Rwg+<5pFfTfrscySr%U!uKs z5d+xzwu1;<KYN@z#s0xYA`I5=$D)zP?(W)09@TtqT{=Nb^}mW<uSXCB1VJz}4#UuB z5df|2&R_D!w>4)@9cC$}Xfzr`QDm&n1f^7GBq1VU{Cr))Uy;1q`XNyyyOEf{H5cLl z3Ps{VV`SpkkppZ$y|^E40F;P?Ot=q!kW@;In>#0d1Eic_0;D5%hyVZp07*qoM6N<$ Ef_e%rM*si- literal 0 HcmV?d00001 diff --git a/components/exctrls/images/tradiogroupex_150.png b/components/exctrls/images/tradiogroupex_150.png new file mode 100644 index 0000000000000000000000000000000000000000..651b7456b0f7872aeb229c4ea261eaa0a1d6d127 GIT binary patch literal 785 zcmV+s1Md8ZP)<h;3K|Lk000e1NJLTq001Na001Ni1^@s6;Q*MJ00004b3#c}2nYxW zd<bNS00009a7bBm000ie000ie0hKEb8vp<R8FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H10**;UK~z|U?U+qS6mb;5fB)Gv3lx$L1JR`qmvx3EY%jalj7sqo z^l{sSQX|;tA_jKz;4useg;0?|w9Z=5#mk<GK4xc^4LS%N#zSFA$d>aul&j>fGpx>L z;O{mwZ{GXOn|U+*2ln{SP+kF-2Q&9U^#FAqp+5v3P7fq*ZAU2V6|nSq@dgm`k*f~4 z32aNKY==fiN56?k1HgDjA>uQcf~!e?!Ahy+?(Xi!zxS8zkcc#-QYio&_a!sW-tcqP z-LgP9q;ct7E2-3JUqTZT6Afkac%9hS)ka@eo3A1}fmX9hKG}6bp0~_(U2NONaU67A z$1n^uEx7CO6tZob`S~oFOf$)(g;EOBH2<BD<2Yn85oTr*07Rn>rfHH+r>iPBJUqNT zAzj!1bVf)d3g~*wSK?~%6fz70r4-SqgRaLg3?mQ`-$I(EVVY*3&&@`5-IRG|wr!hi zHcL}e6P9I>%jE)-0iRPAi9|>w5~b~Grp)gU_?j}yvJeppg#xkIE}1gNafrqCrff5N zQzjxSz`jZW@pzngJPtrInPhHmF7R)ehL6?98oI9gN?a{rfacUJ`LXkawBKGQd!F;| z<I;MNrm-@6fsm$XX=w>WWDN~qVevbw?)qxs?Z{c=#YOtBL;(<_bX+Ol&8%Li9Cybp zhNkjdYd>1*TrDUoG%#i{_&CS)j$@46j@L{E%Dji}JbKU2RG!|><2*>8LMezGX)JYV za&i*a-Fl7Y2c^_<*#ja{tZ#oJE5%jGFMJiwOiS;*SK_)-iHfg?9bu!8h{)>?U$&A7 zTxioRYdvu=$a<cH0YqSb$J2^IhYua#(^O|ogp`OB@&5O-k$R<6F({#5N+30*zs>f6 P00000NkvXXu0mjfILlng literal 0 HcmV?d00001 diff --git a/components/exctrls/images/tradiogroupex_200.png b/components/exctrls/images/tradiogroupex_200.png new file mode 100644 index 0000000000000000000000000000000000000000..1081a1d7cb05fb28b99384235154aa1ac9a6b030 GIT binary patch literal 923 zcmV;M17!S(P)<h;3K|Lk000e1NJLTq001xm001xu1^@s6R|5Hm00004b3#c}2nYxW zd<bNS00009a7bBm000ie000ie0hKEb8vp<R8FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H10~kp}K~!jg?U_$V6j2<<KX25Etvr}u&{E0{1i`|x;>kZG_DAT@ zMWsWfNU;Y`7G64bvWti4pb3HrUCdjA<)I=^i?K`7cA6!EE|p0o8D_h$!@8rZv%53< zZ`#}sEbP2LJKx`X@AtlMW}!lby^XNi@Y3kg1tFOb$YVC|@exVj;jNY@Hv3tk+G@jJ zKfaA4uqz(G4U7ZFbLg9uv@<X<G4Tp$OI|;ESD)97dQL6p(|s`z2(+73WSW7t?H6mS z-}4cWFKy{6vM!<i{(b=FLJ#=7@-wHqH`E<6e#b7;)6*I2E$6_<l~!(#&-3Z)sx1LW z>kl(>wbfQ1J6H~&z1hRU<WSLs<ci|7fPAS0;53$V03aTZ6AFchMxy|zs*2C&Ls6U? z2U`Jz!(lWnLNKTTu(qZF;P?9rugoI00?>7xU{Gahs&lhM;&SOm0KL7v1raqnJ8QXZ z-g7U6<?~=sur&j!s%|bDi4f4zqT}&+je0vHS14=+;Pd$aaJh5<RMl&gIfbnN6h*=B z_dA-jJE{0U+4(>u5<yiHKA>r3=mV;%(%G5t0VyTAUN|4fWf$9fKA>qDQc55(BVKPA z`hcz{e8B6i_(0xNe86cRP^6T?>|@qA=>u-J8@JnyrfG!3;Zkc(LCEBmk|Q%SGxYWK z0mXS9NWK>e1Oldm`v3UA>6<UuU!(AJw8N-uAAZi_+uiRpkJRpCdF~<r9*@T;cgzR= z#3V1@e4)B3bu`yE;-;NeR(~7co%Vr@ODK4+i`cUs2D_V5J8fGwPVPXk89-9c&UB7T z$@Q_<OwGSfDR+3;r;v8|q=ccnp%n4`=Ne0)$rh#o#HHlg*b?K9qej_acN6!An@c9l zD%G%i3C%2gNE!LT?j|O0G@}SX%gH(p?AwYv+xz=sV|tW`#kZ1SyLd<>wgW7tt4Pei z=3ix!gz$m$jVOwsN)cS{I*~DH(>Y|RFr5KK2nPPagf(MV4v4ea-SmvVfrUFAS=*HY xBO7ZE)>!Vez7Z>mm|V#FG%6fE&6WKV*<bz^4?;~&F$w?x002ovPDHLkV1oE2tJnYl literal 0 HcmV?d00001 diff --git a/components/exctrls/source/checkctrlsreg.res b/components/exctrls/source/checkctrlsreg.res new file mode 100644 index 0000000000000000000000000000000000000000..a2679ce1107a4b8735787e8192bdcd9029ba80db GIT binary patch literal 14848 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 literal 0 HcmV?d00001 diff --git a/components/exctrls/source/excheckctrls.pas b/components/exctrls/source/excheckctrls.pas new file mode 100644 index 000000000..0a7b06aa4 --- /dev/null +++ b/components/exctrls/source/excheckctrls.pas @@ -0,0 +1,2177 @@ +{ Extended checked controls (radiobutton, checkbox, radiogroup, checkgroup) + + Copyright (C) 2020 Lazarus team + + This library is free software; you can redistribute it and/or modify it + under the same terms as the Lazarus Component Library (LCL) + + See the file COPYING.modifiedLGPL.txt, included in the Lazarus distribution, + for details about the license. + +} + +unit ExCheckCtrls; + +{$mode objfpc}{$H+} + +interface + +uses + LCLType, LCLIntf, LCLProc, LMessages, + Graphics, Classes, SysUtils, Types, Themes, Controls, + StdCtrls, ExtCtrls, ImgList; + +type + TGetImageIndexEvent = procedure (Sender: TObject; AHover, APressed, AEnabled: Boolean; + AState: TCheckboxState; var AImgIndex: Integer) of object; + + { TCustomCheckControlEx } + + TCustomCheckControlEx = class(TCustomControl) + private + type + TCheckControlKind = (cckCheckbox, cckRadioButton); + private + FAlignment: TLeftRight; + FAllowGrayed: Boolean; + FThemedBtnSize: TSize; + FBtnLayout: TTextLayout; + FDistance: Integer; // between button and caption + FDrawFocusRect: Boolean; + FFocusBorder: Integer; + FGroupLock: Integer; + FHover: Boolean; + FImages: TCustomImageList; + FImagesWidth: Integer; + FKind: TCheckControlKind; + FPressed: Boolean; + FReadOnly: Boolean; + FState: TCheckBoxState; + FTextLayout: TTextLayout; + FThemedCaption: Boolean; +// FTransparent: Boolean; + FWordWrap: Boolean; + FOnChange: TNotifyEvent; + FOnGetImageIndex: TGetImageIndexEvent; + function GetCaption: TCaption; + function GetChecked: Boolean; + procedure SetAlignment(const AValue: TLeftRight); + procedure SetBtnLayout(const AValue: TTextLayout); + procedure SetCaption(const AValue: TCaption); + procedure SetChecked(const AValue: Boolean); + procedure SetDrawFocusRect(const AValue: Boolean); + procedure SetImages(const AValue: TCustomImageList); + procedure SetImagesWidth(const AValue: Integer); + procedure SetState(const AValue: TCheckBoxState); + procedure SetTextLayout(const AValue: TTextLayout); + procedure SetThemedCaption(const AValue: Boolean); + //procedure SetTransparent(const AValue: Boolean); + procedure SetWordWrap(const AValue: Boolean); + + protected + procedure AfterSetState; virtual; + procedure CalculatePreferredSize(var PreferredWidth, PreferredHeight: Integer; + {%H-}WithThemeSpace: Boolean); override; + procedure CMBiDiModeChanged(var {%H-}Message: TLMessage); message CM_BIDIMODECHANGED; + procedure CreateHandle; override; + procedure DoAutoAdjustLayout(const AMode: TLayoutAdjustmentPolicy; + const AXProportion, AYProportion: Double); override; + procedure DoClick; + procedure DoEnter; override; + procedure DoExit; override; + procedure DrawBackground; + procedure DrawButton(AHovered, APressed, AEnabled: Boolean; AState: TCheckboxState); + procedure DrawButtonText(AHovered, APressed, AEnabled: Boolean; + AState: TCheckboxState); + function GetBtnSize: TSize; virtual; + function GetDrawTextFlags: Cardinal; + function GetTextExtent(const ACaption: String): TSize; + function GetThemedButtonDetails(AHovered, APressed, AEnabled: Boolean; + AState: TCheckboxState): TThemedElementDetails; virtual; abstract; +// procedure InitBtnSize(Scaled: Boolean); + procedure LockGroup; + procedure KeyDown(var Key: Word; Shift: TShiftState); override; + procedure KeyUp(var Key: Word; Shift: TShiftState); override; + procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override; + procedure MouseEnter; override; + procedure MouseLeave; override; + procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override; + procedure Paint; override; + procedure TextChanged; override; + procedure UnlockGroup; + procedure WMSize(var Message: TLMSize); message LM_SIZE; + + property Alignment: TLeftRight read FAlignment write SetAlignment default taRightJustify; + property AllowGrayed: Boolean read FAllowGrayed write FAllowGrayed default False; + property ButtonLayout: TTextLayout read FBtnLayout write SetBtnLayout default tlCenter; + property Caption: TCaption read GetCaption write SetCaption; + property Checked: Boolean read GetChecked write SetChecked default false; + property DrawFocusRect: Boolean read FDrawFocusRect write SetDrawFocusRect default true; + property Images: TCustomImageList read FImages write SetImages; + property ImagesWidth: Integer read FImagesWidth write SetImagesWidth default 0; + property ReadOnly: Boolean read FReadOnly write FReadOnly default false; + property State: TCheckBoxState read FState write SetState default cbUnchecked; + property TextLayout: TTextLayout read FTextLayout write SetTextLayout default tlCenter; + property ThemedCaption: Boolean read FThemedCaption write SetThemedCaption default true; + //property Transparent: Boolean read FTransparent write SetTransparent default true; + property WordWrap: Boolean read FWordWrap write SetWordWrap default false; + property OnChange: TNotifyEvent read FOnChange write FOnChange; + property OnGetImageIndex: TGetImageIndexEvent read FOnGetImageIndex write FOnGetImageIndex; + + public + constructor Create(AOwner: TComponent); override; + + end; + + { TCustomCheckboxEx } + + TCustomCheckboxEx = class(TCustomCheckControlEx) + private + protected + function GetThemedButtonDetails(AHovered, APressed, AEnabled: Boolean; + AState: TCheckboxState): TThemedElementDetails; override; + public + constructor Create(AOwner: TComponent); override; + end; + + +{ TCheckBoxEx } + + TCheckBoxEx = class(TCustomCheckBoxEx) + published + //property Action; + property Align; + property Alignment; + property AllowGrayed; + property Anchors; + property AutoSize default true; + property BiDiMode; + property BorderSpacing; + property ButtonLayout; + property Caption; + property Checked; + property Color; + property Constraints; + property Cursor; + property DoubleBuffered; + property DragCursor; + property DragKind; + property DragMode; + property DrawFocusRect; + property Enabled; + property Font; + property Height; + property HelpContext; + property HelpKeyword; + property HelpType; + property Hint; + property Images; + property ImagesWidth; + property Left; + property Name; + property ParentBiDiMode; + property ParentColor; + property ParentDoubleBuffered; + property ParentFont; + property ParentShowHint; + property ReadOnly; + property ShowHint; + property State; + property TabOrder; + property TabStop; + property Tag; + property TextLayout; + property ThemedCaption; + property Top; + //property Transparent; + property Visible; + property Width; + property WordWrap; + property OnChange; + property OnChangeBounds; + property OnClick; + property OnContextPopup; + property OnDragDrop; + property OnDragOver; + property OnEditingDone; + property OnEndDrag; + property OnEnter; + property OnExit; + property OnGetImageIndex; + property OnKeyDown; + property OnKeyPress; + property OnKeyUp; + property OnMouseDown; + property OnMouseEnter; + property OnMouseLeave; + property OnMouseUp; + property OnMouseWheel; + property OnMouseWheelDown; + property OnMouseWheelUp; + property OnResize; + property OnStartDrag; + property OnUTF8KeyPress; + end; + + { TCustomRadioButtonEx } + + TCustomRadioButtonEx = class(TCustomCheckControlEx) + protected + procedure AfterSetState; override; + function GetThemedButtonDetails(AHovered, APressed, AEnabled: Boolean; + AState: TCheckboxState): TThemedElementDetails; override; + public + constructor Create(AOwner: TComponent); override; + published + end; + + { TRadioButtonEx } + + TRadioButtonEx = class(TCustomRadioButtonEx) + published + property Align; + property Alignment; + property Anchors; + property AutoSize default true; + property BiDiMode; + property BorderSpacing; + property ButtonLayout; + property Caption; + property Checked; + property Color; + property Constraints; + property Cursor; + property DoubleBuffered; + property DragCursor; + property DragKind; + property DragMode; + property DrawFocusRect; + property Enabled; + property Font; + property Height; + property HelpContext; + property HelpKeyword; + property HelpType; + property Hint; + property Images; + property ImagesWidth; + property Left; + property Name; + property ParentBiDiMode; + property ParentColor; + property ParentDoubleBuffered; + property ParentFont; + property ParentShowHint; + property PopupMenu; + property ReadOnly; + property ShowHint; + property State; + property TabOrder; + property TabStop; + property Tag; + property TextLayout; + property ThemedCaption; + //property Transparent; + property Visible; + property WordWrap; + property Width; + + property OnChange; + property OnChangeBounds; + property OnClick; + property OnContextPopup; + property OnDragDrop; + property OnDragOver; + property OnEnter; + property OnExit; + property OnKeyDown; + property OnKeyPress; + property OnKeyUp; + property OnMouseDown; + property OnMouseEnter; + property OnMouseLeave; + property OnMouseUp; + property OnMouseWheel; + property OnMouseWheelDown; + property OnMouseWheelUp; + property OnResize; + property OnStartDrag; + property OnGetImageIndex; + end; + + +{ TCustomCheckControlGroupEx } + + TCustomCheckControlGroupEx = class(TCustomGroupBox) + private + FAutoFill: Boolean; + FButtonList: TFPList; + FColumnLayout: TColumnLayout; + FColumns: integer; + FImages: TCustomImageList; + FImagesWidth: Integer; + FItems: TStrings; + FIgnoreClicks: boolean; + FReadOnly: Boolean; + FUpdatingItems: Boolean; + FOnClick: TNotifyEvent; + FOnGetImageIndex: TGetImageIndexEvent; + FOnSelectionChanged: TNotifyEvent; + procedure ItemEnter(Sender: TObject); + procedure ItemExit(Sender: TObject); + procedure ItemKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); virtual; + procedure ItemKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState); virtual; + procedure ItemKeyPress(Sender: TObject; var Key: Char); virtual; + procedure ItemUTF8KeyPress(Sender: TObject; var UTF8Key: TUTF8Char); virtual; + procedure ItemResize(Sender: TObject); + procedure SetAutoFill(const AValue: Boolean); + procedure SetColumnLayout(const AValue: TColumnLayout); + procedure SetColumns(const AValue: integer); + procedure SetImages(const AValue: TCustomImageList); + procedure SetImagesWidth(const AValue: Integer); + procedure SetItems(const AValue: TStrings); + procedure SetOnGetImageIndex(const AValue: TGetImageIndexEvent); + procedure SetReadOnly(const AValue: Boolean); + protected + procedure UpdateAll; + procedure UpdateControlsPerLine; + procedure UpdateInternalObjectList; + procedure UpdateItems; virtual; abstract; + procedure UpdateTabStops; + property AutoFill: Boolean read FAutoFill write SetAutoFill default true; + property ColumnLayout: TColumnLayout read FColumnLayout write SetColumnLayout default clHorizontalThenVertical; + property Columns: Integer read FColumns write SetColumns default 1; + property Images: TCustomImageList read FImages write SetImages; + property ImagesWidth: Integer read FImagesWidth write SetImagesWidth default 0; + property Items: TStrings read FItems write SetItems; + property ReadOnly: Boolean read FReadOnly write SetReadOnly default false; + property OnClick: TNotifyEvent read FOnClick write FOnClick; + property OnGetImageIndex: TGetImageIndexEvent read FOnGetImageIndex write SetOnGetImageIndex; + property OnSelectionChanged: TNotifyEvent read FOnSelectionChanged write FOnSelectionChanged; + public + constructor Create(AOwner: TComponent); override; + destructor Destroy; override; + function CanModify: boolean; virtual; + procedure FlipChildren(AllLevels: Boolean); override; + function Rows: integer; + end; + + { TCustomRadioGroupEx } + TCustomRadioGroupEx = class(TCustomCheckControlGroupEx) + private + FCreatingWnd: Boolean; + FHiddenButton: TRadioButtonEx; + FItemIndex: integer; + FLastClickedItemIndex: Integer; + FReading: Boolean; + procedure Changed(Sender: TObject); + procedure Clicked(Sender: TObject); + function GetButtonCount: Integer; + function GetButtons(AIndex: Integer): TRadioButtonEx; + procedure SetItemIndex(const AValue: Integer); + protected + procedure CheckItemIndexChanged; virtual; + procedure InitializeWnd; override; + procedure ItemKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); override; + procedure ReadState(AReader: TReader); override; + procedure UpdateItems; override; + procedure UpdateRadioButtonStates; virtual; + property ItemIndex: Integer read FItemIndex write SetItemIndex default -1; + public + property ButtonCount: Integer read GetButtonCount; + property Buttons[AIndex: Integer]: TRadioButtonEx read GetButtons; + published + constructor Create(AOwner: TComponent); override; + end; + + { TRadioGroupEx } + TRadioGroupEx = class(TCustomRadioGroupEx) + published + property Align; + property Anchors; + property AutoFill; + property AutoSize; + property BiDiMode; + property BorderSpacing; + property Caption; + property ChildSizing; + property Color; + property ColumnLayout; + property Columns; + property Constraints; + property Cursor; + property DoubleBuffered; + property DragCursor; + property DragMode; + property Enabled; + property Font; + property Height; + property HelpContext; + property HelpKeyword; + property HelpType; + property Hint; + property Images; + property ImagesWidth; + property ItemIndex; + property Items; + property Left; + property Name; + property ParentBiDiMode; + property ParentColor; + property ParentFont; + property ParentShowHint; + property PopupMenu; + property ReadOnly; + property ShowHint; + property TabOrder; + property TabStop; + property Tag; + property Top; + property Visible; + property Width; + property OnChangeBounds; + property OnClick; + property OnDblClick; + property OnDragDrop; + property OnDragOver; + property OnEndDrag; + property OnEnter; + property OnExit; + property OnGetImageIndex; + property OnKeyDown; + property OnKeyPress; + property OnKeyUp; + property OnMouseDown; + property OnMouseEnter; + property OnMouseLeave; + property OnMouseUp; + property OnMouseWheel; + property OnMouseWheelDown; + property OnMouseWheelUp; + property OnResize; + property OnSelectionChanged; + property OnStartDrag; + property OnUTF8KeyPress; + end; + + TCustomCheckGroupEx = class(TCustomCheckControlGroupEx) + private + FOnItemClick: TCheckGroupClicked; + procedure Clicked(Sender: TObject); + procedure DoClick(AIndex: integer); + function GetButtonCount: Integer; + function GetButtons(AIndex: Integer): TCheckBoxEx; + function GetChecked(AIndex: integer): boolean; + function GetCheckEnabled(AIndex: integer): boolean; + procedure RaiseIndexOutOfBounds(AIndex: integer); + procedure SetChecked(AIndex: integer; const AValue: boolean); + procedure SetCheckEnabled(AIndex: integer; const AValue: boolean); + protected + procedure DefineProperties(Filer: TFiler); override; + procedure Loaded; override; + procedure ReadData(Stream: TStream); + procedure UpdateItems; override; + procedure WriteData(Stream: TStream); +// procedure DoOnResize; override; + public + constructor Create(AOwner: TComponent); override; + property ButtonCount: Integer read GetButtonCount; + property Buttons[AIndex: Integer]: TCheckBoxEx read GetButtons; + public + property Checked[Index: integer]: boolean read GetChecked write SetChecked; + property CheckEnabled[Index: integer]: boolean read GetCheckEnabled write SetCheckEnabled; + property OnItemClick: TCheckGroupClicked read FOnItemClick write FOnItemClick; + end; + + { TCheckGroupEx } + + TCheckGroupEx = class(TCustomCheckGroupEx) + published + property Align; + property Anchors; + property AutoFill; + property AutoSize; + property BiDiMode; + property BorderSpacing; + property Caption; + property ChildSizing; + property ClientHeight; + property ClientWidth; + property Color; + property ColumnLayout; + property Columns; + property Constraints; + property DoubleBuffered; + property DragCursor; + property DragMode; + property Enabled; + property Font; + property Images; + property ImagesWidth; + property Items; + property ParentBiDiMode; + property ParentFont; + property ParentColor; + property ParentDoubleBuffered; + property ParentShowHint; + property PopupMenu; + property ReadOnly; + property ShowHint; + property TabOrder; + property TabStop; + property Visible; + + property OnChangeBounds; + property OnClick; + property OnDblClick; + property OnDragDrop; + property OnDragOver; + property OnEndDrag; + property OnEnter; + property OnExit; + property OnGetImageIndex; + property OnItemClick; + property OnKeyDown; + property OnKeyPress; + property OnKeyUp; + property OnMouseDown; + property OnMouseEnter; + property OnMouseLeave; + property OnMouseMove; + property OnMouseUp; + property OnMouseWheel; + property OnMouseWheelDown; + property OnMouseWheelUp; + property OnResize; + property OnStartDrag; + property OnUTF8KeyPress; + end; + +implementation + +uses + Math, LCLStrConsts, LResources; + +const + cIndent = 5; + + FIRST_RADIOBUTTON_DETAIL = tbRadioButtonUncheckedNormal; + FIRST_CHECKBOX_DETAIL = tbCheckBoxUncheckedNormal; + HOT_OFFSET = 1; + PRESSED_OFFSET = 2; + DISABLED_OFFSET = 3; + CHECKED_OFFSET = 4; + MIXED_OFFSET = 8; + +procedure DrawParentImage(Control: TControl; Dest: TCanvas); +var + SaveIndex: integer; + DC: HDC; + Position: TPoint; +begin + with Control do + begin + if Parent = nil then Exit; + DC := Dest.Handle; + SaveIndex := SaveDC(DC); + GetViewportOrgEx(DC, @Position); + SetViewportOrgEx(DC, Position.X - Left, Position.Y - Top, nil); + IntersectClipRect(DC, 0, 0, Parent.ClientWidth, Parent.ClientHeight); + Parent.Perform(LM_ERASEBKGND, DC, 0); + Parent.Perform(LM_PAINT, DC, 0); + RestoreDC(DC, SaveIndex); + end; +end; + +function ProcessLineBreaks(const AString: string; ToC: Boolean): String; +var + idx: Integer; + + procedure AddChar(ch: Char); + begin + Result[idx] := ch; + inc(idx); + if idx > Length(Result) then + SetLength(Result, Length(Result) + 100); + end; + +var + P, PEnd: PChar; +begin + if AString = '' then + begin + Result := ''; + exit; + end; + + SetLength(Result, Length(AString)); + idx := 1; + P := @AString[1]; + PEnd := P + Length(AString); + + if ToC then + // Replace line breaks by '\n' + while P < PEnd do begin + if (P^ = #13) then begin + AddChar('\'); + AddChar('n'); + inc(P); + if P^ <> #10 then dec(P); + end else + if P^ = #10 then + begin + AddChar('\'); + AddChar('n'); + end else + if P^ = '\' then + begin + AddChar('\'); + AddChar('\'); + end else + AddChar(P^); + inc(P); + end + else + // Replace '\n' by LineEnding + while (P < PEnd) do + begin + if (P^ = '\') and (P < PEnd-1) then + begin + inc(P); + if (P^ = 'n') or (P^ = 'N') then + AddChar(#10) + else + AddChar(P^); + end else + AddChar(P^); + inc(P); + end; + SetLength(Result, idx-1); +end; + +{ TCheckboxControlEx } + +constructor TCustomCheckControlEx.Create(AOwner: TComponent); +begin + inherited; + ControlStyle := ControlStyle + [csParentBackground, csReplicatable] - [csOpaque] + - csMultiClicks - [csClickEvents, csNoStdEvents]; { inherited Click not used } + + FAlignment := taRightJustify; + FBtnLayout := tlCenter; + FDrawFocusRect := true; + FKind := cckCheckbox; + FDistance := cIndent; + FFocusBorder := FDistance div 2; + FTextLayout := tlCenter; + FThemedCaption := true; +// FTransparent := true; + + AutoSize := true; + TabStop := true; +end; + +// Is called after the State has changed in SetState. Will be overridden by +// TCustomRadioButtonEx to uncheck all other iteme.s +procedure TCustomCheckControlEx.AfterSetState; +begin +end; + +procedure TCustomCheckControlEx.CalculatePreferredSize(var PreferredWidth, + PreferredHeight: Integer; WithThemeSpace: Boolean); +var + flags: Cardinal; + textSize: TSize; + R: TRect; + captn: String; + details: TThemedElementDetails; + btnSize: TSize; +begin + captn := inherited Caption; + if (captn = '') then + begin + btnSize := GetBtnSize; + PreferredWidth := btnSize.CX; + PreferredHeight := btnSize.CY; + exit; + end; + + Canvas.Font.Assign(Font); + + R := ClientRect; + btnSize := GetBtnSize; + dec(R.Right, btnSize.CX + FDistance); + R.Bottom := MaxInt; // Max height possible + + flags := GetDrawTextFlags + DT_CALCRECT; + + // rectangle available for text + if FThemedCaption then + begin + details := GetThemedButtonDetails(false, false, true, cbChecked); + if FWordWrap then + begin + with ThemeServices.GetTextExtent(Canvas.Handle, details, captn, flags, @R) do begin + textSize.CX := Right; + textSize.CY := Bottom; + end; + end else + with ThemeServices.GetTextExtent(Canvas.Handle, details, captn, flags, nil) do begin + textSize.CX := Right; + textSize.CY := Bottom; + end; + end else + begin + DrawText(Canvas.Handle, PChar(captn), Length(captn), R, flags); + textSize.CX := R.Right - R.Left; + textSize.CY := R.Bottom - R.Top; + end; + + PreferredWidth := btnSize.CX + FDistance + textSize.CX + FFocusBorder; + PreferredHeight := Max(btnSize.CY, textSize.CY + 2*FFocusBorder); +end; + +procedure TCustomCheckControlEx.CMBiDiModeChanged(var Message: TLMessage); +begin + Invalidate; +end; + +procedure TCustomCheckControlEx.CreateHandle; +var + w, h: Integer; +begin + inherited; + if (Width = 0) or (Height = 0) then begin + CalculatePreferredSize(w{%H-}, h{%H-}, false); + if Width <> 0 then w := Width; + if Height <> 0 then h := Height; + SetBounds(Left, Top, w, h); + end; +end; + +procedure TCustomCheckControlEx.DoAutoAdjustLayout( + const AMode: TLayoutAdjustmentPolicy; + const AXProportion, AYProportion: Double); +begin + inherited; + if AMode in [lapAutoAdjustWithoutHorizontalScrolling, lapAutoAdjustForDPI] then + begin + FDistance := Round(cIndent * AXProportion); + FFocusBorder := FDistance div 2; + end; +end; + +procedure TCustomCheckControlEx.DoClick; +begin + if FReadOnly then + exit; + + if AllowGrayed then begin + case FState of + cbUnchecked: SetState(cbGrayed); + cbGrayed: SetState(cbChecked); + cbChecked: SetState(cbUnchecked); + end; + end else + Checked := not Checked; +end; + +procedure TCustomCheckControlEx.DoEnter; +begin + inherited DoEnter; + Invalidate; +end; + +procedure TCustomCheckControlEx.DoExit; +begin + inherited DoExit; + Invalidate; +end; + +procedure TCustomCheckControlEx.DrawBackground; +var + R: TRect; +begin + R := Rect(0, 0, Width, Height); + Canvas.Brush.Style := bsSolid; + Canvas.Brush.Color := Color; + Canvas.FillRect(R); +end; + +procedure TCustomCheckControlEx.DrawButton(AHovered, APressed, AEnabled: Boolean; AState: TCheckboxState); +var + btnRect: TRect; + btnPoint: TPoint = (X:0; Y:0); + details: TThemedElementDetails; + imgIndex: Integer; + imgRes: TScaledImageListResolution; + btnSize: TSize; +begin + // Checkbox/Radiobutton size and position + btnSize := GetBtnSize; + case FAlignment of + taLeftJustify: + if not IsRightToLeft then btnPoint.X := ClientWidth - btnSize.CX; + taRightJustify: + if IsRightToLeft then btnPoint.X := ClientWidth - btnSize.CX; + end; + case FBtnLayout of + tlTop: btnPoint.Y := FFocusBorder; + tlCenter: btnPoint.Y := (ClientHeight - btnSize.CY) div 2; + tlBottom: btnPoint.Y := ClientHeight - btnSize.CY - FFocusBorder; + end; + btnRect := Rect(0, 0, btnSize.CX, btnSize.CY); + OffsetRect(btnRect, btnPoint.X, btnPoint.Y); + + imgIndex := -1; + if (FImages <> nil) and Assigned(FOnGetImageIndex) then + FOnGetImageIndex(Self, AHovered, APressed, AEnabled, AState, imgIndex); + + if imgIndex > -1 then + begin + ImgRes := FImages.ResolutionForPPI[FImagesWidth, Font.PixelsPerInch, GetCanvasScaleFactor]; + ImgRes.Draw(Canvas, btnRect.Left, btnRect.Top, imgIndex, AEnabled); + end else + begin + // Drawing style of button + details := GetThemedButtonDetails(AHovered, APressed, AEnabled, AState); + // Draw button + ThemeServices.DrawElement(Canvas.Handle, details, btnRect); + end; +end; + +procedure TCustomCheckControlEx.DrawButtonText(AHovered, APressed, AEnabled: Boolean; + AState: TCheckboxState); +var + R: TRect; +// textStyle: TTextStyle; + delta: Integer; + details: TThemedElementDetails; + flags: Cardinal; + textSize: TSize; + captn: TCaption; + btnSize: TSize; +begin + captn := inherited Caption; // internal string with line breaks + + if captn = '' then + exit; + + // Determine text drawing parameters + flags := GetDrawTextFlags; + + btnSize := GetBtnSize; + delta := btnSize.CX + FDistance; + R := ClientRect; + dec(R.Right, delta); + Canvas.Font.Assign(Font); + if FThemedCaption then + begin + R.Bottom := MaxInt; // max height for word-wrap + details := GetThemedButtonDetails(AHovered, APressed, AEnabled, AState); + with ThemeServices.GetTextExtent(Canvas.Handle, details, captn, flags, @R) do begin + textSize.CX := Right; + textSize.CY := Bottom; + end; + end else + begin + if not AEnabled then Canvas.Font.Color := clGrayText; + DrawText(Canvas.Handle, PChar(captn), Length(captn), R, flags + DT_CALCRECT); + textSize.CX := R.Right - R.Left; + textSize.CY := R.Bottom - R.Top; + end; + + R := ClientRect; + + case FTextLayout of + tlTop: + R.Top := 0; + tlCenter: + R.Top := (R.Top + R.Bottom - textSize.CY) div 2; + tlBottom: + R.Top := R.Bottom - textSize.CY; + end; + R.Bottom := R.Top + textSize.CY; + + if (FAlignment = taRightJustify) and IsRightToLeft then + begin + dec(R.Right, delta); + R.Left := R.Right - textSize.CX; + end else + begin + inc(R.Left, delta); + R.Right := R.Left + textSize.CX; + end; + + // Draw text + if FThemedCaption then + begin + ThemeServices.DrawText(Canvas, details, captn, R, flags, 0); + end else + begin + Canvas.Brush.Style := bsClear; + DrawText(Canvas.Handle, PChar(captn), Length(captn), R, flags); + end; + + // Draw focus rect + if Focused and FDrawFocusRect then begin + InflateRect(R, FFocusBorder, 0); + if R.Left + R.Width > ClientWidth then R.Width := ClientWidth - R.Left; + if R.Left < 0 then R.Left := 0; + //LCLIntf.SetBkColor(Canvas.Handle, ColorToRGB(clBtnFace)); + Canvas.Font.Color := clBlack; + LCLIntf.DrawFocusRect(Canvas.Handle, R); + end; +end; + +function TCustomCheckControlEx.GetBtnSize: TSize; +var + ImgRes: TScaledImageListResolution; +begin + if (FImages <> nil) then begin + ImgRes := FImages.ResolutionForPPI[FImagesWidth, Font.PixelsPerInch, GetCanvasScaleFactor]; + Result.CX := ImgRes.Width; + Result.CY := ImgRes.Height; + end else + begin + with ThemeServices do + if FKind = cckCheckbox then + Result := GetDetailSize(GetElementDetails(tbCheckBoxCheckedNormal)) + else + if FKind = cckRadioButton then + Result := GetDetailSize(GetElementDetails(tbRadioButtonCheckedNormal)); + //Result.CX := Scale96ToFont(Result.CX); + //Result.CY := Scale96ToFont(Result.CY); + end; +end; + +// Replaces linebreaks in the inherited Caption by '\n' (and '\' by '\\') so +// that line breaks can be entered at designtime. +function TCustomCheckControlEx.GetCaption: TCaption; +const + TO_C = true; +begin + Result := ProcessLineBreaks(inherited Caption, TO_C); +end; + +function TCustomCheckControlEx.GetChecked: Boolean; +begin + Result := (FState = cbChecked); +end; + +// Determine text drawing parameters for the DrawText command +function TCustomCheckControlEx.GetDrawTextFlags: Cardinal; +begin + Result := 0; + case FTextLayout of + tlTop: inc(Result, DT_TOP); + tlCenter: inc(Result, DT_VCENTER); + tlBottom: inc(Result, DT_BOTTOM); + end; + + if (FAlignment = taRightJustify) and IsRightToLeft then + inc(Result, DT_RIGHT) + else + inc(Result, DT_LEFT); + + if IsRightToLeft then inc(Result, DT_RTLREADING); + if FWordWrap then inc(Result, DT_WORDBREAK); +end; + +function TCustomCheckControlEx.GetTextExtent(const ACaption: String): TSize; +var + L: TStrings; + s: String; +begin + Result := Size(0, 0); + L := TStringList.Create; + try + L.Text := ACaption; + for s in L do + begin + Result.CY := Result.CY + Canvas.TextHeight(s); + Result.CX := Max(Result.CX, Canvas.TextWidth(s)); + end; + finally + L.Free; + end; +end; + (* +procedure TCustomCheckControlEx.InitBtnSize(Scaled: Boolean); +var + ImgRes: TScaledImageListResolution; +begin + if (FImages <> nil) then begin + if Scaled then begin + ImgRes := FImages.ResolutionForPPI[FImagesWidth, Font.PixelsPerInch, GetCanvasScaleFactor]; + FBtnSize.CX := ImgRes.Width; + FBtnSize.CY := ImgRes.Height; + end else + begin + FBtnSize.CX := FImages.Width; + FBtnSize.CY := FImages.Height; + end; + end else + begin + with ThemeServices do + if FKind = cckCheckbox then + FBtnSize := GetDetailSize(GetElementDetails(tbCheckBoxCheckedNormal)) + else if FKind = cckRadioButton then + FBtnSize := GetDetailSize(GetElementDetails(tbRadioButtonCheckedNormal)); + if Scaled then + begin + FBtnSize.CX := Scale96ToFont(FBtnSize.CX); + FBtnSize.CY := Scale96ToFont(FBtnSize.CY); + end; + end; +end; +*) + +procedure TCustomCheckControlEx.KeyDown(var Key: Word; Shift: TShiftState); +begin + inherited KeyDown(Key, Shift); + if (Key in [VK_RETURN, VK_SPACE]) and not (ssCtrl in Shift) and (not FReadOnly) then + begin + FPressed := True; + Invalidate; + end; +end; + +procedure TCustomCheckControlEx.KeyUp(var Key: Word; Shift: TShiftState); +begin + inherited KeyUp(Key, Shift); + if (Key in [VK_RETURN, VK_SPACE]) and not (ssCtrl in Shift) then + begin + FPressed := False; + DoClick; + end; +end; + +procedure TCustomCheckControlEx.LockGroup; +begin + inc(FGroupLock); +end; + +procedure TCustomCheckControlEx.MouseDown(Button: TMouseButton; + Shift: TShiftState; X, Y: Integer); +begin + inherited MouseDown(Button, Shift, X, Y); + if (Button = mbLeft) and FHover and not FReadOnly then + begin + FPressed := True; + Invalidate; + end; + SetFocus; +end; + +procedure TCustomCheckControlEx.MouseEnter; +begin + FHover := true; + Invalidate; + inherited; +end; + +procedure TCustomCheckControlEx.MouseLeave; +begin + FHover := false; + Invalidate; + inherited; +end; + +procedure TCustomCheckControlEx.MouseUp(Button: TMouseButton; + Shift: TShiftState; X, Y: Integer); +begin + inherited MouseUp(Button, Shift, X, Y); + if Button = mbLeft then begin + if PtInRect(ClientRect, Point(X, Y)) then DoClick; + FPressed := False; + end; +end; + +procedure TCustomCheckControlEx.Paint; +begin + { + if FTransparent then + DrawParentImage(Self, Self.Canvas) + else + DrawBackground; + } + DrawButton(FHover, FPressed, IsEnabled, FState); + DrawButtonText(FHover, FPressed, IsEnabled, FState); +end; + +procedure TCustomCheckControlEx.SetAlignment(const AValue: TLeftRight); +begin + if AValue = FAlignment then exit; + FAlignment := AValue; + Invalidate; +end; + +procedure TCustomCheckControlEx.SetBtnLayout(const AValue: TTextLayout); +begin + if AValue = FBtnLayout then exit; + FBtnLayout := AValue; + Invalidate; +end; + +procedure TCustomCheckControlEx.SetCaption(const AValue: TCaption); +const + FROM_C = false; +begin + if AValue = GetCaption then exit; + inherited Caption := ProcessLineBreaks(AValue, FROM_C); +end; + +procedure TCustomCheckControlEx.SetChecked(const AValue: Boolean); +begin + if AValue then + State := cbChecked + else + State := cbUnChecked; +end; + +procedure TCustomCheckControlEx.SetDrawFocusRect(const AValue: Boolean); +begin + if AValue = FDrawFocusRect then exit; + FDrawFocusRect := AValue; + Invalidate; +end; + +procedure TCustomCheckControlEx.SetImages(const AValue: TCustomImageList); +begin + if AValue = FImages then exit; + FImages := AValue; +// InitBtnSize(true); + InvalidatePreferredSize; + AdjustSize; +end; + +procedure TCustomCheckControlEx.SetImagesWidth(const AValue: Integer); +begin + if AValue = FImagesWidth then exit; + FImagesWidth := AValue; +// InitBtnSize(true); + InvalidatePreferredSize; + AdjustSize; +end; + +procedure TCustomCheckControlEx.SetTextLayout(const AValue: TTextLayout); +begin + if AValue = FTextLayout then exit; + FTextLayout := AValue; + Invalidate; +end; + +procedure TCustomCheckControlEx.SetThemedCaption(const AValue: Boolean); +begin + if AValue = FThemedCaption then exit; + FThemedCaption := AValue; + Invalidate; +end; + +procedure TCustomCheckControlEx.SetState(const AValue: TCheckboxState); +begin + if (FState = AValue) then exit; + FState := AValue; + if [csLoading, csDestroying, csDesigning] * ComponentState = [] then begin + if Assigned(OnEditingDone) then OnEditingDone(self); + if Assigned(OnChange) then OnChange(self); + { + // Execute only when Action.Checked is changed + if not CheckFromAction then begin + if Assigned(OnClick) then + if not (Assigned(Action) and + CompareMethods(TMethod(Action.OnExecute), TMethod(OnClick))) + then OnClick(self); + if (Action is TCustomAction) and + (TCustomAction(Action).Checked <> (AValue = cbChecked)) + then ActionLink.Execute(self); + end; + } + AfterSetState; + end; + Invalidate; +end; +{ +procedure TCustomCheckControlEx.SetTransparent(const AValue: Boolean); +begin + if AValue = FTransparent then exit; + FTransparent := AValue; + Invalidate; +end; +} + +procedure TCustomCheckControlEx.SetWordWrap(const AValue: Boolean); +begin + if AValue = FWordWrap then exit; + FWordWrap := AValue; + Invalidate; +end; + +procedure TCustomCheckControlEx.TextChanged; +begin + inherited TextChanged; + Invalidate; +end; + +procedure TCustomCheckControlEx.UnlockGroup; +begin + dec(FGroupLock); +end; + +procedure TCustomCheckControlEx.WMSize(var Message: TLMSize); +begin + inherited WMSize(Message); + Invalidate; +end; + + +{ TCustomRadioButtonEx } + +constructor TCustomRadioButtonEx.Create(AOwner: TComponent); +begin + inherited; + FKind := cckRadioButton; +// InitBtnSize(false); +end; + +{ Is called by SetState and is supposed to uncheck all other radiobuttons in the + same group, i.e. having the same parent. Provides a locking mechanism because + uncheding another radiobutton would trigger AfterSetState again. } +procedure TCustomRadioButtonEx.AfterSetState; +var + i: Integer; + C: TControl; +begin + if (FGroupLock > 0) or (Parent = nil) then + exit; + for i := 0 to Parent.ControlCount-1 do + begin + C := Parent.Controls[i]; + if (C is TCustomRadioButtonEx) and (C <> self) then + with TCustomRadioButtonEx(C) do + begin + LockGroup; + try + State := cbUnChecked; + finally + UnlockGroup; + end; + end; + end; +// Parent.Invalidate; +end; + +function TCustomRadioButtonEx.GetThemedButtonDetails( + AHovered, APressed, AEnabled: Boolean; AState: TCheckboxState): TThemedElementDetails; +var + offset: Integer = 0; + tb: TThemedButton; +begin + offset := ord(FIRST_RADIOBUTTON_DETAIL); + if APressed then + inc(offset, PRESSED_OFFSET) + else if AHovered then + inc(offset, HOT_OFFSET); + if not AEnabled then inc(offset, DISABLED_OFFSET); + if AState = cbChecked then inc(offset, CHECKED_OFFSET); + tb := TThemedButton(offset); + Result := ThemeServices.GetElementDetails(tb); +end; +(* + offset := 0 +const // hovered pressed state + caEnabledDetails: array [False..True, False..True, cbUnChecked..cbChecked] of TThemedElementDetails = + ( + ( + (tbRadioButtonUncheckedNormal, tbRadioButtonCheckedNormal), + (tbRadioButtonUncheckedPressed, tbRadioButtonCheckedPressed) + ), + ( + (tbRadioButtonUncheckedHot, tbRadioButtonCheckedHot), + (tbRadioButtonUncheckedPressed, tbRadioButtonCheckedPressed) + ) + ); + + caDisabledDetails: array [cbUnchecked..cbChecked] of TThemedButton = + (tbRadioButtonUncheckedDisabled, tbRadioButtonCheckedDisabled); +begin + if Enabled then + Result := caEnabledDetails[AHovered, APressed, AState] + else + Result := caDisabledDetails[AState]; +end; + *) + + +{==============================================================================} +{ TCustomCheckboxEx } +{==============================================================================} + +constructor TCustomCheckboxEx.Create(AOwner: TComponent); +begin + inherited; + FKind := cckCheckbox; +// InitBtnSize(false); +end; + +function TCustomCheckBoxEx.GetThemedButtonDetails( + AHovered, APressed, AEnabled: Boolean; AState: TCheckboxState): TThemedElementDetails; +var + offset: Integer = 0; + tb: TThemedButton; +begin + offset := ord(FIRST_CHECKBOX_DETAIL); + if APressed then + inc(offset, PRESSED_OFFSET) + else if AHovered then + inc(offset, HOT_OFFSET); + if not AEnabled then inc(offset, DISABLED_OFFSET); + case AState of + cbChecked: inc(offset, CHECKED_OFFSET); + cbGrayed: inc(offset, MIXED_OFFSET); + end; + tb := TThemedButton(offset); + Result := ThemeServices.GetElementDetails(tb); +end; + (* + +const // hovered pressed state + caEnabledDetails: array [False..True, False..True, cbUnchecked..cbGrayed] of TThemedButton = + ( + ( + (tbCheckBoxUncheckedNormal, tbCheckBoxCheckedNormal, tbCheckBoxMixedNormal), + (tbCheckBoxUncheckedPressed, tbCheckBoxCheckedPressed, tbCheckBoxMixedPressed) + ), + ( + (tbCheckBoxUncheckedHot, tbCheckBoxCheckedHot, tbCheckBoxMixedHot), + (tbCheckBoxUncheckedPressed, tbCheckBoxCheckedPressed, tbCheckBoxMixedPressed) + ) + ); + + caDisabledDetails: array [cbUnchecked..cbGrayed] of TThemedButton = + (tbCheckBoxUncheckedDisabled, tbCheckBoxCheckedDisabled, tbCheckBoxMixedDisabled); +var + tb: TThemedButton; +begin + if Enabled then + tb := caEnabledDetails[AHovered, APressed, AState] + else + tb := caDisabledDetails[AState]; + Result := ThemeServices.GetElementDetails(tb); +end; *) + + +{==============================================================================} +{ TCustomCheckControlGroupEx } +{==============================================================================} +constructor TCustomCheckControlGroupEx.Create(AOwner: TComponent); +begin + inherited; + FAutoFill := true; + FButtonList := TFPList.Create; + FColumns := 1; + FColumnLayout := clHorizontalThenVertical; + ChildSizing.Layout := cclLeftToRightThenTopToBottom; + ChildSizing.ControlsPerLine := FColumns; + ChildSizing.ShrinkHorizontal := crsScaleChilds; + ChildSizing.ShrinkVertical := crsScaleChilds; + ChildSizing.EnlargeHorizontal := crsHomogenousChildResize; + ChildSizing.EnlargeVertical := crsHomogenousChildResize; + ChildSizing.LeftRightSpacing := 6; + ChildSizing.TopBottomSpacing := 0; +end; + +destructor TCustomCheckControlGroupEx.Destroy; +var + i: Integer; +begin + for i:=0 to FButtonList.Count-1 do + TCustomCheckControlEx(FButtonList[i]).Free; + FButtonList.Free; + FItems.Free; + inherited; +end; + +function TCustomCheckControlGroupEx.CanModify: Boolean; +begin + Result := not FReadOnly; +end; + +procedure TCustomCheckControlgroupEx.FlipChildren(AllLevels: Boolean); +begin + // no flipping +end; + +procedure TCustomCheckControlGroupEx.ItemEnter(Sender: TObject); +begin + DoEnter; +end; + +procedure TCustomCheckControlGroupEx.ItemExit(Sender: TObject); +begin + DoExit; +end; + +procedure TCustomCheckControlGroupEx.ItemKeyDown(Sender: TObject; + var Key: Word; Shift: TShiftState); +begin + if Key <> 0 then + KeyDown(Key, Shift); +end; + +procedure TCustomCheckControlGroupEx.ItemKeyUp(Sender: TObject; var Key: Word; + Shift: TShiftState); +begin + if Key <> 0 then + KeyUp(Key, Shift); +end; + +procedure TCustomCheckControlGroupEx.ItemKeyPress(Sender: TObject; var Key: Char); +begin + if Key <> #0 then + KeyPress(Key); +end; + +procedure TCustomCheckControlGroupEx.ItemUTF8KeyPress(Sender: TObject; + var UTF8Key: TUTF8Char); +begin + UTF8KeyPress(UTF8Key); +end; + +procedure TCustomCheckControlGroupEx.ItemResize(Sender: TObject); +begin + // +end; + +function TCustomCheckControlGroupEx.Rows: integer; +begin + if FItems.Count > 0 then + Result := ((FItems.Count-1) div Columns) + 1 + else + Result := 0; +end; + +procedure TCustomCheckControlGroupEx.SetAutoFill(const AValue: Boolean); +begin + if FAutoFill = AValue then exit; + FAutoFill := AValue; + DisableAlign; + try + if FAutoFill then begin + ChildSizing.EnlargeHorizontal := crsHomogenousChildResize; + ChildSizing.EnlargeVertical := crsHomogenousChildResize; + end else begin + ChildSizing.EnlargeHorizontal := crsAnchorAligning; + ChildSizing.EnlargeVertical := crsAnchorAligning; + end; + finally + EnableAlign; + end; +end; + +procedure TCustomCheckControlGroupEx.SetColumnLayout(const AValue: TColumnLayout); +begin + if FColumnLayout = AValue then exit; + FColumnLayout := AValue; + if FColumnLayout = clHorizontalThenVertical then + ChildSizing.Layout := cclLeftToRightThenTopToBottom + else + ChildSizing.Layout := cclTopToBottomThenLeftToRight; + UpdateControlsPerLine; +end; + +procedure TCustomCheckControlGroupEx.SetColumns(const AValue: integer); +begin + if AValue <> FColumns then begin + if (AValue < 1) then + raise Exception.Create('TCustomRadioGroup: Columns must be >= 1'); + FColumns := AValue; + UpdateControlsPerLine; + end; +end; + +procedure TCustomCheckControlGroupEx.SetOnGetImageIndex(const AValue: TGetImageIndexEvent); +var + i: Integer; +begin + FOnGetImageIndex := AValue; + for i := 0 to FButtonList.Count - 1 do + TCustomCheckControlEx(FButtonList[i]).OnGetImageIndex := AValue; +end; + +procedure TCustomCheckControlGroupEx.SetImages(const AValue: TCustomImagelist); +var + i: Integer; +begin + if AValue = FImages then exit; + FImages := AValue; + for i:=0 to FButtonList.Count-1 do + TCustomCheckControlEx(FButtonList[i]).Images := FImages; +end; + +procedure TCustomCheckControlGroupEx.SetImagesWidth(const AValue: Integer); +var + i: Integer; +begin + if AValue = FImagesWidth then exit; + FImagesWidth := AValue; + for i := 0 to FButtonList.Count - 1 do + TCustomCheckControlEx(FButtonList[i]).ImagesWidth := FImagesWidth; +end; + +procedure TCustomCheckControlGroupEx.SetItems(const AValue: TStrings); +begin + if (AValue <> FItems) then + begin + FItems.Assign(AValue); + UpdateItems; + UpdateControlsPerLine; + end; +end; + +procedure TCustomCheckControlGroupEx.SetReadOnly(const AValue: Boolean); +var + i: Integer; +begin + if AValue = FReadOnly then exit; + FReadOnly := AValue; + for i := 0 to FButtonList.Count -1 do + TCustomCheckControlEx(FButtonList[i]).ReadOnly := FReadOnly; +end; + +procedure TCustomCheckControlGroupEx.UpdateAll; +begin + UpdateItems; + UpdateControlsPerLine; + OwnerFormDesignerModified(Self); +end; + +procedure TCustomCheckControlGroupEx.UpdateControlsPerLine; +var + newControlsPerLine: LongInt; +begin + if ChildSizing.Layout = cclLeftToRightThenTopToBottom then + newControlsPerLine := Max(1, FColumns) + else + newControlsPerLine := Max(1, Rows); + ChildSizing.ControlsPerLine := NewControlsPerLine; +end; + +procedure TCustomCheckControlGroupEx.UpdateInternalObjectList; +begin + UpdateItems; +end; + +procedure TCustomCheckControlGroupEx.UpdateTabStops; +var + i: Integer; + btn: TCustomCheckControlEx; +begin + for i := 0 to FButtonList.Count - 1 do + begin + btn := TCustomCheckControlEx(FButtonList[i]); + btn.TabStop := btn.Checked; + end; +end; + +{==============================================================================} +{ TRadioGroupExStringList } +{==============================================================================} + +type + TRadioGroupExStringList = class(TStringList) + private + FRadioGroup: TCustomRadioGroupEx; + protected + procedure Changed; override; + public + constructor Create(ARadioGroup: TCustomRadioGroupEx); + procedure Assign(Source: TPersistent); override; + end; + +constructor TRadioGroupExStringList.Create(ARadioGroup: TCustomRadioGroupEx); +begin + inherited Create; + FRadioGroup := ARadioGroup; +end; + +procedure TRadioGroupExStringList.Assign(Source: TPersistent); +var + savedIndex: Integer; +begin + savedIndex := FRadioGroup.ItemIndex; + inherited Assign(Source); + if savedIndex < Count then FRadioGroup.ItemIndex := savedIndex; +end; + +procedure TRadioGroupExStringList.Changed; +begin + inherited Changed; + if (UpdateCount = 0) then + FRadioGroup.UpdateAll + else + FRadioGroup.UpdateInternalObjectList; + FRadioGroup.FLastClickedItemIndex := FRadioGroup.FItemIndex; +end; + + +{==============================================================================} +{ TCustomRadioGroupEx } +{==============================================================================} + +constructor TCustomRadioGroupEx.Create(AOwner: TComponent); +begin + inherited; + FItems := TRadioGroupExStringList.Create(Self); + FItemIndex := -1; + FLastClickedItemIndex := -1; +end; + +procedure TCustomRadioGroupEx.Changed(Sender: TObject); +begin + CheckItemIndexChanged; +end; + +procedure TCustomRadioGroupEx.CheckItemIndexChanged; +begin + if FCreatingWnd or FUpdatingItems then + exit; + if [csLoading,csDestroying]*ComponentState<>[] then exit; + UpdateRadioButtonStates; + if [csDesigning]*ComponentState<>[] then exit; + if FLastClickedItemIndex=FItemIndex then exit; + FLastClickedItemIndex:=FItemIndex; + EditingDone; + // for Delphi compatibility: OnClick should be invoked, whenever ItemIndex + // has changed + if Assigned (FOnClick) then FOnClick(Self); + // And a better named LCL equivalent + if Assigned (FOnSelectionChanged) then FOnSelectionChanged(Self); +end; + +procedure TCustomRadioGroupEx.Clicked(Sender: TObject); +begin + if FIgnoreClicks then exit; + CheckItemIndexChanged; +end; + +function TCustomRadioGroupEx.GetButtonCount: Integer; +var + i: Integer; +begin + Result := 0; + for i := 0 to ControlCount-1 do + if (Controls[i] is TCustomRadioButtonEx) and (Controls[i] <> FHiddenButton) then + inc(Result); +end; + +function TCustomRadioGroupEx.GetButtons(AIndex: Integer): TRadioButtonEx; +begin + Result := Controls[AIndex] as TRadioButtonEx; +end; + +procedure TCustomRadioGroupEx.InitializeWnd; + + procedure RealizeItemIndex; + var + i: Integer; + begin + if (FItemIndex <> -1) and (FItemIndex<FButtonList.Count) then + TRadioButtonEx(FButtonList[FItemIndex]).Checked := true + else if FHiddenButton<>nil then + FHiddenButton.Checked := true; + for i:=0 to FItems.Count-1 do begin + TRadioButtonEx(FButtonList[i]).Checked := (FItemIndex = i); + end; + end; + +begin + if FCreatingWnd then RaiseGDBException('TCustomRadioGroup.InitializeWnd'); + FCreatingWnd := true; + UpdateItems; + inherited InitializeWnd; + RealizeItemIndex; + //debugln(['TCustomRadioGroup.InitializeWnd END']); + FCreatingWnd := false; +end; + +procedure TCustomRadioGroupEx.ItemKeyDown(Sender: TObject; var Key: Word; + Shift: TShiftState); + + procedure MoveSelection(HorzDiff, VertDiff: integer); + var + Count: integer; + StepSize: integer; + BlockSize : integer; + NewIndex : integer; + WrapOffset: integer; + begin + if FReadOnly then + exit; + + Count := FButtonList.Count; + if FColumnLayout = clHorizontalThenVertical then begin + //add a row for ease wrapping + BlockSize := Columns * (Rows+1); + StepSize := HorzDiff + VertDiff * Columns; + WrapOffSet := VertDiff; + end + else begin + //add a column for ease wrapping + BlockSize := (Columns+1) * Rows; + StepSize := HorzDiff * Rows + VertDiff; + WrapOffSet := HorzDiff; + end; + NewIndex := ItemIndex; + repeat + Inc(NewIndex, StepSize); + if (NewIndex >= Count) or (NewIndex < 0) then begin + NewIndex := (NewIndex + WrapOffSet + BlockSize) mod BlockSize; + // Keep moving in the same direction until in valid range + while NewIndex >= Count do + NewIndex := (NewIndex + StepSize) mod BlockSize; + end; + until (NewIndex = ItemIndex) or TCustomCheckControlEx(FButtonList[NewIndex]).Enabled; + ItemIndex := NewIndex; + TCustomCheckControlEx(FButtonList[ItemIndex]).SetFocus; + Key := 0; + end; + +begin + if Shift=[] then begin + case Key of + VK_LEFT: MoveSelection(-1,0); + VK_RIGHT: MoveSelection(1,0); + VK_UP: MoveSelection(0,-1); + VK_DOWN: MoveSelection(0,1); + end; + end; + if Key <> 0 then + KeyDown(Key, Shift); +end; + +procedure TCustomRadioGroupEx.ReadState(AReader: TReader); +begin + FReading := True; + inherited ReadState(AReader); + FReading := False; + if (FItemIndex < -1) or (FItemIndex >= FItems.Count) then + FItemIndex := -1; + FLastClickedItemIndex := FItemIndex; +end; + +procedure TCustomRadioGroupEx.SetItemIndex(const AValue: integer); +var + oldItemIndex: LongInt; + oldIgnoreClicks: Boolean; +begin + if (AValue = FItemIndex) or FReadOnly then exit; + + // needed later if handle isn't allocated + oldItemIndex := FItemIndex; + + if FReading then + FItemIndex := AValue + else begin + if (AValue < -1) or (AValue >= FItems.Count) then + raise Exception.CreateFmt(rsIndexOutOfBounds, [ClassName, AValue, FItems.Count-1]); + + if HandleAllocated then + begin + // the radiobuttons are grouped by the widget interface + // and some does not allow to uncheck all buttons in a group + // Therefore there is a hidden button + FItemIndex := AValue; + oldIgnoreClicks := FIgnoreClicks; + FIgnoreClicks := true; + try + if (FItemIndex <> -1) then + TCustomCheckControlEx(FButtonList[FItemIndex]).Checked := true + else + FHiddenButton.Checked := true; + // uncheck old radiobutton + if (OldItemIndex <> -1) then begin + if (OldItemIndex >= 0) and (OldItemIndex < FButtonList.Count) then + TCustomCheckControlEx(FButtonList[OldItemIndex]).Checked := false + end else + FHiddenButton.Checked := false; + finally + FIgnoreClicks := OldIgnoreClicks; + end; + // this has automatically unset the old button. But they do not recognize + // it. Update the states. + CheckItemIndexChanged; + UpdateTabStops; + OwnerFormDesignerModified(Self); + end else + begin + FItemIndex := AValue; + + // maybe handle was recreated. issue #26714 + FLastClickedItemIndex := -1; + + // trigger event to be delphi compat, even if handle isn't allocated. + // issue #15989 + if (AValue <> oldItemIndex) and not FCreatingWnd then + begin + if Assigned(FOnClick) then FOnClick(Self); + if Assigned(FOnSelectionChanged) then FOnSelectionChanged(Self); + FLastClickedItemIndex := FItemIndex; + end; + end; + end; +end; + +procedure TCustomRadioGroupEx.UpdateItems; +var + i: integer; + button: TCustomCheckControlEx; +begin + if FUpdatingItems then exit; + FUpdatingItems := true; + try + // destroy radiobuttons, if there are too many + while FButtonList.Count > FItems.Count do + begin + TObject(FButtonList[FButtonList.Count-1]).Free; + FButtonList.Delete(FButtonList.Count-1); + end; + + // create as many TRadioButtons as needed + while (FButtonList.Count < FItems.Count) do + begin + button := TRadioButtonEx.Create(Self); + with TCustomCheckControlEx(button) do + begin + Name := 'RadioButtonEx' + IntToStr(FButtonList.Count); + OnClick := @Self.Clicked; + OnChange := @Self.Changed; + OnEnter := @Self.ItemEnter; + OnExit := @Self.ItemExit; + OnKeyDown := @Self.ItemKeyDown; + OnKeyUp := @Self.ItemKeyUp; + OnKeyPress := @Self.ItemKeyPress; + OnUTF8KeyPress := @Self.ItemUTF8KeyPress; + OnResize := @Self.ItemResize; + ParentFont := True; + ReadOnly := Self.ReadOnly; + BorderSpacing.CellAlignHorizontal := ccaLeftTop; + BorderSpacing.CellAlignVertical := ccaCenter; + ControlStyle := ControlStyle + [csNoDesignSelectable]; + end; + FButtonList.Add(button); + end; + if FHiddenButton = nil then begin + FHiddenButton := TRadioButtonEx.Create(nil); + with FHiddenButton do + begin + Name := 'HiddenRadioButton'; + Visible := False; + ControlStyle := ControlStyle + [csNoDesignSelectable, csNoDesignVisible]; + end; + end; + + if (FItemIndex >= FItems.Count) and not (csLoading in ComponentState) then + FItemIndex := FItems.Count-1; + + if FItems.Count > 0 then + begin + // to reduce overhead do it in several steps + + // assign Caption and then Parent + for i:=0 to FItems.Count-1 do + begin + button := TCustomCheckControlEx(FButtonList[i]); + button.Caption := FItems[i]; + button.Parent := Self; + end; + FHiddenButton.Parent := Self; + + // the checked and unchecked states can be applied only after all other + for i := 0 to FItems.Count-1 do + begin + button := TCustomCheckControlEx(FButtonList[i]); + button.Checked := (i = FItemIndex); + button.Visible := true; + end; + + //FHiddenButton must remain the last item in Controls[], so that Controls[] is in sync with Items[] + Self.RemoveControl(FHiddenButton); + Self.InsertControl(FHiddenButton); + if HandleAllocated then + FHiddenButton.HandleNeeded; + FHiddenButton.Checked := (FItemIndex = -1); + UpdateTabStops; + end; + finally + FUpdatingItems := false; + end; +end; + +procedure TCustomRadioGroupEx.UpdateRadioButtonStates; +var + i: Integer; +begin + if FReadOnly then + exit; + + FItemIndex := -1; + FHiddenButton.Checked; + for i:=0 to FButtonList.Count-1 do + if TCustomRadioButtonEx(FButtonList[i]).Checked then FItemIndex := i; + UpdateTabStops; +end; + + +{==============================================================================} +{ TCheckGroupExStringList } +{==============================================================================} + +type + TCheckGroupExStringList = class(TStringList) + private + FCheckGroup: TCustomCheckGroupEx; + procedure RestoreCheckStates(const AStates: TByteDynArray); + procedure SaveCheckStates(out AStates: TByteDynArray); + protected + procedure Changed; override; + public + constructor Create(ACheckGroup: TCustomCheckGroupEx); + procedure Delete(AIndex: Integer); override; + end; + + +constructor TCheckGroupExStringList.Create(ACheckGroup: TCustomCheckGroupEx); +begin + inherited Create; + FCheckGroup := ACheckGroup; +end; + +procedure TCheckGroupExStringList.Changed; +begin + inherited Changed; + if UpdateCount = 0 then + FCheckGroup.UpdateAll + else + FCheckGroup.UpdateInternalObjectList; +end; + +procedure TCheckGroupExStringList.Delete(AIndex: Integer); +// Deleting destroys the checked state of the items -> we must save and restore it +// Issue https://bugs.freepascal.org/view.php?id=34327. +var + b: TByteDynArray; + i: Integer; +begin + SaveCheckStates(b); + + inherited Delete(AIndex); + + for i:= AIndex to High(b)-1 do b[i] := b[i+1]; + SetLength(b, Length(b)-1); + RestoreCheckStates(b); +end; + +procedure TCheckGroupExStringList.RestoreCheckStates(const AStates: TByteDynArray); +var + i: Integer; +begin + Assert(Length(AStates) = FCheckGroup.Items.Count); + for i:=0 to FCheckgroup.Items.Count-1 do begin + FCheckGroup.Checked[i] := AStates[i] and 1 <> 0; + FCheckGroup.CheckEnabled[i] := AStates[i] and 2 <> 0; + end; +end; + +procedure TCheckGroupExStringList.SaveCheckStates(out AStates: TByteDynArray); +var + i: Integer; +begin + SetLength(AStates, FCheckgroup.Items.Count); + for i:=0 to FCheckgroup.Items.Count-1 do begin + AStates[i] := 0; + if FCheckGroup.Checked[i] then inc(AStates[i]); + if FCheckGroup.CheckEnabled[i] then inc(AStates[i], 2); + end; +end; + + +{==============================================================================} +{ TCustomCheckGroupEx } +{==============================================================================} + +constructor TCustomCheckGroupEx.Create(AOwner: TComponent); +begin + inherited; + FItems := TCheckGroupExStringList.Create(Self); +end; + +procedure TCustomCheckGroupEx.Clicked(Sender: TObject); +var + index: Integer; +begin + index := FButtonList.IndexOf(Sender); + if index < 0 then exit; + DoClick(index); +end; + +procedure TCustomCheckGroupEx.DefineProperties(Filer: TFiler); +begin + inherited DefineProperties(Filer); + Filer.DefineBinaryProperty('Data', @ReadData, @WriteData, FItems.Count > 0); +end; + +procedure TCustomCheckGroupEx.DoClick(AIndex: integer); +begin + if [csLoading,csDestroying, csDesigning] * ComponentState <> [] then exit; + EditingDone; + if Assigned(FOnItemClick) then FOnItemClick(Self, AIndex); +end; + +function TCustomCheckGroupEx.GetButtonCount: Integer; +var + i: Integer; +begin + Result := 0; + for i := 0 to ControlCount-1 do + if (Controls[i] is TCustomCheckBoxEx) then + inc(Result); +end; + +function TCustomCheckGroupEx.GetButtons(AIndex: Integer): TCheckBoxEx; +begin + Result := Controls[AIndex] as TCheckBoxEx; +end; + +function TCustomCheckGroupEx.GetChecked(AIndex: Integer): Boolean; +begin + if (AIndex < -1) or (AIndex >= FItems.Count) then + RaiseIndexOutOfBounds(AIndex); + Result := TCustomCheckControlEx(FButtonList[AIndex]).Checked; +end; + +function TCustomCheckGroupEx.GetCheckEnabled(AIndex: integer): boolean; +begin + if (AIndex < -1) or (AIndex >= FItems.Count) then + RaiseIndexOutOfBounds(AIndex); + Result := TCustomCheckControlEx(FButtonList[AIndex]).Enabled; +end; + +procedure TCustomCheckGroupEx.Loaded; +begin + inherited Loaded; + UpdateItems; +end; + +procedure TCustomCheckGroupEx.RaiseIndexOutOfBounds(AIndex: integer); +begin + raise Exception.CreateFmt(rsIndexOutOfBounds, [ClassName, AIndex, FItems.Count - 1]); +end; + +procedure TCustomCheckGroupEx.ReadData(Stream: TStream); +var + ChecksCount: integer; + Checks: string; + i: Integer; + v: Integer; +begin + ChecksCount := ReadLRSInteger(Stream); + if ChecksCount > 0 then begin + SetLength(Checks, ChecksCount); + Stream.ReadBuffer(Checks[1], ChecksCount); + for i:=0 to ChecksCount-1 do begin + v := ord(Checks[i+1]); + Checked[i] := ((v and 1) > 0); + CheckEnabled[i] := ((v and 2) > 0); + end; + end; +end; + +procedure TCustomCheckGroupEx.SetChecked(AIndex: integer; const AValue: boolean); +begin + if (AIndex < -1) or (AIndex >= FItems.Count) then + RaiseIndexOutOfBounds(AIndex); + // disable OnClick + TCheckBox(FButtonList[AIndex]).OnClick := nil; + // set value + TCheckBox(FButtonList[AIndex]).Checked := AValue; + // enable OnClick + TCheckBox(FButtonList[AIndex]).OnClick := @Clicked; +end; + +procedure TCustomCheckGroupEx.SetCheckEnabled(AIndex: integer; + const AValue: boolean); +begin + if (AIndex < -1) or (AIndex >= FItems.Count) then + RaiseIndexOutOfBounds(AIndex); + TCustomCheckControlEx(FButtonList[AIndex]).Enabled := AValue; +end; + +procedure TCustomCheckGroupEx.UpdateItems; +var + i: integer; + btn: TCustomCheckControlEx; +begin + if FUpdatingItems then exit; + FUpdatingItems := true; + try + // destroy checkboxes, if there are too many + while FButtonList.Count > FItems.Count do begin + TObject(FButtonList[FButtonList.Count-1]).Free; + FButtonList.Delete(FButtonList.Count-1); + end; + + // create as many TCheckBoxes as needed + while (FButtonList.Count < FItems.Count) do begin + btn := TCheckBoxEx.Create(Self); + with TCheckBoxEx(btn) do begin + Name := 'CheckBoxEx' + IntToStr(FButtonList.Count); + OnClick := @Self.Clicked; + OnKeyDown := @Self.ItemKeyDown; + OnKeyUp := @Self.ItemKeyUp; + OnKeyPress := @Self.ItemKeyPress; + OnUTF8KeyPress := @Self.ItemUTF8KeyPress; + AutoSize := False; + Parent := Self; + ParentFont := true; + ReadOnly := Self.ReadOnly; + BorderSpacing.CellAlignHorizontal := ccaLeftTop; + BorderSpacing.CellAlignVertical := ccaCenter; + ControlStyle := ControlStyle + [csNoDesignSelectable]; + end; + FButtonList.Add(btn); + end; + + for i:=0 to FItems.Count-1 do begin + btn := TCustomCheckControlEx(FButtonList[i]); + btn.Caption := FItems[i]; + end; + finally + FUpdatingItems := false; + end; +end; + +procedure TCustomCheckGroupEx.WriteData(Stream: TStream); +var + ChecksCount: integer; + Checks: string; + i: Integer; + v: Integer; +begin + ChecksCount := FItems.Count; + WriteLRSInteger(Stream, ChecksCount); + if ChecksCount > 0 then begin + SetLength(Checks, ChecksCount); + for i := 0 to ChecksCount-1 do begin + v := 0; + if Checked[i] then inc(v, 1); + if CheckEnabled[i] then inc(v, 2); + Checks[i+1] := chr(v); + end; + Stream.WriteBuffer(Checks[1], ChecksCount); + end; +end; + +end. + diff --git a/components/exctrls/source/exctrlsreg.pas b/components/exctrls/source/exctrlsreg.pas new file mode 100644 index 000000000..61d5033d6 --- /dev/null +++ b/components/exctrls/source/exctrlsreg.pas @@ -0,0 +1,31 @@ +unit ExCtrlsReg; + +{$mode objfpc}{$H+} + +interface + +uses + Classes, SysUtils; + +procedure Register; + +implementation + +uses + ExCheckCtrls, ExEditCtrls; + +{$R exctrlsreg.res} + +procedure Register; +begin + RegisterComponents('ExCtrls', [ + TCheckboxEx, TRadioButtonEx, TCheckGroupEx, TRadioGroupEx + ]); + RegisterComponents('LazControls', [ + TCurrSpinEditEx + ]); +end; + + +end. + diff --git a/components/exctrls/source/exctrlsreg.res b/components/exctrls/source/exctrlsreg.res new file mode 100644 index 0000000000000000000000000000000000000000..a2679ce1107a4b8735787e8192bdcd9029ba80db GIT binary patch literal 14848 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 literal 0 HcmV?d00001 diff --git a/components/exctrls/source/exeditctrls.pas b/components/exctrls/source/exeditctrls.pas new file mode 100644 index 000000000..cc59d48e9 --- /dev/null +++ b/components/exctrls/source/exeditctrls.pas @@ -0,0 +1,404 @@ +unit ExEditCtrls; + +{$mode objfpc}{$H+} + +{.$define debug_editctrls} + +interface + +uses + Classes, SysUtils, Controls, SpinEx; + +type + { TCustomCurrEditEx } + TSpinEditCurrencyFormat = ( + secfCurrVal, // 0: $1 + secfValCurr, // 1: 1$ + secfCurrSpaceVal, // 2: $ 1 + secfValSpaceCurr // 3: 1 $ + ); + TSpinEditNegCurrencyFormat = ( + sencfParCurrValPar, // 0: ($1) + sencfMinusCurrVal, // 1: -1$ + sencfCurrMinusVal, // 2: $-1 + sencfCurrValMinus, // 3: $1- + sencfParValCurrPar, // 4: (1$) + sencfMinusValCurr, // 5: -1$ + sencfValMinusCurr, // 6: 1-$ + sencfValCurrMinus, // 7: 1$- + sencfMinusValSpaceCurr, // 8: -1 $ + sencfMinusCurrSpaceVal, // 9: -$ 1 + sencfValSpaceCurrMinus, //10: 1 $- + sencfCurrSpaceValMinus, //11: $ 1- + sencfCurrSpaceMinusVal, //12: $ -1 + sencfValMinusSpaceCurr, //13: 1- $ + sencfParCurrSpaceValPar, //14: ($ 1) + sencfParValSpaceCurrPar //15: (1 $) + ); + + TSpinEditCurrencyDecimals = 0..4; + + TCustomCurrSpinEditEx = class(specialize TSpinEditExBase<Currency>) + private + FCurrencyString: String; + FDecimals: TSpinEditCurrencyDecimals; + FCurrencyFormat: TSpinEditCurrencyFormat; + FDecimalSeparator: Char; + FNegCurrencyFormat: TSpinEditNegCurrencyFormat; + FThousandSeparator: Char; + function IsIncrementStored: Boolean; + procedure SetCurrencyFormat(AValue: TSpinEditCurrencyFormat); + procedure SetCurrencyString(AValue: String); + procedure SetDecimals(AValue: TSpinEditCurrencyDecimals); + procedure SetDecimalSeparator(AValue: Char); + procedure SetNegCurrencyFormat(AValue: TSpinEditNegCurrencyFormat); + procedure SetThousandSeparator(AValue: Char); + protected + procedure EditKeyPress(var Key: char); override; + function SafeInc(AValue: Currency): Currency; override; + function SafeDec(AValue: Currency): Currency; override; + function TextIsNumber(const S: String; out ANumber: Currency): Boolean; override; + function TryExtractCurrency(AText: String; out AValue: Currency; + const AFormatSettings: TFormatSettings): Boolean; + function UsedFormatSettings: TFormatSettings; + public + constructor Create(AOwner: TComponent); override; + function ValueToStr(const AValue: Currency): String; override; + procedure ResetFormatSettings; + function StrToValue(const S: String): Currency; override; + public + property Increment stored IsIncrementStored; + property CurrencyFormat: TSpinEditCurrencyFormat + read FCurrencyFormat write SetCurrencyFormat; + property CurrencyString: String + read FCurrencyString write SetCurrencyString; + property Decimals: TSpinEditCurrencyDecimals + read FDecimals write SetDecimals; + property DecimalSeparator: Char + read FDecimalSeparator write SetDecimalSeparator; + property NegCurrencyFormat: TSpinEditNegCurrencyFormat + read FNegCurrencyFormat write SetNegCurrencyFormat; + property ThousandSeparator: Char + read FThousandSeparator write SetThousandSeparator; + end; + + { TCurrSpinEdit } + + TCurrSpinEditEx = class(TCustomCurrSpinEditEx) + public + property AutoSelected; + published + //From TCustomEdit + property AutoSelect; + property AutoSizeHeightIsEditHeight; + property AutoSize default True; + property Action; + property Align; + property Alignment default taRightJustify; + property Anchors; + property BiDiMode; + property BorderSpacing; + property BorderStyle default bsNone; + property CharCase; + property Color; + property Constraints; + property Cursor; + property DirectInput; + property EchoMode; + property Enabled; + property FocusOnBuddyClick; + property Font; + property Hint; + property Layout; + property MaxLength; + property NumbersOnly; + property ParentBiDiMode; + property ParentColor; + property ParentFont; + property ParentShowHint; + property PopupMenu; + property ReadOnly; + property ShowHint; + property TabOrder; + property TabStop; + property TextHint; + property Visible; + + property OnChange; + property OnClick; + property OnDblClick; + property OnDragDrop; + property OnDragOver; + property OnContextPopup; + property OnEditingDone; + property OnEndDrag; + property OnEnter; + property OnExit; + property OnKeyDown; + property OnKeyPress; + property OnKeyUp; + property OnMouseDown; + property OnMouseEnter; + property OnMouseLeave; + property OnMouseMove; + property OnMouseUp; + property OnMouseWheel; + property OnMouseWheelDown; + property OnMouseWheelUp; + property OnStartDrag; + property OnUTF8KeyPress; + + //From TCustomFloatSpinEditEx + property ArrowKeys; + property CurrencyFormat; + property CurrencyString; + property Decimals; + property DecimalSeparator; + property Increment; + property MaxValue; + property MinValue; + property MinRepeatValue; + property NegCurrencyFormat; + property NullValue; + property NullValueBehaviour; + property Spacing; + property ThousandSeparator; + property UpDownVisible; + property Value; + end; + +implementation + +uses + LCLProc; + +resourcestring + RsDecSepMustNotMatchThSep = 'Decimal and thousand separators most not be equal.'; + +const + Digits = ['0'..'9']; + AllowedControlChars = [#8, #9, ^C, ^X, ^V, ^Z]; + +{ TCustomCurrSpinEditEx } + +constructor TCustomCurrSpinEditEx.Create(AOwner: TComponent); +begin + inherited; + FMaxValue := 0; + FMinValue := 0; // --> disable Max/Min check by default + ResetFormatSettings; +end; + +procedure TCustomCurrSpinEditEx.EditKeyPress(var Key: char); +begin + inherited EditKeyPress(Key); + {Disallow any key that is not a digit or - + Tab, BackSpace, Cut, Paste, Copy, Undo of course should be passed onto inherited KeyPress + } + if not (Key in (Digits + AllowedControlChars + ['-'])) then Key := #0; + if (Key = '-') and IsLimited and (MinValue >= 0) then Key := #0; +end; + +procedure TCustomCurrSpinEditEx.ResetFormatSettings; +begin + FDecimals := DefaultFormatSettings.CurrencyDecimals; + FCurrencyFormat := TSpinEditCurrencyFormat(DefaultFormatSettings.CurrencyFormat); + FCurrencyString := DefaultFormatSettings.CurrencyString; + FDecimalSeparator := DefaultFormatSettings.DecimalSeparator; + FNegCurrencyFormat := TSpinEditNegCurrencyFormat(DefaultFormatSettings.NegCurrFormat); + FThousandSeparator := DefaultFormatSettings.ThousandSeparator; +end; + +function TCustomCurrSpinEditEx.IsIncrementStored: Boolean; +begin + Result := FIncrement <> 1; +end; + +function TCustomCurrSpinEditEx.SafeInc(AValue: Currency): Currency; +begin + if (AValue > 0) and (AValue > MaxCurrency-FIncrement) then + Result := MaxCurrency + else + Result := AValue + FIncrement; +end; + +function TCustomCurrSpinEditEx.SafeDec(AValue: Currency): Currency; +begin + if (AValue < 0) and (MinCurrency + FIncrement > AValue) then + Result := MinCurrency + else + Result := AValue - FIncrement; +end; + +procedure TCustomCurrSpinEditEx.SetCurrencyFormat(AValue: TSpinEditCurrencyFormat); +begin + if AValue = FCurrencyFormat then exit; + FCurrencyFormat := AValue; + UpdateControl; +end; + +procedure TCustomCurrSpinEditEx.SetCurrencyString(AValue: string); +begin + if AValue = FCurrencyString then exit; + FCurrencyString := AValue; + UpdateControl; +end; + +procedure TCustomCurrSpinEditEx.SetDecimals(AValue: TSpinEditCurrencyDecimals); +begin + if AValue = FDecimals then exit; + FDecimals := AValue; + UpdateControl; +end; + +procedure TCustomCurrSpinEditEx.SetDecimalSeparator(AValue: char); +begin + if AValue = FDecimalSeparator then exit; + if (AValue = FThousandSeparator) and (ComponentState = []) then + raise Exception.Create(RsDecSepMustNotMatchThSep); + FDecimalSeparator := AValue; + UpdateControl; +end; + +procedure TCustomCurrSpinEditEx.SetNegCurrencyFormat( + AValue: TSpinEditNegCurrencyFormat); +begin + if AValue = FNegCurrencyFormat then exit; + FNegCurrencyFormat := AValue; + UpdateControl; +end; + +procedure TCustomCurrSpinEditEx.SetThousandSeparator(AValue: char); +begin + if AValue = FThousandSeparator then exit; + if AValue = FDecimalSeparator then + raise Exception.Create(RsDecSepMustNotMatchThSep); + FThousandSeparator := AValue; + UpdateControl; +end; + +function TCustomCurrSpinEditEx.TextIsNumber(const S: String; out ANumber: Currency + ): Boolean; +var + C: Currency; +begin + {$ifdef debug_editctrls} + DbgOut(['TCustomSpinEditEx.TextIsNumber: S = "',S,'" Result = ']); + {$endif} + + try + Result := TryExtractCurrency(S, C, UsedFormatSettings); +// Result := TryStrToCurr(S, C); + ANumber := C; + except + Result := False; + end; + {$ifdef debug_editctrls} + debugln([Result]); + {$endif} +end; + +function TCustomCurrSpinEditEx.TryExtractCurrency(AText: String; + out AValue: Currency; const AFormatSettings: TFormatSettings): Boolean; +type + TParenthesis = (pNone, pOpen, pClose); +var + isNeg: Boolean; + parenth: TParenthesis; + P: PChar; + PEnd: PChar; + s: String; + n: Integer; +begin + Result := false; + if AText = '' then + exit; + isNeg := false; + parenth := pNone; + SetLength(s, Length(AText)); + n := 1; + P := @AText[1]; + PEnd := @AText[Length(AText)]; + while P <= PEnd do begin + if (P^ in ['0'..'9']) or (P^ in [AFormatSettings.DecimalSeparator]) then + begin + s[n] := P^; + inc(n); + end else + if P^ = '-' then + isNeg := true + else + if P^ = '(' then begin + isNeg := true; + parenth := pOpen; + end else + if P^ = ')' then begin + if not IsNeg then + exit; + parenth := pClose; + end; + inc(P); + end; + if IsNeg and (parenth = pOpen) then + exit; + SetLength(s, n-1); + Result := TryStrToCurr(s, AValue, AFormatSettings); + if Result and isNeg then AValue := -AValue; +end; + +function TCustomCurrSpinEditEx.UsedFormatSettings: TFormatSettings; +begin + Result := DefaultFormatSettings; + Result.CurrencyFormat := ord(FCurrencyFormat); + Result.NegCurrFormat := ord(FNegCurrencyFormat); + Result.ThousandSeparator := FThousandSeparator; + Result.DecimalSeparator := FDecimalSeparator; + Result.CurrencyString := FCurrencyString; + Result.CurrencyDecimals := FDecimals; +end; + +function TCustomCurrSpinEditEx.ValueToStr(const AValue: Currency): String; +begin + Result := Format('%m', [AValue], UsedFormatSettings); +end; + +function TCustomCurrSpinEditEx.StrToValue(const S: String): Currency; +var + Def, N: Currency; +begin + {$ifdef debug_editctrls} + debugln(['TCustomSpinEditEx.StrToValue: S="',S,'"']); + {$endif} + case FNullValueBehaviour of + nvbShowTextHint: Def := FNullValue; + nvbLimitedNullValue: Def := GetLimitedValue(FNullValue); + nvbMinValue: Def := FMinValue; + nvbMaxValue: Def := MaxValue; + nvbInitialValue: Def := FInitialValue; + end; + try + if (FNullValueBehaviour = nvbShowTextHint)then + begin + if TextIsNumber(S, N) + then + Result := N + else + Result := Def; + end + else + if TextIsNumber(S, N) then + Result := GetLimitedValue(N) + else + Result := Def +// Result := GetLimitedValue(StrToCurrDef(S, Def)); + except + Result := Def; + end; + {$ifdef debug_editctrls} + debugln([' Result=',(Result)]); + {$endif} +end; + + +end. +