NewCheckListBox.pas 81 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375
  1. unit NewCheckListBox;
  2. { TNewCheckListBox by Martijn Laan for Inno Setup
  3. Based on TPBCheckListBox by Patrick Brisacier and TCheckListBox by Borland
  4. Group item support, child item support, exclusive item support,
  5. ShowLines support and 'WantTabs mode' by Alex Yackimoff.
  6. Note: TNewCheckListBox uses Items.Objects to store the item state. Don't use
  7. Item.Objects yourself, use ItemObject instead.
  8. Define VCLSTYLES for full VCL Styles support.
  9. }
  10. interface
  11. uses
  12. Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  13. {$IFDEF VCLSTYLES} Vcl.Themes, {$ELSE} Themes, {$ENDIF}
  14. StdCtrls, NewUxTheme;
  15. const
  16. WM_UPDATEUISTATE = $0128;
  17. type
  18. TItemType = (itGroup, itCheck, itRadio);
  19. TCheckBoxState2 = (cb2Normal, cb2Hot, cb2Pressed, cb2Disabled);
  20. TItemState = class (TObject)
  21. public
  22. Enabled: Boolean;
  23. HasInternalChildren: Boolean;
  24. CheckWhenParentChecked: Boolean;
  25. IsLastChild: Boolean;
  26. ItemType: TItemType;
  27. Level: Byte;
  28. Obj: TObject;
  29. State: TCheckBoxState;
  30. SubItem: string;
  31. ThreadCache: set of Byte;
  32. MeasuredHeight: Integer;
  33. ItemFontStyle: TFontStyles;
  34. SubItemFontStyle: TFontStyles;
  35. end;
  36. TCheckItemOperation = (coUncheck, coCheck, coCheckWithChildren);
  37. TEnumChildrenProc = procedure(Index: Integer; HasChildren: Boolean; Ext: NativeInt) of object;
  38. TNewCheckListBox = class (TCustomListBox)
  39. private
  40. FAccObjectInstance: TObject;
  41. FCaptureIndex: Integer;
  42. FSpaceDown: Boolean;
  43. FCheckHeight: Integer;
  44. FCheckWidth: Integer;
  45. FFormFocusChanged: Boolean;
  46. FFlat: Boolean;
  47. FLastMouseMoveIndex: Integer;
  48. FMinItemHeight: Integer;
  49. FOffset: Integer;
  50. FOnClickCheck: TNotifyEvent;
  51. FRequireRadioSelection: Boolean;
  52. FShowLines: Boolean;
  53. FStateList: TList;
  54. FWantTabs: Boolean;
  55. FThemeData: HTHEME;
  56. FThreadsUpToDate: Boolean;
  57. FHotIndex: Integer;
  58. FDisableItemStateDeletion: Integer;
  59. FWheelAccum: Integer;
  60. FDisableStyledButtons: Boolean;
  61. class constructor Create;
  62. class destructor Destroy;
  63. class var FComplexParentBackground: Boolean;
  64. procedure UpdateThemeData(const Close, Open: Boolean);
  65. function CanFocusItem(Item: Integer): Boolean;
  66. function CheckPotentialRadioParents(Index, ALevel: Integer): Boolean;
  67. procedure CMDialogChar(var Message: TCMDialogChar); message CM_DIALOGCHAR;
  68. procedure CMEnter(var Message: TCMEnter); message CM_ENTER;
  69. procedure CMExit(var Message: TCMExit); message CM_EXIT;
  70. procedure CMFocusChanged(var Message: TCMFocusChanged); message CM_FOCUSCHANGED;
  71. procedure CMFontChanged(var Message: TMessage); message CM_FONTCHANGED;
  72. procedure CMWantSpecialKey(var Message: TMessage); message CM_WANTSPECIALKEY;
  73. procedure CNDrawItem(var Message: TWMDrawItem); message CN_DRAWITEM;
  74. procedure EndCapture(Cancel: Boolean);
  75. function AddItem2(AType: TItemType; const ACaption, ASubItem: string;
  76. ALevel: Byte; AChecked, AEnabled, AHasInternalChildren,
  77. ACheckWhenParentChecked: Boolean; AObject: TObject): Integer;
  78. function FindAccel(VK: Word): Integer;
  79. function FindCheckedSibling(const AIndex: Integer): Integer;
  80. function FindNextItem(StartFrom: Integer; GoForward,
  81. SkipUncheckedRadios: Boolean): Integer;
  82. function GetItemState(Index: Integer): TItemState;
  83. procedure HandleScroll;
  84. procedure InvalidateCheck(Index: Integer);
  85. function RemeasureItem(Index: Integer): Integer;
  86. procedure Toggle(Index: Integer);
  87. procedure UpdateScrollRange;
  88. procedure LBDeleteString(var Message: TMessage); message LB_DELETESTRING;
  89. procedure LBResetContent(var Message: TMessage); message LB_RESETCONTENT;
  90. procedure WMGetDlgCode(var Message: TWMGetDlgCode); message WM_GETDLGCODE;
  91. procedure WMGetObject(var Message: TMessage); message WM_GETOBJECT;
  92. procedure WMKeyDown(var Message: TWMKeyDown); message WM_KEYDOWN;
  93. procedure WMMouseMove(var Message: TWMMouseMove); message WM_MOUSEMOVE;
  94. procedure WMMouseWheel(var Message: TWMMouseWheel); message WM_MOUSEWHEEL;
  95. procedure WMNCHitTest(var Message: TWMNCHitTest); message WM_NCHITTEST;
  96. procedure WMSetFocus(var Message: TWMSetFocus); message WM_SETFOCUS;
  97. procedure WMSize(var Message: TWMSize); message WM_SIZE;
  98. procedure WMThemeChanged(var Message: TMessage); message WM_THEMECHANGED;
  99. procedure WMUpdateUIState(var Message: TMessage); message WM_UPDATEUISTATE;
  100. procedure WMVScroll(var Message: TWMVScroll); message WM_VSCROLL;
  101. protected
  102. procedure CreateWnd; override;
  103. procedure MeasureItem(Index: Integer; var Height: Integer); override;
  104. procedure DrawItem(Index: Integer; Rect: TRect; State: TOwnerDrawState);
  105. override;
  106. function GetCaption(Index: Integer): String;
  107. function GetChecked(Index: Integer): Boolean;
  108. function GetItemEnabled(Index: Integer): Boolean;
  109. function GetItemFontStyle(Index: Integer): TFontStyles;
  110. function GetLevel(Index: Integer): Byte;
  111. function GetObject(Index: Integer): TObject;
  112. function GetState(Index: Integer): TCheckBoxState;
  113. function GetSubItem(Index: Integer): string;
  114. function GetSubItemFontStyle(Index: Integer): TFontStyles;
  115. function GetTransparentIfStyled: Boolean;
  116. procedure KeyDown(var Key: Word; Shift: TShiftState); override;
  117. procedure KeyUp(var Key: Word; Shift: TShiftState); override;
  118. procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  119. override;
  120. procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
  121. procedure UpdateHotIndex(NewHotIndex: Integer);
  122. procedure CMMouseLeave(var Message: TMessage); message CM_MOUSELEAVE;
  123. procedure SetCaption(Index: Integer; const Value: String);
  124. procedure SetChecked(Index: Integer; const AChecked: Boolean);
  125. procedure SetFlat(Value: Boolean);
  126. procedure SetItemEnabled(Index: Integer; const AEnabled: Boolean);
  127. procedure SetItemFontStyle(Index: Integer; const AItemFontStyle: TFontStyles);
  128. procedure SetItemIndex(const Value: Integer); override;
  129. procedure SetObject(Index: Integer; const AObject: TObject);
  130. procedure SetOffset(AnOffset: Integer);
  131. procedure SetShowLines(Value: Boolean);
  132. procedure SetSubItem(Index: Integer; const ASubItem: String);
  133. procedure SetSubItemFontStyle(Index: Integer; const ASubItemFontStyle: TFontStyles);
  134. property ItemStates[Index: Integer]: TItemState read GetItemState;
  135. public
  136. constructor Create(AOwner: TComponent); override;
  137. procedure CreateWindowHandle(const Params: TCreateParams); override;
  138. destructor Destroy; override;
  139. function AddCheckBox(const ACaption, ASubItem: string; ALevel: Byte;
  140. AChecked, AEnabled, AHasInternalChildren, ACheckWhenParentChecked: Boolean;
  141. AObject: TObject): Integer;
  142. function AddGroup(const ACaption, ASubItem: string; ALevel: Byte;
  143. AObject: TObject): Integer;
  144. function AddRadioButton(const ACaption, ASubItem: string;
  145. ALevel: Byte; AChecked, AEnabled: Boolean; AObject: TObject): Integer;
  146. function CheckItem(const Index: Integer; const AOperation: TCheckItemOperation): Boolean;
  147. procedure EnumChildrenOf(Item: Integer; Proc: TEnumChildrenProc; Ext: NativeInt);
  148. function GetParentOf(Item: Integer): Integer;
  149. procedure UpdateThreads;
  150. property Checked[Index: Integer]: Boolean read GetChecked write SetChecked;
  151. property DisableStyledButtons: Boolean read FDisableStyledButtons write FDisableStyledButtons;
  152. property ItemCaption[Index: Integer]: String read GetCaption write SetCaption;
  153. property ItemEnabled[Index: Integer]: Boolean read GetItemEnabled write SetItemEnabled;
  154. property ItemFontStyle[Index: Integer]: TFontStyles read GetItemFontStyle write SetItemFontStyle;
  155. property ItemLevel[Index: Integer]: Byte read GetLevel;
  156. property ItemObject[Index: Integer]: TObject read GetObject write SetObject;
  157. property ItemSubItem[Index: Integer]: string read GetSubItem write SetSubItem;
  158. property State[Index: Integer]: TCheckBoxState read GetState;
  159. property SubItemFontStyle[Index: Integer]: TFontStyles read GetSubItemFontStyle write SetSubItemFontStyle;
  160. property TransparentIfStyled: Boolean read GetTransparentIfStyled;
  161. class property ComplexParentBackground: Boolean read FComplexParentBackground write FComplexParentBackground;
  162. published
  163. property Align;
  164. property Anchors;
  165. property BorderStyle;
  166. property Color;
  167. property Ctl3D;
  168. property DragCursor;
  169. property DragMode;
  170. property Enabled;
  171. property Flat: Boolean read FFlat write SetFlat default False;
  172. property Font;
  173. property Items;
  174. property MinItemHeight: Integer read FMinItemHeight write FMinItemHeight default 16;
  175. property Offset: Integer read FOffset write SetOffset default 4;
  176. property OnClick;
  177. property OnClickCheck: TNotifyEvent read FOnClickCheck write FOnClickCheck;
  178. property OnDblClick;
  179. property OnDragDrop;
  180. property OnDragOver;
  181. property OnEndDrag;
  182. property OnEnter;
  183. property OnExit;
  184. property OnKeyDown;
  185. property OnKeyPress;
  186. property OnKeyUp;
  187. property OnMouseDown;
  188. property OnMouseMove;
  189. property OnMouseUp;
  190. property OnStartDrag;
  191. property ParentColor;
  192. property ParentCtl3D;
  193. property ParentFont;
  194. property ParentShowHint;
  195. property PopupMenu;
  196. property RequireRadioSelection: Boolean read FRequireRadioSelection write FRequireRadioSelection default False;
  197. property ShowHint;
  198. property ShowLines: Boolean read FShowLines write SetShowLines default True;
  199. property TabOrder;
  200. property Visible;
  201. property WantTabs: Boolean read FWantTabs write FWantTabs default False;
  202. end;
  203. TNewCheckListBoxStyleHook = class(TScrollingStyleHook)
  204. {$IFDEF VCLSTYLES}
  205. strict private
  206. FStyleColorsChecked: Boolean;
  207. FStyleColorsCheckedWantTabs: Boolean;
  208. procedure UpdateColors;
  209. strict protected
  210. procedure PaintBackground(Canvas: TCanvas); override;
  211. procedure WndProc(var Message: TMessage); override;
  212. procedure WMSetFocus(var Message: TMessage); message WM_SETFOCUS;
  213. procedure WMKillFocus(var Message: TMessage); message WM_KILLFOCUS;
  214. public
  215. constructor Create(AControl: TWinControl); override;
  216. {$ENDIF}
  217. end;
  218. procedure Register;
  219. implementation
  220. uses
  221. UITypes, Types, ActiveX,
  222. NewUxTheme.TmSchema, PathFunc, BidiUtils, UnsignedFunc;
  223. const
  224. sRadioCantHaveDisabledChildren = 'Radio item cannot have disabled child items';
  225. OBM_CHECKBOXES = 32759;
  226. WM_CHANGEUISTATE = $0127;
  227. WM_QUERYUISTATE = $0129;
  228. UIS_SET = 1;
  229. UIS_CLEAR = 2;
  230. UIS_INITIALIZE = 3;
  231. UISF_HIDEFOCUS = $1;
  232. UISF_HIDEACCEL = $2;
  233. DT_HIDEPREFIX = $00100000;
  234. OBJID_CLIENT = $FFFFFFFC;
  235. CHILDID_SELF = 0;
  236. ROLE_SYSTEM_OUTLINE = $23;
  237. ROLE_SYSTEM_STATICTEXT = $29;
  238. ROLE_SYSTEM_CHECKBUTTON = $2c;
  239. ROLE_SYSTEM_RADIOBUTTON = $2d;
  240. STATE_SYSTEM_UNAVAILABLE = $1;
  241. STATE_SYSTEM_CHECKED = $10;
  242. STATE_SYSTEM_MIXED = $20;
  243. EVENT_OBJECT_STATECHANGE = $800A;
  244. IID_IUnknown: TGUID = (
  245. D1:$00000000; D2:$0000; D3:$0000; D4:($C0,$00,$00,$00,$00,$00,$00,$46));
  246. IID_IDispatch: TGUID = (
  247. D1:$00020400; D2:$0000; D3:$0000; D4:($C0,$00,$00,$00,$00,$00,$00,$46));
  248. IID_IAccessible: TGUID = (
  249. D1:$618736e0; D2:$3c3d; D3:$11cf; D4:($81,$0c,$00,$aa,$00,$38,$9b,$71));
  250. type
  251. TWinControlAccess = class (TWinControl);
  252. { Note: We have to use TVariantArg for Delphi 2 compat., because D2 passes
  253. Variant parameters by reference (wrong), unlike D3+ which pass
  254. Variant/OleVariant parameters by value }
  255. NewOleVariant = TVariantArg;
  256. NewWideString = Pointer;
  257. TIUnknown = class
  258. public
  259. function QueryInterface(const iid: TIID; var obj): HRESULT; virtual; stdcall; abstract;
  260. function AddRef: Longint; virtual; stdcall; abstract;
  261. function Release: Longint; virtual; stdcall; abstract;
  262. end;
  263. TIDispatch = class(TIUnknown)
  264. public
  265. function GetTypeInfoCount(var ctinfo: Integer): HRESULT; virtual; stdcall; abstract;
  266. function GetTypeInfo(itinfo: Integer; lcid: TLCID; var tinfo: ITypeInfo): HRESULT; virtual; stdcall; abstract;
  267. function GetIDsOfNames(const iid: TIID; rgszNames: POleStrList;
  268. cNames: Integer; lcid: TLCID; rgdispid: PDispIDList): HRESULT; virtual; stdcall; abstract;
  269. function Invoke(dispIDMember: TDispID; const iid: TIID; lcid: TLCID;
  270. flags: Word; var dispParams: TDispParams; varResult: PVariant;
  271. excepInfo: PExcepInfo; argErr: PInteger): HRESULT; virtual; stdcall; abstract;
  272. end;
  273. TIAccessible = class(TIDispatch)
  274. public
  275. function get_accParent(var ppdispParent: IDispatch): HRESULT; virtual; stdcall; abstract;
  276. function get_accChildCount(var pcountChildren: Integer): HRESULT; virtual; stdcall; abstract;
  277. function get_accChild(varChild: NewOleVariant; var ppdispChild: IDispatch): HRESULT; virtual; stdcall; abstract;
  278. function get_accName(varChild: NewOleVariant; var pszName: NewWideString): HRESULT; virtual; stdcall; abstract;
  279. function get_accValue(varChild: NewOleVariant; var pszValue: NewWideString): HRESULT; virtual; stdcall; abstract;
  280. function get_accDescription(varChild: NewOleVariant; var pszDescription: NewWideString): HRESULT; virtual; stdcall; abstract;
  281. function get_accRole(varChild: NewOleVariant; var pvarRole: NewOleVariant): HRESULT; virtual; stdcall; abstract;
  282. function get_accState(varChild: NewOleVariant; var pvarState: NewOleVariant): HRESULT; virtual; stdcall; abstract;
  283. function get_accHelp(varChild: NewOleVariant; var pszHelp: NewWideString): HRESULT; virtual; stdcall; abstract;
  284. function get_accHelpTopic(var pszHelpFile: NewWideString; varChild: NewOleVariant; var pidTopic: Integer): HRESULT; virtual; stdcall; abstract;
  285. function get_accKeyboardShortcut(varChild: NewOleVariant; var pszKeyboardShortcut: NewWideString): HRESULT; virtual; stdcall; abstract;
  286. function get_accFocus(var pvarID: NewOleVariant): HRESULT; virtual; stdcall; abstract;
  287. function get_accSelection(var pvarChildren: NewOleVariant): HRESULT; virtual; stdcall; abstract;
  288. function get_accDefaultAction(varChild: NewOleVariant; var pszDefaultAction: NewWideString): HRESULT; virtual; stdcall; abstract;
  289. function accSelect(flagsSelect: Integer; varChild: NewOleVariant): HRESULT; virtual; stdcall; abstract;
  290. function accLocation(var pxLeft: Integer; var pyTop: Integer; var pcxWidth: Integer;
  291. var pcyHeight: Integer; varChild: NewOleVariant): HRESULT; virtual; stdcall; abstract;
  292. function accNavigate(navDir: Integer; varStart: NewOleVariant; var pvarEnd: NewOleVariant): HRESULT; virtual; stdcall; abstract;
  293. function accHitTest(xLeft: Integer; yTop: Integer; var pvarID: NewOleVariant): HRESULT; virtual; stdcall; abstract;
  294. function accDoDefaultAction(varChild: NewOleVariant): HRESULT; virtual; stdcall; abstract;
  295. function put_accName(varChild: NewOleVariant; const pszName: NewWideString): HRESULT; virtual; stdcall; abstract;
  296. function put_accValue(varChild: NewOleVariant; const pszValue: NewWideString): HRESULT; virtual; stdcall; abstract;
  297. end;
  298. TAccObject = class(TIAccessible)
  299. private
  300. FControl: TNewCheckListBox;
  301. FRefCount: Integer;
  302. FStdAcc: TIAccessible;
  303. { TIUnknown }
  304. function QueryInterface(const iid: TIID; var obj): HRESULT; override;
  305. function AddRef: Longint; override;
  306. function Release: Longint; override;
  307. { TIDispatch }
  308. function GetTypeInfoCount(var ctinfo: Integer): HRESULT; override;
  309. function GetTypeInfo(itinfo: Integer; lcid: TLCID; var tinfo: ITypeInfo): HRESULT; override;
  310. function GetIDsOfNames(const iid: TIID; rgszNames: POleStrList;
  311. cNames: Integer; lcid: TLCID; rgdispid: PDispIDList): HRESULT; override;
  312. function Invoke(dispIDMember: TDispID; const iid: TIID; lcid: TLCID;
  313. flags: Word; var dispParams: TDispParams; varResult: PVariant;
  314. excepInfo: PExcepInfo; argErr: PInteger): HRESULT; override;
  315. { TIAccessible }
  316. function get_accParent(var ppdispParent: IDispatch): HRESULT; override;
  317. function get_accChildCount(var pcountChildren: Integer): HRESULT; override;
  318. function get_accChild(varChild: NewOleVariant; var ppdispChild: IDispatch): HRESULT; override;
  319. function get_accName(varChild: NewOleVariant; var pszName: NewWideString): HRESULT; override;
  320. function get_accValue(varChild: NewOleVariant; var pszValue: NewWideString): HRESULT; override;
  321. function get_accDescription(varChild: NewOleVariant; var pszDescription: NewWideString): HRESULT; override;
  322. function get_accRole(varChild: NewOleVariant; var pvarRole: NewOleVariant): HRESULT; override;
  323. function get_accState(varChild: NewOleVariant; var pvarState: NewOleVariant): HRESULT; override;
  324. function get_accHelp(varChild: NewOleVariant; var pszHelp: NewWideString): HRESULT; override;
  325. function get_accHelpTopic(var pszHelpFile: NewWideString; varChild: NewOleVariant; var pidTopic: Integer): HRESULT; override;
  326. function get_accKeyboardShortcut(varChild: NewOleVariant; var pszKeyboardShortcut: NewWideString): HRESULT; override;
  327. function get_accFocus(var pvarID: NewOleVariant): HRESULT; override;
  328. function get_accSelection(var pvarChildren: NewOleVariant): HRESULT; override;
  329. function get_accDefaultAction(varChild: NewOleVariant; var pszDefaultAction: NewWideString): HRESULT; override;
  330. function accSelect(flagsSelect: Integer; varChild: NewOleVariant): HRESULT; override;
  331. function accLocation(var pxLeft: Integer; var pyTop: Integer; var pcxWidth: Integer;
  332. var pcyHeight: Integer; varChild: NewOleVariant): HRESULT; override;
  333. function accNavigate(navDir: Integer; varStart: NewOleVariant; var pvarEnd: NewOleVariant): HRESULT; override;
  334. function accHitTest(xLeft: Integer; yTop: Integer; var pvarID: NewOleVariant): HRESULT; override;
  335. function accDoDefaultAction(varChild: NewOleVariant): HRESULT; override;
  336. function put_accName(varChild: NewOleVariant; const pszName: NewWideString): HRESULT; override;
  337. function put_accValue(varChild: NewOleVariant; const pszValue: NewWideString): HRESULT; override;
  338. public
  339. constructor Create(AControl: TNewCheckListBox);
  340. destructor Destroy; override;
  341. procedure ControlDestroying;
  342. end;
  343. function CoDisconnectObject(unk: TIUnknown; dwReserved: DWORD): HRESULT;
  344. stdcall; external 'ole32.dll';
  345. var
  346. NotifyWinEventFunc: procedure(event: DWORD; hwnd: HWND; idObject: DWORD;
  347. idChild: Longint); stdcall;
  348. OleAccInited: BOOL;
  349. OleAccAvailable: BOOL;
  350. LresultFromObjectFunc: function(const riid: TGUID; wParam: WPARAM;
  351. pUnk: TIUnknown): LRESULT; stdcall;
  352. CreateStdAccessibleObjectFunc: function(hwnd: HWND; idObject: Longint;
  353. const riidInterface: TGUID; var ppvObject: Pointer): HRESULT; stdcall;
  354. function InitializeOleAcc: Boolean;
  355. function GetSystemDir: String;
  356. var
  357. Buf: array[0..MAX_PATH-1] of Char;
  358. begin
  359. GetSystemDirectory(Buf, SizeOf(Buf) div SizeOf(Buf[0]));
  360. Result := StrPas(Buf);
  361. end;
  362. var
  363. M: HMODULE;
  364. begin
  365. if not OleAccInited then begin
  366. M := LoadLibrary(PChar(AddBackslash(GetSystemDir) + 'oleacc.dll'));
  367. if M <> 0 then begin
  368. LresultFromObjectFunc := GetProcAddress(M, 'LresultFromObject');
  369. CreateStdAccessibleObjectFunc := GetProcAddress(M, 'CreateStdAccessibleObject');
  370. if Assigned(LresultFromObjectFunc) and
  371. Assigned(CreateStdAccessibleObjectFunc) then
  372. OleAccAvailable := True;
  373. end;
  374. OleAccInited := True;
  375. end;
  376. Result := OleAccAvailable;
  377. end;
  378. { TNewCheckListBox }
  379. class constructor TNewCheckListBox.Create;
  380. begin
  381. TCustomStyleEngine.RegisterStyleHook(TNewCheckListBox, TNewCheckListBoxStyleHook);
  382. end;
  383. constructor TNewCheckListBox.Create(AOwner: TComponent);
  384. begin
  385. inherited Create(AOwner);
  386. with TBitmap.Create do
  387. begin
  388. try
  389. Handle := LoadBitmap(0, PChar(OBM_CHECKBOXES));
  390. FCheckWidth := Width div 4;
  391. FCheckHeight := Height div 3;
  392. finally
  393. Free;
  394. end;
  395. end;
  396. FStateList := TList.Create;
  397. FMinItemHeight := 16;
  398. FOffset := 4;
  399. FShowLines := True;
  400. Style := lbOwnerDrawVariable;
  401. FHotIndex := -1;
  402. FCaptureIndex := -1;
  403. end;
  404. procedure TNewCheckListBox.CreateWnd;
  405. begin
  406. { TCustomListBox.CreateWnd causes a LB_RESETCONTENT message to be sent when
  407. it's restoring FSaveItems. Increment FDisableItemStateDeletion so that
  408. our LB_RESETCONTENT handler doesn't delete any item states. }
  409. Inc(FDisableItemStateDeletion);
  410. try
  411. inherited;
  412. finally
  413. Dec(FDisableItemStateDeletion);
  414. end;
  415. end;
  416. procedure TNewCheckListBox.UpdateThemeData(const Close, Open: Boolean);
  417. begin
  418. if Close then begin
  419. if FThemeData <> 0 then begin
  420. CloseThemeData(FThemeData);
  421. FThemeData := 0;
  422. end;
  423. end;
  424. if Open then begin
  425. if UseThemes then
  426. FThemeData := OpenThemeData(Handle, 'Button')
  427. else
  428. FThemeData := 0;
  429. end;
  430. end;
  431. procedure TNewCheckListBox.CreateWindowHandle(const Params: TCreateParams);
  432. begin
  433. inherited CreateWindowHandle(Params);
  434. UpdateThemeData(True, True);
  435. end;
  436. class destructor TNewCheckListBox.Destroy;
  437. begin
  438. TCustomStyleEngine.UnRegisterStyleHook(TNewCheckListBox, TNewCheckListBoxStyleHook);
  439. end;
  440. destructor TNewCheckListBox.Destroy;
  441. begin
  442. if Assigned(FAccObjectInstance) then begin
  443. { Detach from FAccObjectInstance if someone still has a reference to it }
  444. TAccObject(FAccObjectInstance).ControlDestroying;
  445. FAccObjectInstance := nil;
  446. end;
  447. if Assigned(FStateList) then begin
  448. for var I := FStateList.Count-1 downto 0 do
  449. TItemState(FStateList[I]).Free;
  450. FStateList.Free;
  451. end;
  452. UpdateThemeData(True, False);
  453. inherited Destroy;
  454. end;
  455. function TNewCheckListBox.AddCheckBox(const ACaption, ASubItem: string;
  456. ALevel: Byte; AChecked, AEnabled, AHasInternalChildren,
  457. ACheckWhenParentChecked: Boolean; AObject: TObject): Integer;
  458. begin
  459. if not AEnabled and CheckPotentialRadioParents(Items.Count, ALevel) then
  460. raise Exception.Create(sRadioCantHaveDisabledChildren);
  461. Result := AddItem2(itCheck, ACaption, ASubItem, ALevel, AChecked, AEnabled,
  462. AHasInternalChildren, ACheckWhenParentChecked, AObject);
  463. end;
  464. function TNewCheckListBox.AddGroup(const ACaption, ASubItem: string;
  465. ALevel: Byte; AObject: TObject): Integer;
  466. begin
  467. Result := AddItem2(itGroup, ACaption, ASubItem, ALevel, False, True, False,
  468. True, AObject);
  469. end;
  470. function TNewCheckListBox.AddRadioButton(const ACaption, ASubItem: string;
  471. ALevel: Byte; AChecked, AEnabled: Boolean; AObject: TObject): Integer;
  472. begin
  473. if not AEnabled then
  474. AChecked := False;
  475. Result := AddItem2(itRadio, ACaption, ASubItem, ALevel, AChecked, AEnabled,
  476. False, True, AObject);
  477. end;
  478. function TNewCheckListBox.CanFocusItem(Item: Integer): Boolean;
  479. begin
  480. with ItemStates[Item] do
  481. Result := Self.Enabled and Enabled and (ItemType <> itGroup);
  482. end;
  483. function TNewCheckListBox.CheckPotentialRadioParents(Index, ALevel: Integer): Boolean;
  484. begin
  485. Result := True;
  486. Dec(Index);
  487. Dec(ALevel);
  488. while Index >= 0 do
  489. begin
  490. with ItemStates[Index] do
  491. if Level = ALevel then
  492. if ItemType = itRadio then
  493. Exit
  494. else
  495. Break;
  496. Dec(Index);
  497. end;
  498. if Index >= 0 then
  499. begin
  500. Index := GetParentOf(Index);
  501. while Index >= 0 do
  502. begin
  503. if ItemStates[Index].ItemType = itRadio then
  504. Exit;
  505. Index := GetParentOf(Index);
  506. end;
  507. end;
  508. Result := False;
  509. end;
  510. procedure TNewCheckListBox.CMDialogChar(var Message: TCMDialogChar);
  511. var
  512. I: Integer;
  513. begin
  514. if FWantTabs and CanFocus then
  515. with Message do
  516. begin
  517. I := FindAccel(CharCode);
  518. if I >= 0 then
  519. begin
  520. SetFocus;
  521. if (FCaptureIndex <> I) or FSpaceDown then EndCapture(not FSpaceDown);
  522. ItemIndex := I;
  523. Toggle(I);
  524. Result := 1
  525. end;
  526. end;
  527. end;
  528. procedure TNewCheckListBox.CMEnter(var Message: TCMEnter);
  529. var
  530. GoForward, Arrows: Boolean;
  531. begin
  532. if FWantTabs and FFormFocusChanged and (GetKeyState(VK_LBUTTON) >= 0) then
  533. begin
  534. if GetKeyState(VK_TAB) < 0 then begin
  535. Arrows := False;
  536. GoForward := (GetKeyState(VK_SHIFT) >= 0);
  537. end
  538. else if (GetKeyState(VK_UP) < 0) or (GetKeyState(VK_LEFT) < 0) then begin
  539. Arrows := True;
  540. GoForward := False;
  541. end
  542. else if (GetKeyState(VK_DOWN) < 0) or (GetKeyState(VK_RIGHT) < 0) then begin
  543. Arrows := True;
  544. GoForward := True;
  545. end
  546. else begin
  547. { Otherwise, just select the first item }
  548. Arrows := False;
  549. GoForward := True;
  550. end;
  551. if GoForward then
  552. ItemIndex := FindNextItem(-1, True, not Arrows)
  553. else
  554. ItemIndex := FindNextItem(Items.Count, False, not Arrows)
  555. end;
  556. inherited;
  557. end;
  558. procedure TNewCheckListBox.CMExit(var Message: TCMExit);
  559. begin
  560. EndCapture(not FSpaceDown or (GetKeyState(VK_MENU) >= 0));
  561. inherited;
  562. end;
  563. procedure TNewCheckListBox.CMFocusChanged(var Message: TCMFocusChanged);
  564. begin
  565. FFormFocusChanged := True;
  566. inherited;
  567. end;
  568. procedure TNewCheckListBox.CMFontChanged(var Message: TMessage);
  569. begin
  570. inherited;
  571. Canvas.Font := Font;
  572. end;
  573. procedure LineDDAProc(X, Y: Integer; Canvas: TCanvas); stdcall;
  574. begin
  575. if ((X xor Y) and 1) = 0 then
  576. begin
  577. Canvas.MoveTo(X, Y);
  578. Canvas.LineTo(X + 1, Y)
  579. end;
  580. end;
  581. procedure TNewCheckListBox.CMWantSpecialKey(var Message: TMessage);
  582. begin
  583. Message.Result := Ord(FWantTabs and (Message.WParam = VK_TAB));
  584. end;
  585. procedure TNewCheckListBox.CNDrawItem(var Message: TWMDrawItem);
  586. begin
  587. with Message.DrawItemStruct^ do
  588. begin
  589. { Note: itemID is -1 when there are no items }
  590. if Integer(itemID) >= 0 then begin
  591. var L := ItemStates[Integer(itemID)].Level;
  592. if ItemStates[Integer(itemID)].ItemType <> itGroup then Inc(L);
  593. rcItem.Left := rcItem.Left + (FCheckWidth + 2 * FOffset) * L;
  594. FlipRect(rcItem, ClientRect, IsRightToLeft);
  595. end;
  596. { Don't let TCustomListBox.CNDrawItem draw the focus }
  597. if FWantTabs or
  598. (SendMessage(Handle, WM_QUERYUISTATE, 0, 0) and UISF_HIDEFOCUS <> 0) then
  599. itemState := itemState and not ODS_FOCUS;
  600. inherited;
  601. end;
  602. end;
  603. function TNewCheckListBox.RemeasureItem(Index: Integer): Integer;
  604. { Recalculates an item's height. Does not repaint and does not update the
  605. vertical scroll range (as the LB_SETITEMHEIGHT message does neither). }
  606. begin
  607. Result := ItemHeight;
  608. MeasureItem(Index, Result);
  609. SendMessage(Handle, LB_SETITEMHEIGHT, Index, Result);
  610. end;
  611. procedure TNewCheckListBox.UpdateScrollRange;
  612. { Updates the vertical scroll range, hiding/showing the scroll bar if needed.
  613. This should be called after any RemeasureItem call. }
  614. begin
  615. { Update the scroll bounds by sending a seemingly-ineffectual LB_SETTOPINDEX
  616. message. This works on Windows 95 and 2000.
  617. NOTE: This causes the selected item to be repainted for no apparent reason!
  618. I wish I knew of a better way to do this... }
  619. SendMessage(Handle, LB_SETTOPINDEX, SendMessage(Handle, LB_GETTOPINDEX, 0, 0), 0);
  620. end;
  621. procedure TNewCheckListBox.MeasureItem(Index: Integer; var Height: Integer);
  622. var
  623. Rect, SubItemRect: TRect;
  624. ItemState: TItemState;
  625. L, SubItemWidth: Integer;
  626. S: String;
  627. begin
  628. with Canvas do begin
  629. ItemState := ItemStates[Index];
  630. Rect := Classes.Rect(0, 0, ClientWidth, 0);
  631. L := ItemState.Level;
  632. if ItemState.ItemType <> itGroup then
  633. Inc(L);
  634. Rect.Left := Rect.Left + (FCheckWidth + 2 * FOffset) * L;
  635. Inc(Rect.Left);
  636. if ItemState.SubItem <> '' then begin
  637. const DrawTextFormat = UDrawTextBiDiModeFlags(Self, DT_CALCRECT or DT_NOCLIP or DT_NOPREFIX or DT_SINGLELINE);
  638. SetRectEmpty(SubItemRect);
  639. DrawText(Canvas.Handle, PChar(ItemState.SubItem), Length(ItemState.SubItem),
  640. SubItemRect, DrawTextFormat);
  641. SubItemWidth := SubItemRect.Right + 2 * FOffset;
  642. Dec(Rect.Right, SubItemWidth)
  643. end else
  644. Dec(Rect.Right, FOffset);
  645. if not FWantTabs then
  646. Inc(Rect.Left);
  647. var DrawTextFormat: UINT := DT_NOCLIP or DT_CALCRECT or DT_WORDBREAK or DT_WORD_ELLIPSIS;
  648. if not FWantTabs or (ItemState.ItemType = itGroup) then
  649. DrawTextFormat := DrawTextFormat or DT_NOPREFIX;
  650. DrawTextFormat := UDrawTextBiDiModeFlags(Self, DrawTextFormat);
  651. S := Items[Index]; { Passing Items[Index] directly into DrawText doesn't work on Unicode build. }
  652. ItemState.MeasuredHeight := DrawText(Canvas.Handle, PChar(S), Length(S), Rect, DrawTextFormat);
  653. if ItemState.MeasuredHeight < FMinItemHeight then
  654. Height := FMinItemHeight
  655. else
  656. Height := ItemState.MeasuredHeight + 4;
  657. { The height must be an even number for tree lines to be painted correctly }
  658. if Odd(Height) then
  659. Inc(Height);
  660. end;
  661. end;
  662. const
  663. ColorStates: array[Boolean] of TStyleColor = (scListBoxDisabled, scListBox);
  664. TextLabelFontColorStates: array[Boolean] of TStyleFont = (sfTextLabelDisabled, sfTextLabelNormal);
  665. ListItemFontColorStates: array[Boolean] of TStyleFont = (sfListItemTextDisabled, sfListItemTextNormal);
  666. procedure TNewCheckListBox.DrawItem(Index: Integer; Rect: TRect; State: TOwnerDrawState);
  667. const
  668. ButtonStates: array [TItemType] of UINT =
  669. (
  670. 0,
  671. DFCS_BUTTONCHECK,
  672. DFCS_BUTTONRADIO
  673. );
  674. ButtonPartIds: array [TItemType] of Integer =
  675. (
  676. 0,
  677. BP_CHECKBOX,
  678. BP_RADIOBUTTON
  679. );
  680. ButtonStateIds: array [TCheckBoxState, TCheckBoxState2] of Integer =
  681. (
  682. //Can be used for both checkboxes and radiobuttons because RBS_... constants
  683. //equal CBS_... constants
  684. (CBS_UNCHECKEDNORMAL, CBS_UNCHECKEDHOT, CBS_UNCHECKEDPRESSED, CBS_UNCHECKEDDISABLED),
  685. (CBS_CHECKEDNORMAL, CBS_CHECKEDHOT, CBS_CHECKEDPRESSED, CBS_CHECKEDDISABLED),
  686. (CBS_MIXEDNORMAL, CBS_MIXEDHOT, CBS_MIXEDPRESSED, CBS_MIXEDDISABLED)
  687. );
  688. CheckListItemStates: array[Boolean] of TThemedCheckListBox = (tclListItemDisabled, tclListItemNormal);
  689. CheckBoxCheckedStates: array[Boolean] of TThemedButton = (tbCheckBoxCheckedDisabled, tbCheckBoxCheckedNormal);
  690. CheckBoxUncheckedStates: array[Boolean] of TThemedButton = (tbCheckBoxUncheckedDisabled, tbCheckBoxUncheckedNormal);
  691. CheckBoxMixedStates: array[Boolean] of TThemedButton = (tbCheckBoxMixedDisabled, tbCheckBoxMixedNormal);
  692. RadioButtonCheckedStates: array[Boolean] of TThemedButton = (tbRadioButtonCheckedDisabled, tbRadioButtonCheckedNormal);
  693. RadioButtonUncheckedStates: array[Boolean] of TThemedButton = (tbRadioButtonUncheckedDisabled, tbRadioButtonUncheckedNormal);
  694. var
  695. SavedClientRect: TRect;
  696. function FlipX(const X: Integer): Integer;
  697. begin
  698. if IsRightToLeft then
  699. Result := (SavedClientRect.Right - 1) - X
  700. else
  701. Result := X;
  702. end;
  703. procedure InternalDrawText(const S: string; var R: TRect; Format: UINT;
  704. Embossed: Boolean);
  705. begin
  706. if Embossed then
  707. begin
  708. Canvas.Brush.Style := bsClear;
  709. OffsetRect(R, 1, 1);
  710. SetTextColor(Canvas.Handle, GetSysColor(COLOR_BTNHIGHLIGHT));
  711. DrawText(Canvas.Handle, PChar(S), Length(S), R, Format);
  712. OffsetRect(R, -1, -1);
  713. SetTextColor(Canvas.Handle, GetSysColor(COLOR_BTNSHADOW));
  714. DrawText(Canvas.Handle, PChar(S), Length(S), R, Format);
  715. end
  716. else
  717. DrawText(Canvas.Handle, PChar(S), Length(S), R, Format);
  718. end;
  719. var
  720. ItemDisabled: Boolean;
  721. I, ThreadPosX, ThreadBottom, ThreadLevel, ItemMiddle: Integer;
  722. CheckRect, SubItemRect, FocusRect: TRect;
  723. NewTextColor: TColor;
  724. ItemState: TItemState;
  725. SubItemWidth: Integer;
  726. PartId, StateId: Integer;
  727. Size: TSize;
  728. begin
  729. if FShowLines and not FThreadsUpToDate then begin
  730. UpdateThreads;
  731. FThreadsUpToDate := True;
  732. end;
  733. SavedClientRect := ClientRect;
  734. { Undo flipping performed by TNewCheckListBox.CNDrawItem }
  735. FlipRect(Rect, SavedClientRect, IsRightToLeft);
  736. ItemState := ItemStates[Index];
  737. const UIState = SendMessage(Handle, WM_QUERYUISTATE, 0, 0);
  738. ItemDisabled := not Enabled or not ItemState.Enabled;
  739. { Style code below is based on Vcl.StdCtrls' TCustomListBox.CNDrawItem and Vcl.CheckLst's
  740. TCustomCheckListBox.DrawItem and .DrawCheck }
  741. var LStyle := StyleServices(Self);
  742. if not LStyle.Enabled or LStyle.IsSystemStyle then
  743. LStyle := nil;
  744. with Canvas do begin { From now on Handle refers to Canvas.Handle! }
  745. { Initialize colors }
  746. if not FWantTabs and (odSelected in State) and Focused then begin
  747. NewTextColor := clHighlightText;
  748. if (LStyle <> nil) and (seClient in StyleElements) then begin
  749. Brush.Color := LStyle.GetSystemColor(clHighlight);
  750. if seFont in StyleElements then
  751. NewTextColor := LStyle.GetStyleFontColor(sfListItemTextSelected);
  752. end else
  753. Brush.Color := clHighlight;
  754. end else begin
  755. if ItemDisabled then
  756. NewTextColor := clGrayText
  757. else
  758. NewTextColor := Self.Font.Color;
  759. if (LStyle <> nil) and (seClient in StyleElements) then begin
  760. if FWantTabs then
  761. Brush.Color := LStyle.GetStyleColor(scWindow)
  762. else
  763. Brush.Color := LStyle.GetStyleColor(ColorStates[Enabled]);
  764. if seFont in StyleElements then begin
  765. if FWantTabs then
  766. NewTextColor := LStyle.GetStyleFontColor(TextLabelFontColorStates[not ItemDisabled])
  767. else
  768. NewTextColor := LStyle.GetStyleFontColor(ListItemFontColorStates[not ItemDisabled]);
  769. const Details = LStyle.GetElementDetails(CheckListItemStates[not ItemDisabled]);
  770. var LColor: TColor;
  771. if LStyle.GetElementColor(Details, ecTextColor, LColor) and (LColor <> clNone) then
  772. NewTextColor := LColor;
  773. end;
  774. end else
  775. Brush.Color := Self.Color;
  776. end;
  777. { Draw threads }
  778. if FShowLines then begin
  779. Pen.Color := clGrayText;
  780. ThreadLevel := ItemLevel[Index];
  781. for I := 0 to ThreadLevel - 1 do
  782. if I in ItemStates[Index].ThreadCache then begin
  783. ThreadPosX := (FCheckWidth + 2 * FOffset) * I + FCheckWidth div 2 + FOffset;
  784. ItemMiddle := (Rect.Bottom - Rect.Top) div 2 + Rect.Top;
  785. ThreadBottom := Rect.Bottom;
  786. if I = ThreadLevel - 1 then begin
  787. if ItemStates[Index].IsLastChild then
  788. ThreadBottom := ItemMiddle;
  789. LineDDA(FlipX(ThreadPosX), ItemMiddle, FlipX(ThreadPosX + FCheckWidth div 2 + FOffset),
  790. ItemMiddle, @LineDDAProc, LPARAM(Canvas));
  791. end;
  792. LineDDA(FlipX(ThreadPosX), Rect.Top, FlipX(ThreadPosX), ThreadBottom,
  793. @LineDDAProc, LPARAM(Canvas));
  794. end;
  795. end;
  796. { Draw checkmark}
  797. if ItemState.ItemType <> itGroup then begin
  798. CheckRect := Bounds(Rect.Left - (FCheckWidth + FOffset),
  799. Rect.Top + ((Rect.Bottom - Rect.Top - FCheckHeight) div 2),
  800. FCheckWidth, FCheckHeight);
  801. FlipRect(CheckRect, SavedClientRect, IsRightToLeft);
  802. if (LStyle <> nil) and not FDisableStyledButtons then begin
  803. var Detail: TThemedButton;
  804. if ItemState.State <> cbGrayed then begin
  805. if ItemState.ItemType = itCheck then begin
  806. if ItemState.State = cbChecked then
  807. Detail := CheckBoxCheckedStates[not ItemDisabled]
  808. else
  809. Detail := CheckBoxUncheckedStates[not ItemDisabled];
  810. end else begin
  811. if ItemState.State = cbChecked then
  812. Detail := RadioButtonCheckedStates[not ItemDisabled]
  813. else
  814. Detail := RadioButtonUncheckedStates[not ItemDisabled];
  815. end;
  816. end else
  817. Detail := CheckBoxMixedStates[not ItemDisabled];
  818. const ElementDetails = LStyle.GetElementDetails(Detail);
  819. const SaveColor = Brush.Color;
  820. const SaveIndex = SaveDC(Handle);
  821. try
  822. LStyle.DrawElement(Handle, ElementDetails, CheckRect, nil, CurrentPPI);
  823. finally
  824. RestoreDC(Handle, SaveIndex);
  825. end;
  826. Brush.Color := SaveColor;
  827. end else if FThemeData = 0 then begin
  828. var uState: UINT;
  829. case ItemState.State of
  830. cbChecked: uState := ButtonStates[ItemState.ItemType] or DFCS_CHECKED;
  831. cbUnchecked: uState := ButtonStates[ItemState.ItemType];
  832. else
  833. uState := DFCS_BUTTON3STATE or DFCS_CHECKED;
  834. end;
  835. if FFlat then
  836. uState := uState or DFCS_FLAT;
  837. if ItemDisabled then
  838. uState := uState or DFCS_INACTIVE;
  839. if (FCaptureIndex = Index) and (FSpaceDown or (FLastMouseMoveIndex = Index)) then
  840. uState := uState or DFCS_PUSHED;
  841. DrawFrameControl(Handle, CheckRect, DFC_BUTTON, uState)
  842. end else begin
  843. PartId := ButtonPartIds[ItemState.ItemType];
  844. if ItemDisabled then
  845. StateId := ButtonStateIds[ItemState.State][cb2Disabled]
  846. else if Index = FCaptureIndex then
  847. if FSpaceDown or (FLastMouseMoveIndex = Index) then
  848. StateId := ButtonStateIds[ItemState.State][cb2Pressed]
  849. else
  850. StateId := ButtonStateIds[ItemState.State][cb2Hot]
  851. else if (FCaptureIndex < 0) and (Index = FHotIndex) then
  852. StateId := ButtonStateIds[ItemState.State][cb2Hot]
  853. else
  854. StateId := ButtonStateIds[ItemState.State][cb2Normal];
  855. GetThemePartSize(FThemeData, Handle, PartId, StateId, @CheckRect, TS_TRUE, Size);
  856. if (Size.cx <> FCheckWidth) or (Size.cy <> FCheckHeight) then begin
  857. CheckRect := Bounds(Rect.Left - (Size.cx + FOffset),
  858. Rect.Top + ((Rect.Bottom - Rect.Top - Size.cy) div 2),
  859. Size.cx, Size.cy);
  860. FlipRect(CheckRect, SavedClientRect, IsRightToLeft);
  861. end;
  862. //if IsThemeBackgroundPartiallyTransparent(FThemeData, PartId, StateId) then
  863. // DrawThemeParentBackground(Self.Handle, Handle, @CheckRect);
  864. DrawThemeBackGround(FThemeData, Handle, PartId, StateId, CheckRect, @CheckRect);
  865. end;
  866. end;
  867. { Draw background & subitem }
  868. FlipRect(Rect, SavedClientRect, IsRightToLeft);
  869. if TransparentIfStyled and (LStyle <> nil) then begin
  870. { Same method as TTrackBar.CNNotify uses }
  871. const Rgn = CreateRectRgn(Rect.Left, Rect.Top, Rect.Right, Rect.Bottom);
  872. SelectClipRgn(Handle, Rgn);
  873. LStyle.DrawParentBackground(Self.Handle, Handle, nil, False, Rect);
  874. DeleteObject(Rgn);
  875. SelectClipRgn(Handle, 0);
  876. end else
  877. FillRect(Rect);
  878. FlipRect(Rect, SavedClientRect, IsRightToLeft);
  879. Inc(Rect.Left);
  880. const OldColor = SetTextColor(Handle, UColorToRGB(NewTextColor));
  881. if ItemState.SubItem <> '' then
  882. begin
  883. const DrawTextFormat = UDrawTextBiDiModeFlags(Self, DT_NOCLIP or DT_NOPREFIX or DT_SINGLELINE or DT_VCENTER);
  884. Font.Style := ItemState.SubItemFontStyle;
  885. SetRectEmpty(SubItemRect);
  886. InternalDrawText(ItemState.SubItem, SubItemRect, DrawTextFormat or
  887. DT_CALCRECT, False);
  888. SubItemWidth := SubItemRect.Right + 2 * FOffset;
  889. SubItemRect := Rect;
  890. SubItemRect.Left := SubItemRect.Right - SubItemWidth + FOffset;
  891. FlipRect(SubItemRect, SavedClientRect, IsRightToLeft);
  892. InternalDrawText(ItemState.SubItem, SubItemRect, DrawTextFormat,
  893. FWantTabs and ItemDisabled);
  894. Dec(Rect.Right, SubItemWidth);
  895. end
  896. else
  897. Dec(Rect.Right, FOffset);
  898. { Draw item text }
  899. if not FWantTabs then
  900. Inc(Rect.Left);
  901. OffsetRect(Rect, 0, (Rect.Bottom - Rect.Top - ItemState.MeasuredHeight) div 2);
  902. var DrawTextFormat: UINT := DT_NOCLIP or DT_WORDBREAK or DT_WORD_ELLIPSIS;
  903. if not FWantTabs or (ItemState.ItemType = itGroup) then
  904. DrawTextFormat := DrawTextFormat or DT_NOPREFIX;
  905. if (UIState and UISF_HIDEACCEL) <> 0 then
  906. DrawTextFormat := DrawTextFormat or DT_HIDEPREFIX;
  907. DrawTextFormat := UDrawTextBiDiModeFlags(Self, DrawTextFormat);
  908. Font.Style := ItemState.ItemFontStyle;
  909. { When you call DrawText with the DT_CALCRECT flag and there's a word wider
  910. than the rectangle width, it increases the rectangle width and wraps
  911. at the new Right point. On the other hand, when you call DrawText
  912. _without_ the DT_CALCRECT flag, it always wraps at the Right point you
  913. specify -- it doesn't check for long words first.
  914. Therefore, to ensure we wrap at the same place when drawing as when
  915. measuring, pass our rectangle to DrawText with DT_CALCRECT first.
  916. Wrapping at the same place is important because it can affect how many
  917. lines are drawn -- and we mustn't draw too many. }
  918. InternalDrawText(Items[Index], Rect, DrawTextFormat or DT_CALCRECT, False);
  919. FlipRect(Rect, SavedClientRect, IsRightToLeft);
  920. const Embossed = FWantTabs and ItemDisabled and (LStyle = nil);
  921. if TransparentIfStyled and (LStyle <> nil) then begin
  922. const OldBkMode = SetBkMode(Handle, Windows.TRANSPARENT);
  923. InternalDrawText(Items[Index], Rect, DrawTextFormat, Embossed);
  924. SetBkMode(Handle, OldBkMode);
  925. end else
  926. InternalDrawText(Items[Index], Rect, DrawTextFormat, Embossed);
  927. { Draw focus rectangle }
  928. if FWantTabs and not ItemDisabled and (odSelected in State) and Focused and
  929. (UIState and UISF_HIDEFOCUS = 0) then
  930. begin
  931. FocusRect := Rect;
  932. InflateRect(FocusRect, 1, 1);
  933. DrawFocusRect(FocusRect);
  934. end;
  935. SetTextColor(Handle, OldColor);
  936. end;
  937. end;
  938. procedure TNewCheckListBox.EndCapture(Cancel: Boolean);
  939. var
  940. InvalidateItem: Boolean;
  941. Item: Integer;
  942. begin
  943. Item := FCaptureIndex;
  944. if Item >= 0 then
  945. begin
  946. InvalidateItem := FSpaceDown or (FCaptureIndex = FLastMouseMoveIndex) or (FThemeData <> 0);
  947. FSpaceDown := False;
  948. FCaptureIndex := -1;
  949. FLastMouseMoveIndex := -1;
  950. if not Cancel then
  951. Toggle(Item);
  952. if InvalidateItem then
  953. InvalidateCheck(Item);
  954. end;
  955. if MouseCapture then
  956. MouseCapture := False;
  957. end;
  958. procedure TNewCheckListBox.EnumChildrenOf(Item: Integer; Proc: TEnumChildrenProc;
  959. Ext: NativeInt);
  960. var
  961. L: Integer;
  962. begin
  963. if (Item < -1) or (Item >= Items.Count) then
  964. Exit;
  965. if Item = -1 then
  966. begin
  967. L := 0;
  968. Item := 0;
  969. end
  970. else
  971. begin
  972. L := ItemLevel[Item] + 1;
  973. Inc(Item);
  974. end;
  975. while (Item < Items.Count) and (ItemLevel[Item] >= L) do
  976. begin
  977. if ItemLevel[Item] = L then
  978. Proc(Item, (Item < Items.Count - 1) and (ItemLevel[Item + 1] > L), Ext);
  979. Inc(Item);
  980. end;
  981. end;
  982. function TNewCheckListBox.AddItem2(AType: TItemType;
  983. const ACaption, ASubItem: string; ALevel: Byte;
  984. AChecked, AEnabled, AHasInternalChildren, ACheckWhenParentChecked: Boolean;
  985. AObject: TObject): Integer;
  986. var
  987. ItemState: TItemState;
  988. I: Integer;
  989. begin
  990. if Items.Count <> FStateList.Count then { sanity check }
  991. raise Exception.Create('List item and state item count mismatch');
  992. if Items.Count > 0 then
  993. begin
  994. if ItemLevel[Items.Count - 1] + 1 < ALevel then
  995. ALevel := Byte(ItemLevel[Items.Count - 1] + 1);
  996. end
  997. else
  998. ALevel := 0;
  999. FThreadsUpToDate := False;
  1000. { Use our own grow code to minimize heap fragmentation }
  1001. if FStateList.Count = FStateList.Capacity then begin
  1002. if FStateList.Capacity < 64 then
  1003. FStateList.Capacity := 64
  1004. else
  1005. FStateList.Capacity := FStateList.Capacity * 2;
  1006. end;
  1007. ItemState := TItemState.Create;
  1008. try
  1009. ItemState.ItemType := AType;
  1010. ItemState.Enabled := AEnabled;
  1011. ItemState.Obj := AObject;
  1012. ItemState.Level := ALevel;
  1013. ItemState.SubItem := ASubItem;
  1014. ItemState.HasInternalChildren := AHasInternalChildren;
  1015. ItemState.CheckWhenParentChecked := ACheckWhenParentChecked;
  1016. except
  1017. ItemState.Free;
  1018. raise;
  1019. end;
  1020. FStateList.Add(ItemState);
  1021. try
  1022. Result := Items.Add(ACaption);
  1023. except
  1024. FStateList.Delete(FStateList.Count-1);
  1025. ItemState.Free;
  1026. raise;
  1027. end;
  1028. { If the first item in a radio group is being added, and it is top-level or
  1029. has a checked parent, force it to be checked. (We don't want to allow radio
  1030. groups with no selection.) }
  1031. if (AType = itRadio) and not AChecked and AEnabled then begin
  1032. I := GetParentOf(Result);
  1033. { FRequireRadioSelection only affects top-level items; we never allow
  1034. child radio groups with no selection (because nobody should need that) }
  1035. if FRequireRadioSelection or (I <> -1) then
  1036. if (I = -1) or (GetState(I) <> cbUnchecked) then
  1037. if FindCheckedSibling(Result) = -1 then
  1038. AChecked := True;
  1039. end;
  1040. SetChecked(Result, AChecked);
  1041. end;
  1042. function TNewCheckListBox.FindAccel(VK: Word): Integer;
  1043. begin
  1044. for Result := 0 to Items.Count - 1 do
  1045. if CanFocusItem(Result) and IsAccel(VK, Items[Result]) then
  1046. Exit;
  1047. Result := -1;
  1048. end;
  1049. function TNewCheckListBox.FindNextItem(StartFrom: Integer; GoForward,
  1050. SkipUncheckedRadios: Boolean): Integer;
  1051. function ShouldSkip(Index: Integer): Boolean;
  1052. begin
  1053. with ItemStates[Index] do
  1054. Result := (ItemType = itRadio) and (State <> cbChecked)
  1055. end;
  1056. var
  1057. Delta: Integer;
  1058. begin
  1059. if StartFrom < -1 then
  1060. StartFrom := ItemIndex;
  1061. if Items.Count > 0 then
  1062. begin
  1063. Delta := Ord(GoForward) * 2 - 1;
  1064. Result := StartFrom + Delta;
  1065. while (Result >= 0) and (Result < Items.Count) and
  1066. (not CanFocusItem(Result) or SkipUncheckedRadios and ShouldSkip(Result)) do
  1067. Result := Result + Delta;
  1068. if (Result < 0) or (Result >= Items.Count) then
  1069. Result := -1;
  1070. end
  1071. else
  1072. Result := -1;
  1073. end;
  1074. function TNewCheckListBox.GetCaption(Index: Integer): String;
  1075. begin
  1076. Result := Items[Index];
  1077. end;
  1078. function TNewCheckListBox.GetChecked(Index: Integer): Boolean;
  1079. begin
  1080. Result := GetState(Index) <> cbUnchecked;
  1081. end;
  1082. function TNewCheckListBox.GetItemEnabled(Index: Integer): Boolean;
  1083. begin
  1084. Result := ItemStates[Index].Enabled;
  1085. end;
  1086. function TNewCheckListBox.GetItemFontStyle(Index: Integer): TFontStyles;
  1087. begin
  1088. Result := ItemStates[Index].ItemFontStyle;
  1089. end;
  1090. function TNewCheckListBox.GetItemState(Index: Integer): TItemState;
  1091. begin
  1092. Result := FStateList[Index];
  1093. end;
  1094. function TNewCheckListBox.GetLevel(Index: Integer): Byte;
  1095. begin
  1096. Result := ItemStates[Index].Level;
  1097. end;
  1098. function TNewCheckListBox.GetObject(Index: Integer): TObject;
  1099. begin
  1100. Result := ItemStates[Index].Obj;
  1101. end;
  1102. function TNewCheckListBox.GetParentOf(Item: Integer): Integer;
  1103. { Gets index of Item's parent, or -1 if there is none. }
  1104. var
  1105. Level, I: Integer;
  1106. begin
  1107. Level := ItemStates[Item].Level;
  1108. if Level > 0 then
  1109. for I := Item-1 downto 0 do begin
  1110. if ItemStates[I].Level < Level then begin
  1111. Result := I;
  1112. Exit;
  1113. end;
  1114. end;
  1115. Result := -1;
  1116. end;
  1117. function TNewCheckListBox.GetState(Index: Integer): TCheckBoxState;
  1118. begin
  1119. Result := ItemStates[Index].State;
  1120. end;
  1121. function TNewCheckListBox.GetSubItem(Index: Integer): String;
  1122. begin
  1123. Result := ItemStates[Index].SubItem;
  1124. end;
  1125. function TNewCheckListBox.GetSubItemFontStyle(Index: Integer): TFontStyles;
  1126. begin
  1127. Result := ItemStates[Index].SubItemFontStyle;
  1128. end;
  1129. function TNewCheckListBox.GetTransparentIfStyled: Boolean;
  1130. begin
  1131. Result := WantTabs;
  1132. end;
  1133. procedure TNewCheckListBox.HandleScroll;
  1134. begin
  1135. { Windows copies item backgrounds when scrolling, but if the listbox is
  1136. transparent and its parent background is complex (such as a bitmap),
  1137. the item backgrounds need to be updated. Can be called even if it's
  1138. not sure the list was actually scrolled. }
  1139. if FComplexParentBackground and TransparentIfStyled and IsCustomStyleActive then begin
  1140. var ScrollBarInfo: TScrollBarInfo;
  1141. ScrollBarInfo.cbSize := SizeOf(ScrollBarInfo);
  1142. if GetScrollBarInfo(Handle, Integer(OBJID_VSCROLL), ScrollBarInfo) and
  1143. (ScrollBarInfo.rgstate[0] <> STATE_SYSTEM_INVISIBLE) then
  1144. InvalidateRect(Handle, nil, True);
  1145. end;
  1146. end;
  1147. procedure TNewCheckListBox.InvalidateCheck(Index: Integer);
  1148. var
  1149. IRect: TRect;
  1150. begin
  1151. IRect := ItemRect(Index);
  1152. Inc(IRect.Left, (FCheckWidth + 2 * Offset) * (ItemLevel[Index]));
  1153. IRect.Right := IRect.Left + (FCheckWidth + 2 * Offset);
  1154. FlipRect(IRect, ClientRect, IsRightToLeft);
  1155. InvalidateRect(Handle, @IRect, FThemeData <> 0);
  1156. end;
  1157. procedure TNewCheckListBox.KeyDown(var Key: Word; Shift: TShiftState);
  1158. begin
  1159. if (Key = VK_SPACE) and not (ssAlt in Shift) and (ItemIndex >= 0) and
  1160. (FCaptureIndex < 0) and CanFocusItem(ItemIndex) then
  1161. if FWantTabs then begin
  1162. if not FSpaceDown then begin
  1163. FCaptureIndex := ItemIndex;
  1164. FSpaceDown := True;
  1165. InvalidateCheck(ItemIndex);
  1166. if (FHotIndex <> ItemIndex) and (FHotIndex <> -1) and (FThemeData <> 0) then
  1167. InvalidateCheck(FHotIndex);
  1168. end;
  1169. end
  1170. else
  1171. Toggle(ItemIndex);
  1172. inherited;
  1173. end;
  1174. procedure TNewCheckListBox.KeyUp(var Key: Word; Shift: TShiftState);
  1175. begin
  1176. if (Key = VK_SPACE) and FWantTabs and FSpaceDown and (FCaptureIndex >= 0) then begin
  1177. EndCapture(False);
  1178. if (FHotIndex <> -1) and (FThemeData <> 0) then
  1179. InvalidateCheck(FHotIndex);
  1180. end;
  1181. inherited;
  1182. end;
  1183. procedure TNewCheckListBox.MouseDown(Button: TMouseButton; Shift: TShiftState;
  1184. X, Y: Integer);
  1185. var
  1186. Index: Integer;
  1187. begin
  1188. if Button = mbLeft then begin
  1189. Index := ItemAtPos(Point(X, Y), True);
  1190. if (Index <> -1) and CanFocusItem(Index) then
  1191. begin
  1192. if FWantTabs then begin
  1193. if not FSpaceDown then begin
  1194. if not MouseCapture then
  1195. MouseCapture := True;
  1196. FCaptureIndex := Index;
  1197. FLastMouseMoveIndex := Index;
  1198. InvalidateCheck(Index);
  1199. HandleScroll; { Might have scrolled a new item into view }
  1200. end;
  1201. end
  1202. else
  1203. Toggle(Index);
  1204. end;
  1205. end;
  1206. inherited;
  1207. end;
  1208. procedure TNewCheckListBox.MouseUp(Button: TMouseButton; Shift: TShiftState;
  1209. X, Y: Integer);
  1210. var
  1211. Index: Integer;
  1212. begin
  1213. if (Button = mbLeft) and FWantTabs and not FSpaceDown and (FCaptureIndex >= 0) then
  1214. begin
  1215. Index := ItemAtPos(Point(X, Y), True);
  1216. EndCapture(Index <> FCaptureIndex);
  1217. if (FHotIndex <> -1) and (FThemeData <> 0) then
  1218. InvalidateCheck(FHotIndex);
  1219. end;
  1220. end;
  1221. procedure TNewCheckListBox.UpdateHotIndex(NewHotIndex: Integer);
  1222. var
  1223. OldHotIndex: Integer;
  1224. begin
  1225. OldHotIndex := FHotIndex;
  1226. if NewHotIndex <> OldHotIndex then
  1227. begin
  1228. FHotIndex := NewHotIndex;
  1229. if FCaptureIndex = -1 then begin
  1230. if (OldHotIndex <> -1) and (FThemeData <> 0) then
  1231. InvalidateCheck(OldHotIndex);
  1232. if (NewHotIndex <> -1) and (FThemeData <> 0) then
  1233. InvalidateCheck(NewHotIndex);
  1234. end;
  1235. end;
  1236. end;
  1237. procedure TNewCheckListBox.CMMouseLeave(var Message: TMessage);
  1238. begin
  1239. UpdateHotIndex(-1);
  1240. inherited;
  1241. end;
  1242. procedure TNewCheckListBox.SetCaption(Index: Integer; const Value: String);
  1243. begin
  1244. { Changing an item's text actually involves deleting and re-inserting the
  1245. item. Increment FDisableItemStateDeletion so the item state isn't lost. }
  1246. Inc(FDisableItemStateDeletion);
  1247. try
  1248. Items[Index] := Value;
  1249. finally
  1250. Dec(FDisableItemStateDeletion);
  1251. end;
  1252. end;
  1253. procedure TNewCheckListBox.SetChecked(Index: Integer; const AChecked: Boolean);
  1254. begin
  1255. if AChecked then
  1256. CheckItem(Index, coCheck)
  1257. else
  1258. CheckItem(Index, coUncheck);
  1259. end;
  1260. function TNewCheckListBox.FindCheckedSibling(const AIndex: Integer): Integer;
  1261. { Finds a checked sibling of AIndex (which is assumed to be a radio button).
  1262. Returns -1 if no checked sibling was found. }
  1263. var
  1264. ThisLevel, I: Integer;
  1265. begin
  1266. ThisLevel := ItemStates[AIndex].Level;
  1267. for I := AIndex-1 downto 0 do begin
  1268. if ItemStates[I].Level < ThisLevel then
  1269. Break;
  1270. if ItemStates[I].Level = ThisLevel then begin
  1271. if ItemStates[I].ItemType <> itRadio then
  1272. Break;
  1273. if GetState(I) <> cbUnchecked then begin
  1274. Result := I;
  1275. Exit;
  1276. end;
  1277. end;
  1278. end;
  1279. for I := AIndex+1 to Items.Count-1 do begin
  1280. if ItemStates[I].Level < ThisLevel then
  1281. Break;
  1282. if ItemStates[I].Level = ThisLevel then begin
  1283. if ItemStates[I].ItemType <> itRadio then
  1284. Break;
  1285. if GetState(I) <> cbUnchecked then begin
  1286. Result := I;
  1287. Exit;
  1288. end;
  1289. end;
  1290. end;
  1291. Result := -1;
  1292. end;
  1293. function TNewCheckListBox.CheckItem(const Index: Integer;
  1294. const AOperation: TCheckItemOperation): Boolean;
  1295. { Tries to update the checked state of Index. Returns True if any changes were
  1296. made to the state of Index or any of its children. }
  1297. procedure SetItemState(const AIndex: Integer; const AState: TCheckBoxState);
  1298. begin
  1299. if ItemStates[AIndex].State <> AState then begin
  1300. ItemStates[AIndex].State := AState;
  1301. InvalidateCheck(AIndex);
  1302. { Notify MSAA of the state change }
  1303. if Assigned(NotifyWinEventFunc) then
  1304. NotifyWinEventFunc(EVENT_OBJECT_STATECHANGE, Handle, OBJID_CLIENT,
  1305. 1 + AIndex);
  1306. end;
  1307. end;
  1308. function CalcState(const AIndex: Integer; ACheck: Boolean): TCheckBoxState;
  1309. { Determines new state for AIndex based on desired checked state (ACheck) and
  1310. current state of the item's immediate children. }
  1311. var
  1312. RootLevel, I: Integer;
  1313. HasChecked, HasUnchecked: Boolean;
  1314. begin
  1315. HasChecked := False;
  1316. HasUnchecked := False;
  1317. RootLevel := ItemStates[AIndex].Level;
  1318. for I := AIndex+1 to Items.Count-1 do begin
  1319. if ItemStates[I].Level <= RootLevel then
  1320. Break;
  1321. if (ItemStates[I].Level = RootLevel+1) and
  1322. (ItemStates[I].ItemType in [itCheck, itRadio]) then begin
  1323. case GetState(I) of
  1324. cbUnchecked: begin
  1325. if (ItemStates[I].ItemType <> itRadio) or
  1326. (FindCheckedSibling(I) = -1) then
  1327. HasUnchecked := True;
  1328. end;
  1329. cbChecked: begin
  1330. HasChecked := True;
  1331. end;
  1332. cbGrayed: begin
  1333. HasChecked := True;
  1334. HasUnchecked := True;
  1335. end;
  1336. end;
  1337. end;
  1338. end;
  1339. { If the parent is a check box with children, don't allow it to be checked
  1340. if none of its children are checked, unless it "has internal children" }
  1341. if HasUnchecked and not HasChecked and
  1342. (ItemStates[AIndex].ItemType = itCheck) and
  1343. not ItemStates[AIndex].HasInternalChildren then
  1344. ACheck := False;
  1345. if ACheck or HasChecked then begin
  1346. if HasUnchecked and (ItemStates[AIndex].ItemType = itCheck) then
  1347. Result := cbGrayed
  1348. else
  1349. Result := cbChecked;
  1350. end
  1351. else
  1352. Result := cbUnchecked;
  1353. end;
  1354. function RecursiveCheck(const AIndex: Integer;
  1355. const AOperation: TCheckItemOperation): Boolean;
  1356. { Checks or unchecks AIndex and all enabled child items of AIndex at any
  1357. level. In radio button groups, only one item per group is checked.
  1358. Returns True if any of the items' states were changed. }
  1359. var
  1360. RootLevel, I: Integer;
  1361. NewState: TCheckBoxState;
  1362. begin
  1363. Result := False;
  1364. RootLevel := ItemStates[AIndex].Level;
  1365. for I := AIndex+1 to Items.Count-1 do begin
  1366. if ItemStates[I].Level <= RootLevel then
  1367. Break;
  1368. if (ItemStates[I].Level = RootLevel+1) and ItemStates[I].Enabled and
  1369. ((AOperation = coUncheck) or
  1370. ((AOperation = coCheckWithChildren) and ItemStates[I].CheckWhenParentChecked) or
  1371. (ItemStates[I].ItemType = itRadio)) then
  1372. { If checking and I is a radio button, don't recurse if a sibling
  1373. already got checked in a previous iteration of this loop. This is
  1374. needed in the following case to prevent all three radio buttons from
  1375. being checked when "Parent check" is checked. In addition, it
  1376. prevents "Child check" from being checked.
  1377. [ ] Parent check
  1378. ( ) Radio 1
  1379. ( ) Radio 2
  1380. ( ) Radio 3
  1381. [ ] Child check
  1382. }
  1383. if (AOperation = coUncheck) or (ItemStates[I].ItemType <> itRadio) or
  1384. (FindCheckedSibling(I) = -1) then
  1385. if RecursiveCheck(I, AOperation) then
  1386. Result := True;
  1387. end;
  1388. NewState := CalcState(AIndex, AOperation <> coUncheck);
  1389. if GetState(AIndex) <> NewState then begin
  1390. SetItemState(AIndex, NewState);
  1391. Result := True;
  1392. end;
  1393. end;
  1394. procedure UncheckSiblings(const AIndex: Integer);
  1395. { Unchecks all siblings (and their children) of AIndex, which is assumed to
  1396. be a radio button. }
  1397. var
  1398. I: Integer;
  1399. begin
  1400. while True do begin
  1401. I := FindCheckedSibling(AIndex);
  1402. if I = -1 then
  1403. Break;
  1404. RecursiveCheck(I, coUncheck);
  1405. end;
  1406. end;
  1407. procedure EnsureChildRadioItemsHaveSelection(const AIndex: Integer);
  1408. { Ensures all radio button groups that are immediate children of AIndex have
  1409. a selected item. }
  1410. var
  1411. RootLevel, I: Integer;
  1412. begin
  1413. RootLevel := ItemStates[AIndex].Level;
  1414. for I := AIndex+1 to Items.Count-1 do begin
  1415. if ItemStates[I].Level <= RootLevel then
  1416. Break;
  1417. if (ItemStates[I].Level = RootLevel+1) and
  1418. (ItemStates[I].ItemType = itRadio) and
  1419. ItemStates[I].Enabled and
  1420. (GetState(I) <> cbChecked) and
  1421. (FindCheckedSibling(I) = -1) then
  1422. { Note: This uses coCheck instead of coCheckWithChildren (or the value
  1423. of AOperation) in order to keep side effects to a minimum. Seems
  1424. like the most logical behavior. For example, in this case:
  1425. [ ] A
  1426. ( ) B
  1427. [ ] C
  1428. [ ] D
  1429. clicking D will cause the radio button B to be checked (out of
  1430. necessity), but won't automatically check its child check box, C.
  1431. (If C were instead a radio button, it *would* be checked.) }
  1432. RecursiveCheck(I, coCheck);
  1433. end;
  1434. end;
  1435. procedure UpdateParentStates(const AIndex: Integer);
  1436. var
  1437. I: Integer;
  1438. ChildChecked: Boolean;
  1439. NewState: TCheckBoxState;
  1440. begin
  1441. I := AIndex;
  1442. while True do begin
  1443. ChildChecked := (GetState(I) <> cbUnchecked);
  1444. I := GetParentOf(I);
  1445. if I = -1 then
  1446. Break;
  1447. { When a child item is checked, must ensure that all sibling radio button
  1448. groups have selections }
  1449. if ChildChecked then
  1450. EnsureChildRadioItemsHaveSelection(I);
  1451. NewState := CalcState(I, GetState(I) <> cbUnchecked);
  1452. { If a parent radio button is becoming checked, uncheck any previously
  1453. selected sibling of that radio button }
  1454. if (NewState <> cbUnchecked) and (ItemStates[I].ItemType = itRadio) then
  1455. UncheckSiblings(I);
  1456. SetItemState(I, NewState);
  1457. end;
  1458. end;
  1459. begin
  1460. if ItemStates[Index].ItemType = itRadio then begin
  1461. { Setting Checked to False on a radio button is a no-op. (A radio button
  1462. may only be unchecked by checking another radio button in the group, or
  1463. by unchecking a parent check box.) }
  1464. if AOperation = coUncheck then begin
  1465. Result := False;
  1466. Exit;
  1467. end;
  1468. { Before checking a new item in a radio group, uncheck any siblings and
  1469. their children }
  1470. UncheckSiblings(Index);
  1471. end;
  1472. { Check or uncheck this item and all its children }
  1473. Result := RecursiveCheck(Index, AOperation);
  1474. { Update state of parents. For example, if a child check box is being
  1475. checked, its parent must also become checked if it isn't already. }
  1476. UpdateParentStates(Index);
  1477. end;
  1478. procedure TNewCheckListBox.SetFlat(Value: Boolean);
  1479. begin
  1480. if Value <> FFlat then
  1481. begin
  1482. FFlat := Value;
  1483. Invalidate;
  1484. end;
  1485. end;
  1486. procedure TNewCheckListBox.SetItemEnabled(Index: Integer; const AEnabled: Boolean);
  1487. begin
  1488. if ItemStates[Index].Enabled <> AEnabled then
  1489. begin
  1490. ItemStates[Index].Enabled := AEnabled;
  1491. InvalidateCheck(Index);
  1492. end;
  1493. end;
  1494. procedure TNewCheckListBox.SetItemFontStyle(Index: Integer; const AItemFontStyle: TFontStyles);
  1495. var
  1496. R: TRect;
  1497. begin
  1498. if ItemStates[Index].ItemFontStyle <> AItemFontStyle then begin
  1499. ItemStates[Index].ItemFontStyle := AItemFontStyle;
  1500. R := ItemRect(Index);
  1501. InvalidateRect(Handle, @R, True);
  1502. end;
  1503. end;
  1504. procedure TNewCheckListBox.SetItemIndex(const Value: Integer);
  1505. begin
  1506. const Before = ItemIndex;
  1507. inherited;
  1508. const After = ItemIndex;
  1509. if (Before <> After) then
  1510. HandleScroll; { Might have scrolled a new item into view }
  1511. end;
  1512. procedure TNewCheckListBox.SetObject(Index: Integer; const AObject: TObject);
  1513. begin
  1514. ItemStates[Index].Obj := AObject;
  1515. end;
  1516. procedure TNewCheckListBox.SetOffset(AnOffset: Integer);
  1517. begin
  1518. if FOffset <> AnOffset then
  1519. begin
  1520. FOffset := AnOffset;
  1521. Invalidate;
  1522. end;
  1523. end;
  1524. procedure TNewCheckListBox.SetShowLines(Value: Boolean);
  1525. begin
  1526. if FShowLines <> Value then
  1527. begin
  1528. FShowLines := Value;
  1529. Invalidate;
  1530. end;
  1531. end;
  1532. procedure TNewCheckListBox.SetSubItem(Index: Integer; const ASubItem: String);
  1533. var
  1534. OldHeight, NewHeight: Integer;
  1535. R, R2: TRect;
  1536. begin
  1537. if ItemStates[Index].SubItem <> ASubItem then
  1538. begin
  1539. ItemStates[Index].SubItem := ASubItem;
  1540. OldHeight := Integer(SendMessage(Handle, LB_GETITEMHEIGHT, Index, 0));
  1541. NewHeight := RemeasureItem(Index);
  1542. R := ItemRect(Index);
  1543. { Scroll subsequent items down or up, if necessary }
  1544. if NewHeight <> OldHeight then begin
  1545. if Index >= TopIndex then begin
  1546. R2 := ClientRect;
  1547. R2.Top := R.Top + OldHeight;
  1548. if not IsRectEmpty(R2) then
  1549. ScrollWindowEx(Handle, 0, NewHeight - OldHeight, @R2, nil, 0, nil,
  1550. SW_INVALIDATE or SW_ERASE);
  1551. end;
  1552. UpdateScrollRange;
  1553. end;
  1554. InvalidateRect(Handle, @R, True);
  1555. end;
  1556. end;
  1557. procedure TNewCheckListBox.SetSubItemFontStyle(Index: Integer; const ASubItemFontStyle: TFontStyles);
  1558. var
  1559. R: TRect;
  1560. begin
  1561. if ItemStates[Index].SubItemFontStyle <> ASubItemFontStyle then begin
  1562. ItemStates[Index].SubItemFontStyle := ASubItemFontStyle;
  1563. R := ItemRect(Index);
  1564. InvalidateRect(Handle, @R, True);
  1565. end;
  1566. end;
  1567. procedure TNewCheckListBox.Toggle(Index: Integer);
  1568. begin
  1569. case ItemStates[Index].ItemType of
  1570. itCheck:
  1571. case ItemStates[Index].State of
  1572. cbUnchecked: CheckItem(Index, coCheckWithChildren);
  1573. cbChecked: CheckItem(Index, coUncheck);
  1574. cbGrayed:
  1575. { First try checking, but if that doesn't work because of children
  1576. that are disabled and unchecked, try unchecking }
  1577. if not CheckItem(Index, coCheckWithChildren) then
  1578. CheckItem(Index, coUncheck);
  1579. end;
  1580. itRadio: CheckItem(Index, coCheckWithChildren);
  1581. end;
  1582. if Assigned(FOnClickCheck) then
  1583. FOnClickCheck(Self);
  1584. end;
  1585. procedure TNewCheckListBox.UpdateThreads;
  1586. function LastImmediateChildOf(Item: Integer): Integer;
  1587. var
  1588. L: Integer;
  1589. begin
  1590. Result := -1;
  1591. L := ItemLevel[Item] + 1;
  1592. Inc(Item);
  1593. while (Item < Items.Count) and (ItemLevel[Item] >= L) do
  1594. begin
  1595. if ItemLevel[Item] = L then
  1596. Result := Item;
  1597. Inc(Item);
  1598. end;
  1599. if Result >= 0 then
  1600. ItemStates[Result].IsLastChild := True;
  1601. end;
  1602. var
  1603. I, J, LastChild, L: Integer;
  1604. begin
  1605. for I := 0 to Items.Count - 1 do
  1606. begin
  1607. ItemStates[I].ThreadCache := [0]; //Doing ':= []' causes a "F2084 Internal Error: C21846" compiler error on Delphi 10.3 Rio }
  1608. Exclude(ItemStates[I].ThreadCache, 0); //
  1609. ItemStates[I].IsLastChild := False;
  1610. end;
  1611. for I := 0 to Items.Count - 1 do
  1612. begin
  1613. LastChild := LastImmediateChildOf(I);
  1614. L := ItemLevel[I];
  1615. for J := I + 1 to LastChild do
  1616. Include(ItemStates[J].ThreadCache, L);
  1617. end;
  1618. end;
  1619. procedure TNewCheckListBox.LBDeleteString(var Message: TMessage);
  1620. var
  1621. ItemState: TItemState;
  1622. begin
  1623. inherited;
  1624. if FDisableItemStateDeletion = 0 then begin
  1625. const I = Integer(Message.WParam);
  1626. if (I >= 0) and (I < FStateList.Count) then begin
  1627. ItemState := FStateList[I];
  1628. FStateList.Delete(I);
  1629. ItemState.Free;
  1630. end;
  1631. end;
  1632. end;
  1633. procedure TNewCheckListBox.LBResetContent(var Message: TMessage);
  1634. var
  1635. ItemState: TItemState;
  1636. begin
  1637. inherited;
  1638. if FDisableItemStateDeletion = 0 then
  1639. for var I := FStateList.Count-1 downto 0 do begin
  1640. ItemState := FStateList[I];
  1641. FStateList.Delete(I);
  1642. ItemState.Free;
  1643. end;
  1644. end;
  1645. procedure TNewCheckListBox.WMGetDlgCode(var Message: TWMGetDlgCode);
  1646. begin
  1647. inherited;
  1648. if FWantTabs then
  1649. Message.Result := Message.Result and not DLGC_WANTCHARS;
  1650. end;
  1651. procedure TNewCheckListBox.WMKeyDown(var Message: TWMKeyDown);
  1652. var
  1653. GoForward, Arrows: Boolean;
  1654. I: Integer;
  1655. Prnt, Ctrl: TWinControl;
  1656. begin
  1657. { If space is pressed, avoid flickering -- exit now. }
  1658. if not FWantTabs or (Message.CharCode = VK_SPACE) then
  1659. begin
  1660. inherited;
  1661. Exit;
  1662. end;
  1663. Arrows := True;
  1664. case Message.CharCode of
  1665. VK_TAB:
  1666. begin
  1667. GoForward := GetKeyState(VK_SHIFT) >= 0;
  1668. Arrows := False
  1669. end;
  1670. VK_DOWN, VK_RIGHT: GoForward := True;
  1671. VK_UP, VK_LEFT: GoForward := False
  1672. else
  1673. if FSpaceDown then EndCapture(True);
  1674. inherited;
  1675. Exit;
  1676. end;
  1677. EndCapture(not FSpaceDown);
  1678. SendMessage(Handle, WM_CHANGEUISTATE, UIS_CLEAR or (UISF_HIDEFOCUS shl 16), 0);
  1679. if Arrows or TabStop then
  1680. I := FindNextItem(-2, GoForward, not Arrows)
  1681. else
  1682. I := -1;
  1683. if I < 0 then
  1684. begin
  1685. Prnt := nil;
  1686. if not Arrows then
  1687. Prnt := GetParentForm(Self);
  1688. if Prnt = nil then Prnt := Parent;
  1689. if Prnt <> nil then
  1690. begin
  1691. Ctrl := TWinControlAccess(Prnt).FindNextControl(Self, GoForward, True, Arrows);
  1692. if (Ctrl <> nil) and (Ctrl <> Self) then
  1693. begin
  1694. Ctrl.SetFocus;
  1695. Exit;
  1696. end;
  1697. end;
  1698. if GoForward then
  1699. I := FindNextItem(-1, True, not Arrows)
  1700. else
  1701. I := FindNextItem(Items.Count, False, not Arrows);
  1702. end;
  1703. ItemIndex := I;
  1704. if (I <> -1) and (ItemStates[I].ItemType = itRadio) and Arrows then
  1705. Toggle(I);
  1706. end;
  1707. procedure TNewCheckListBox.WMMouseMove(var Message: TWMMouseMove);
  1708. var
  1709. Pos: TPoint;
  1710. Index, NewHotIndex: Integer;
  1711. Rect: TRect;
  1712. Indent: Integer;
  1713. begin
  1714. Pos := SmallPointToPoint(Message.Pos);
  1715. Index := ItemAtPos(Pos, True);
  1716. if FCaptureIndex >= 0 then begin
  1717. if not FSpaceDown and (Index <> FLastMouseMoveIndex) then begin
  1718. if (FLastMouseMoveIndex = FCaptureIndex) or (Index = FCaptureIndex) then
  1719. InvalidateCheck(FCaptureIndex);
  1720. FLastMouseMoveIndex := Index;
  1721. end
  1722. end;
  1723. NewHotIndex := -1;
  1724. if (Index <> -1) and CanFocusItem(Index) then
  1725. begin
  1726. Rect := ItemRect(Index);
  1727. Indent := (FOffset * 2 + FCheckWidth);
  1728. if FWantTabs or ((Pos.X >= Rect.Left + Indent * ItemLevel[Index]) and
  1729. (Pos.X < Rect.Left + Indent * (ItemLevel[Index] + 1))) then
  1730. NewHotIndex := Index;
  1731. end;
  1732. UpdateHotIndex(NewHotIndex);
  1733. end;
  1734. procedure TNewCheckListBox.WMMouseWheel(var Message: TWMMouseWheel);
  1735. begin
  1736. { See TCustomListView.WMVScroll for same code and also see WMVScroll below }
  1737. const Before = GetScrollPos(Handle, SB_VERT);
  1738. inherited;
  1739. const After = GetScrollPos(Handle, SB_VERT);
  1740. if (Before <> After) then
  1741. HandleScroll;
  1742. end;
  1743. procedure TNewCheckListBox.WMVScroll(var Message: TWMVScroll);
  1744. begin
  1745. { Also see WMMouseWheel above }
  1746. const Before = GetScrollPos(Handle, SB_VERT);
  1747. inherited;
  1748. if Message.ScrollCode <> SB_THUMBTRACK then begin
  1749. const After = GetScrollPos(Handle, SB_VERT);
  1750. if (Before <> After) then
  1751. HandleScroll;
  1752. end else
  1753. HandleScroll;
  1754. end;
  1755. procedure TNewCheckListBox.WMNCHitTest(var Message: TWMNCHitTest);
  1756. var
  1757. I: Integer;
  1758. begin
  1759. inherited;
  1760. if FWantTabs and not (csDesigning in ComponentState) then
  1761. begin
  1762. if Message.Result = HTCLIENT then
  1763. begin
  1764. I := ItemAtPos(ScreenToClient(SmallPointToPoint(Message.Pos)), True);
  1765. if (I < 0) or not CanFocusItem(I) then
  1766. begin
  1767. UpdateHotIndex(-1);
  1768. Message.Result := 12345;
  1769. Exit;
  1770. end;
  1771. end;
  1772. end;
  1773. end;
  1774. procedure TNewCheckListBox.WMSetFocus(var Message: TWMSetFocus);
  1775. begin
  1776. FWheelAccum := 0;
  1777. inherited;
  1778. end;
  1779. procedure TNewCheckListBox.WMSize(var Message: TWMSize);
  1780. var
  1781. I: Integer;
  1782. begin
  1783. inherited;
  1784. { When the scroll bar appears/disappears, the client width changes and we
  1785. must recalculate the height of the items }
  1786. for I := Items.Count-1 downto 0 do
  1787. RemeasureItem(I);
  1788. UpdateScrollRange;
  1789. end;
  1790. procedure TNewCheckListBox.WMThemeChanged(var Message: TMessage);
  1791. begin
  1792. { Don't Run to Cursor into this function, it will interrupt up the theme change }
  1793. UpdateThemeData(True, True);
  1794. inherited;
  1795. end;
  1796. procedure TNewCheckListBox.WMUpdateUIState(var Message: TMessage);
  1797. begin
  1798. Invalidate;
  1799. inherited;
  1800. end;
  1801. procedure TNewCheckListBox.WMGetObject(var Message: TMessage);
  1802. begin
  1803. { Per docs, lParam must be casted to DWORD (32 bits) because it may be
  1804. sign-extended in a 64-bit process }
  1805. if (DWORD(Message.LParam) = OBJID_CLIENT) and InitializeOleAcc then begin
  1806. if FAccObjectInstance = nil then begin
  1807. try
  1808. FAccObjectInstance := TAccObject.Create(Self);
  1809. except
  1810. inherited;
  1811. Exit;
  1812. end;
  1813. end;
  1814. Message.Result := LresultFromObjectFunc(IID_IAccessible, Message.WParam,
  1815. TAccObject(FAccObjectInstance));
  1816. end
  1817. else
  1818. inherited;
  1819. end;
  1820. {$IFDEF VCLSTYLES}
  1821. { TNewCheckListBoxStyleHook - same as Vcl.StdCtrls' TListBoxStyleHook except that it picks the
  1822. correct colors when WantTabs is True }
  1823. constructor TNewCheckListBoxStyleHook.Create(AControl: TWinControl);
  1824. begin
  1825. inherited;
  1826. OverrideEraseBkgnd := True;
  1827. UpdateColors;
  1828. end;
  1829. procedure TNewCheckListBoxStyleHook.WMSetFocus(var Message: TMessage);
  1830. begin
  1831. inherited;
  1832. CallDefaultProc(Message);
  1833. RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW);
  1834. Handled := True;
  1835. end;
  1836. procedure TNewCheckListBoxStyleHook.WndProc(var Message: TMessage);
  1837. begin
  1838. if (Message.Msg = WM_ERASEBKGND) and (Control.StyleName <> '') then begin
  1839. const WantTabs = (Control is TNewCheckListBox) and TNewCheckListBox(Control).WantTabs;
  1840. if not FStyleColorsChecked or (FStyleColorsCheckedWantTabs <> WantTabs) then begin
  1841. FStyleColorsChecked := True;
  1842. FStyleColorsCheckedWantTabs := WantTabs;
  1843. UpdateColors;
  1844. end;
  1845. end;
  1846. case Message.Msg of
  1847. CN_CTLCOLORMSGBOX..CN_CTLCOLORSTATIC:
  1848. begin
  1849. UpdateColors;
  1850. SetTextColor(Message.WParam, UColorToRGB(FontColor));
  1851. const Transparent = (Control is TNewCheckListBox) and TNewCheckListBox(Control).TransparentIfStyled;
  1852. if Transparent then begin
  1853. SetBkMode(Message.WParam, Windows.TRANSPARENT);
  1854. Message.Result := LRESULT(GetStockObject(NULL_BRUSH));
  1855. end else begin
  1856. SetBkColor(Message.WParam, UColorToRGB(Brush.Color));
  1857. Message.Result := LRESULT(Brush.Handle);
  1858. end;
  1859. Handled := True;
  1860. end;
  1861. CM_ENABLEDCHANGED:
  1862. begin
  1863. UpdateColors;
  1864. Handled := False; // Allow control to handle message
  1865. end
  1866. else
  1867. inherited WndProc(Message);
  1868. end;
  1869. end;
  1870. procedure TNewCheckListBoxStyleHook.PaintBackground(Canvas: TCanvas);
  1871. begin
  1872. const Transparent = (Control is TNewCheckListBox) and TNewCheckListBox(Control).TransparentIfStyled;
  1873. if Transparent then
  1874. StyleServices.DrawParentBackground(Handle, Canvas.Handle, nil, False)
  1875. else
  1876. inherited;
  1877. end;
  1878. procedure TNewCheckListBoxStyleHook.UpdateColors;
  1879. begin
  1880. const WantTabs = (Control is TNewCheckListBox) and TNewCheckListBox(Control).WantTabs;
  1881. const LStyle = StyleServices;
  1882. { Also see color initialization in TNewCheckListBox.DrawItem }
  1883. if WantTabs then
  1884. Brush.Color := LStyle.GetStyleColor(scWindow)
  1885. else
  1886. Brush.Color := LStyle.GetStyleColor(ColorStates[Control.Enabled]);
  1887. if seFont in Control.StyleElements then begin
  1888. if WantTabs then
  1889. FontColor := LStyle.GetStyleFontColor(TextLabelFontColorStates[Control.Enabled])
  1890. else
  1891. FontColor := LStyle.GetStyleFontColor(ListItemFontColorStates[Control.Enabled])
  1892. end else
  1893. FontColor := TWinControlAccess(Control).Font.Color;
  1894. end;
  1895. procedure TNewCheckListBoxStyleHook.WMKillFocus(var Message: TMessage);
  1896. begin
  1897. inherited;
  1898. CallDefaultProc(Message);
  1899. RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW);
  1900. Handled := True;
  1901. end;
  1902. {$ENDIF}
  1903. { TAccObject }
  1904. constructor TAccObject.Create(AControl: TNewCheckListBox);
  1905. begin
  1906. inherited Create;
  1907. if CreateStdAccessibleObjectFunc(AControl.Handle, Integer(OBJID_CLIENT),
  1908. IID_IAccessible, Pointer(FStdAcc)) <> S_OK then begin
  1909. { Note: The user will never actually see this message since the call to
  1910. TAccObject.Create in TNewCheckListBox.WMGetObject is protected by a
  1911. try..except. }
  1912. raise Exception.Create('CreateStdAccessibleObject failed');
  1913. end;
  1914. FControl := AControl;
  1915. end;
  1916. destructor TAccObject.Destroy;
  1917. begin
  1918. { If FControl is assigned, then we are being destroyed before the control --
  1919. the usual case. Clear FControl's reference to us. }
  1920. if Assigned(FControl) then begin
  1921. FControl.FAccObjectInstance := nil;
  1922. FControl := nil;
  1923. end;
  1924. if Assigned(FStdAcc) then
  1925. FStdAcc.Release;
  1926. inherited;
  1927. end;
  1928. procedure TAccObject.ControlDestroying;
  1929. begin
  1930. { Set FControl to nil, since it's no longer valid }
  1931. FControl := nil;
  1932. { Take this opportunity to disconnect remote clients, i.e. don't allow them
  1933. to call us anymore. This prevents invalid memory accesses if this unit's
  1934. code is in a DLL, and the application subsequently unloads the DLL while
  1935. remote clients still hold (and are using) references to this TAccObject. }
  1936. CoDisconnectObject(Self, 0);
  1937. { NOTE: Don't access Self in any way at this point. The CoDisconnectObject
  1938. call likely caused all references to be relinquished and Self to be
  1939. destroyed. }
  1940. end;
  1941. function TAccObject.QueryInterface(const iid: TIID; var obj): HRESULT;
  1942. begin
  1943. if IsEqualIID(iid, IID_IUnknown) or
  1944. IsEqualIID(iid, IID_IDispatch) or
  1945. IsEqualIID(iid, IID_IAccessible) then begin
  1946. Pointer(obj) := Self;
  1947. AddRef;
  1948. Result := S_OK;
  1949. end
  1950. else begin
  1951. Pointer(obj) := nil;
  1952. Result := E_NOINTERFACE;
  1953. end;
  1954. end;
  1955. function TAccObject.AddRef: Longint;
  1956. begin
  1957. Inc(FRefCount);
  1958. Result := FRefCount;
  1959. end;
  1960. function TAccObject.Release: Longint;
  1961. begin
  1962. Dec(FRefCount);
  1963. Result := FRefCount;
  1964. if Result = 0 then
  1965. Destroy;
  1966. end;
  1967. function TAccObject.GetTypeInfoCount(var ctinfo: Integer): HRESULT;
  1968. begin
  1969. Result := E_NOTIMPL;
  1970. end;
  1971. function TAccObject.GetTypeInfo(itinfo: Integer; lcid: TLCID; var tinfo: ITypeInfo): HRESULT;
  1972. begin
  1973. Result := E_NOTIMPL;
  1974. end;
  1975. function TAccObject.GetIDsOfNames(const iid: TIID; rgszNames: POleStrList;
  1976. cNames: Integer; lcid: TLCID; rgdispid: PDispIDList): HRESULT;
  1977. begin
  1978. Result := E_NOTIMPL;
  1979. end;
  1980. function TAccObject.Invoke(dispIDMember: TDispID; const iid: TIID; lcid: TLCID;
  1981. flags: Word; var dispParams: TDispParams; varResult: PVariant;
  1982. excepInfo: PExcepInfo; argErr: PInteger): HRESULT;
  1983. begin
  1984. Result := E_NOTIMPL;
  1985. end;
  1986. function TAccObject.accDoDefaultAction(varChild: NewOleVariant): HRESULT;
  1987. begin
  1988. { A list box's default action is Double Click, which is useless for a
  1989. list of check boxes. }
  1990. Result := DISP_E_MEMBERNOTFOUND;
  1991. end;
  1992. function TAccObject.accHitTest(xLeft, yTop: Integer;
  1993. var pvarID: NewOleVariant): HRESULT;
  1994. begin
  1995. Result := FStdAcc.accHitTest(xLeft, yTop, pvarID);
  1996. end;
  1997. function TAccObject.accLocation(var pxLeft, pyTop, pcxWidth,
  1998. pcyHeight: Integer; varChild: NewOleVariant): HRESULT;
  1999. begin
  2000. Result := FStdAcc.accLocation(pxLeft, pyTop, pcxWidth, pcyHeight, varChild);
  2001. end;
  2002. function TAccObject.accNavigate(navDir: Integer; varStart: NewOleVariant;
  2003. var pvarEnd: NewOleVariant): HRESULT;
  2004. begin
  2005. Result := FStdAcc.accNavigate(navDir, varStart, pvarEnd);
  2006. end;
  2007. function TAccObject.accSelect(flagsSelect: Integer;
  2008. varChild: NewOleVariant): HRESULT;
  2009. begin
  2010. Result := FStdAcc.accSelect(flagsSelect, varChild);
  2011. end;
  2012. function TAccObject.get_accChild(varChild: NewOleVariant;
  2013. var ppdispChild: IDispatch): HRESULT;
  2014. begin
  2015. Result := FStdAcc.get_accChild(varChild, ppdispChild);
  2016. end;
  2017. function TAccObject.get_accChildCount(var pcountChildren: Integer): HRESULT;
  2018. begin
  2019. Result := FStdAcc.get_accChildCount(pcountChildren);
  2020. end;
  2021. function TAccObject.get_accDefaultAction(varChild: NewOleVariant;
  2022. var pszDefaultAction: NewWideString): HRESULT;
  2023. begin
  2024. { A list box's default action is Double Click, which is useless for a
  2025. list of check boxes. }
  2026. pszDefaultAction := nil;
  2027. Result := S_FALSE;
  2028. end;
  2029. function TAccObject.get_accDescription(varChild: NewOleVariant;
  2030. var pszDescription: NewWideString): HRESULT;
  2031. begin
  2032. Result := FStdAcc.get_accDescription(varChild, pszDescription);
  2033. end;
  2034. function TAccObject.get_accFocus(var pvarID: NewOleVariant): HRESULT;
  2035. begin
  2036. Result := FStdAcc.get_accFocus(pvarID);
  2037. end;
  2038. function TAccObject.get_accHelp(varChild: NewOleVariant;
  2039. var pszHelp: NewWideString): HRESULT;
  2040. begin
  2041. Result := FStdAcc.get_accHelp(varChild, pszHelp);
  2042. end;
  2043. function TAccObject.get_accHelpTopic(var pszHelpFile: NewWideString;
  2044. varChild: NewOleVariant; var pidTopic: Integer): HRESULT;
  2045. begin
  2046. Result := FStdAcc.get_accHelpTopic(pszHelpFile, varChild, pidTopic);
  2047. end;
  2048. function TAccObject.get_accKeyboardShortcut(varChild: NewOleVariant;
  2049. var pszKeyboardShortcut: NewWideString): HRESULT;
  2050. begin
  2051. Result := FStdAcc.get_accKeyboardShortcut(varChild, pszKeyboardShortcut);
  2052. end;
  2053. function TAccObject.get_accName(varChild: NewOleVariant;
  2054. var pszName: NewWideString): HRESULT;
  2055. begin
  2056. Result := FStdAcc.get_accName(varChild, pszName);
  2057. end;
  2058. function TAccObject.get_accParent(var ppdispParent: IDispatch): HRESULT;
  2059. begin
  2060. Result := FStdAcc.get_accParent(ppdispParent);
  2061. end;
  2062. function TAccObject.get_accRole(varChild: NewOleVariant;
  2063. var pvarRole: NewOleVariant): HRESULT;
  2064. begin
  2065. pvarRole.vt := VT_EMPTY;
  2066. if FControl = nil then begin
  2067. Result := E_FAIL;
  2068. Exit;
  2069. end;
  2070. if varChild.vt <> VT_I4 then begin
  2071. Result := E_INVALIDARG;
  2072. Exit;
  2073. end;
  2074. if varChild.lVal = CHILDID_SELF then begin
  2075. pvarRole.lVal := ROLE_SYSTEM_OUTLINE;
  2076. pvarRole.vt := VT_I4;
  2077. Result := S_OK;
  2078. end
  2079. else begin
  2080. try
  2081. case FControl.ItemStates[varChild.lVal-1].ItemType of
  2082. itCheck: pvarRole.lVal := ROLE_SYSTEM_CHECKBUTTON;
  2083. itRadio: pvarRole.lVal := ROLE_SYSTEM_RADIOBUTTON;
  2084. else
  2085. pvarRole.lVal := ROLE_SYSTEM_STATICTEXT;
  2086. end;
  2087. pvarRole.vt := VT_I4;
  2088. Result := S_OK;
  2089. except
  2090. Result := E_INVALIDARG;
  2091. end;
  2092. end;
  2093. end;
  2094. function TAccObject.get_accSelection(var pvarChildren: NewOleVariant): HRESULT;
  2095. begin
  2096. Result := FStdAcc.get_accSelection(pvarChildren);
  2097. end;
  2098. function TAccObject.get_accState(varChild: NewOleVariant;
  2099. var pvarState: NewOleVariant): HRESULT;
  2100. var
  2101. ItemState: TItemState;
  2102. begin
  2103. Result := FStdAcc.get_accState(varChild, pvarState);
  2104. try
  2105. if (Result = S_OK) and (varChild.vt = VT_I4) and
  2106. (varChild.lVal <> CHILDID_SELF) and (pvarState.vt = VT_I4) and
  2107. Assigned(FControl) then begin
  2108. ItemState := FControl.ItemStates[varChild.lVal-1];
  2109. case ItemState.State of
  2110. cbChecked: pvarState.lVal := pvarState.lVal or STATE_SYSTEM_CHECKED;
  2111. cbGrayed: pvarState.lVal := pvarState.lVal or STATE_SYSTEM_MIXED;
  2112. end;
  2113. if not ItemState.Enabled then
  2114. pvarState.lVal := pvarState.lVal or STATE_SYSTEM_UNAVAILABLE;
  2115. end;
  2116. except
  2117. Result := E_INVALIDARG;
  2118. end;
  2119. end;
  2120. function TAccObject.get_accValue(varChild: NewOleVariant;
  2121. var pszValue: NewWideString): HRESULT;
  2122. begin
  2123. pszValue := nil;
  2124. if FControl = nil then begin
  2125. Result := E_FAIL;
  2126. Exit;
  2127. end;
  2128. if varChild.vt <> VT_I4 then begin
  2129. Result := E_INVALIDARG;
  2130. Exit;
  2131. end;
  2132. if varChild.lVal = CHILDID_SELF then
  2133. Result := S_FALSE
  2134. else begin
  2135. { Return the level as the value, like standard tree view controls do.
  2136. Not sure if any screen readers will actually use this, seeing as we
  2137. aren't a real tree view control. }
  2138. try
  2139. pszValue := StringToOleStr(IntToStr(FControl.ItemStates[varChild.lVal-1].Level));
  2140. Result := S_OK;
  2141. except
  2142. Result := E_INVALIDARG;
  2143. end;
  2144. end;
  2145. end;
  2146. function TAccObject.put_accName(varChild: NewOleVariant;
  2147. const pszName: NewWideString): HRESULT;
  2148. begin
  2149. Result := S_FALSE;
  2150. end;
  2151. function TAccObject.put_accValue(varChild: NewOleVariant;
  2152. const pszValue: NewWideString): HRESULT;
  2153. begin
  2154. Result := S_FALSE;
  2155. end;
  2156. procedure Register;
  2157. begin
  2158. RegisterComponents('JR', [TNewCheckListBox]);
  2159. end;
  2160. { Note: This COM initialization code based on code from DBTables }
  2161. var
  2162. SaveInitProc: Pointer;
  2163. NeedToUninitialize: Boolean;
  2164. procedure InitCOM;
  2165. begin
  2166. if SaveInitProc <> nil then TProcedure(SaveInitProc);
  2167. NeedToUninitialize := SUCCEEDED(CoInitialize(nil));
  2168. end;
  2169. initialization
  2170. if not IsLibrary then begin
  2171. SaveInitProc := InitProc;
  2172. InitProc := @InitCOM;
  2173. end;
  2174. InitThemeLibrary;
  2175. NotifyWinEventFunc := GetProcAddress(GetModuleHandle(user32), 'NotifyWinEvent');
  2176. finalization
  2177. if NeedToUninitialize then
  2178. CoUninitialize;
  2179. end.