2
0

NewCheckListBox.pas 77 KB

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