1
0

TB2Acc.pas 36 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177
  1. unit TB2Acc;
  2. {
  3. Toolbar2000
  4. Copyright (C) 1998-2005 by Jordan Russell
  5. All rights reserved.
  6. The contents of this file are subject to the "Toolbar2000 License"; you may
  7. not use or distribute this file except in compliance with the
  8. "Toolbar2000 License". A copy of the "Toolbar2000 License" may be found in
  9. TB2k-LICENSE.txt or at:
  10. https://jrsoftware.org/files/tb2k/TB2k-LICENSE.txt
  11. Alternatively, the contents of this file may be used under the terms of the
  12. GNU General Public License (the "GPL"), in which case the provisions of the
  13. GPL are applicable instead of those in the "Toolbar2000 License". A copy of
  14. the GPL may be found in GPL-LICENSE.txt or at:
  15. https://jrsoftware.org/files/tb2k/GPL-LICENSE.txt
  16. If you wish to allow use of your version of this file only under the terms of
  17. the GPL and not to allow others to use your version of this file under the
  18. "Toolbar2000 License", indicate your decision by deleting the provisions
  19. above and replace them with the notice and other provisions required by the
  20. GPL. If you do not delete the provisions above, a recipient may use your
  21. version of this file under either the "Toolbar2000 License" or the GPL.
  22. $jrsoftware: tb2k/Source/TB2Acc.pas,v 1.7 2005/01/06 03:56:50 jr Exp $
  23. This unit is used internally to implement the IAccessible interface on
  24. TTBView and TTBItemViewer for Microsoft Active Accessibility support.
  25. }
  26. interface
  27. {$I TB2Ver.inc}
  28. uses
  29. Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  30. TB2Item;
  31. type
  32. { Our declaration for IAccessible }
  33. ITBAccessible = interface(IDispatch)
  34. ['{618736E0-3C3D-11CF-810C-00AA00389B71}']
  35. function get_accParent(out ppdispParent: IDispatch): HRESULT; stdcall;
  36. function get_accChildCount(out pcountChildren: Integer): HRESULT; stdcall;
  37. function get_accChild(varChild: OleVariant; out ppdispChild: IDispatch): HRESULT; stdcall;
  38. function get_accName(varChild: OleVariant; out pszName: WideString): HRESULT; stdcall;
  39. function get_accValue(varChild: OleVariant; out pszValue: WideString): HRESULT; stdcall;
  40. function get_accDescription(varChild: OleVariant; out pszDescription: WideString): HRESULT; stdcall;
  41. function get_accRole(varChild: OleVariant; out pvarRole: OleVariant): HRESULT; stdcall;
  42. function get_accState(varChild: OleVariant; out pvarState: OleVariant): HRESULT; stdcall;
  43. function get_accHelp(varChild: OleVariant; out pszHelp: WideString): HRESULT; stdcall;
  44. function get_accHelpTopic(out pszHelpFile: WideString; varChild: OleVariant; out pidTopic: Integer): HRESULT; stdcall;
  45. function get_accKeyboardShortcut(varChild: OleVariant; out pszKeyboardShortcut: WideString): HRESULT; stdcall;
  46. function get_accFocus(out pvarID: OleVariant): HRESULT; stdcall;
  47. function get_accSelection(out pvarChildren: OleVariant): HRESULT; stdcall;
  48. function get_accDefaultAction(varChild: OleVariant; out pszDefaultAction: WideString): HRESULT; stdcall;
  49. function accSelect(flagsSelect: Integer; varChild: OleVariant): HRESULT; stdcall;
  50. function accLocation(out pxLeft: Integer; out pyTop: Integer; out pcxWidth: Integer;
  51. out pcyHeight: Integer; varChild: OleVariant): HRESULT; stdcall;
  52. function accNavigate(navDir: Integer; varStart: OleVariant; out pvarEnd: OleVariant): HRESULT; stdcall;
  53. function accHitTest(xLeft: Integer; yTop: Integer; out pvarID: OleVariant): HRESULT; stdcall;
  54. function accDoDefaultAction(varChild: OleVariant): HRESULT; stdcall;
  55. function put_accName(varChild: OleVariant; const pszName: WideString): HRESULT; stdcall;
  56. function put_accValue(varChild: OleVariant; const pszValue: WideString): HRESULT; stdcall;
  57. end;
  58. TTBCustomAccObject = class(TTBBaseAccObject, IUnknown, IDispatch)
  59. private
  60. FPrevious, FNext: TTBCustomAccObject;
  61. public
  62. constructor Create;
  63. destructor Destroy; override;
  64. end;
  65. TTBViewAccObject = class(TTBCustomAccObject, IUnknown, IDispatch, ITBAccessible)
  66. private
  67. FView: TTBView;
  68. function Check(const varChild: OleVariant; var ErrorCode: HRESULT): Boolean;
  69. { ITBAccessible }
  70. function accDoDefaultAction(varChild: OleVariant): HRESULT; stdcall;
  71. function accHitTest(xLeft: Integer; yTop: Integer; out pvarID: OleVariant): HRESULT; stdcall;
  72. function accLocation(out pxLeft: Integer; out pyTop: Integer; out pcxWidth: Integer;
  73. out pcyHeight: Integer; varChild: OleVariant): HRESULT; stdcall;
  74. function accNavigate(navDir: Integer; varStart: OleVariant; out pvarEnd: OleVariant): HRESULT; stdcall;
  75. function accSelect(flagsSelect: Integer; varChild: OleVariant): HRESULT; stdcall;
  76. function get_accChild(varChild: OleVariant; out ppdispChild: IDispatch): HRESULT; stdcall;
  77. function get_accChildCount(out pcountChildren: Integer): HRESULT; stdcall;
  78. function get_accDefaultAction(varChild: OleVariant; out pszDefaultAction: WideString): HRESULT; stdcall;
  79. function get_accDescription(varChild: OleVariant; out pszDescription: WideString): HRESULT; stdcall;
  80. function get_accFocus(out pvarID: OleVariant): HRESULT; stdcall;
  81. function get_accHelp(varChild: OleVariant; out pszHelp: WideString): HRESULT; stdcall;
  82. function get_accHelpTopic(out pszHelpFile: WideString; varChild: OleVariant; out pidTopic: Integer): HRESULT; stdcall;
  83. function get_accKeyboardShortcut(varChild: OleVariant; out pszKeyboardShortcut: WideString): HRESULT; stdcall;
  84. function get_accName(varChild: OleVariant; out pszName: WideString): HRESULT; stdcall;
  85. function get_accParent(out ppdispParent: IDispatch): HRESULT; stdcall;
  86. function get_accRole(varChild: OleVariant; out pvarRole: OleVariant): HRESULT; stdcall;
  87. function get_accSelection(out pvarChildren: OleVariant): HRESULT; stdcall;
  88. function get_accState(varChild: OleVariant; out pvarState: OleVariant): HRESULT; stdcall;
  89. function get_accValue(varChild: OleVariant; out pszValue: WideString): HRESULT; stdcall;
  90. function put_accName(varChild: OleVariant; const pszName: WideString): HRESULT; stdcall;
  91. function put_accValue(varChild: OleVariant; const pszValue: WideString): HRESULT; stdcall;
  92. public
  93. constructor Create(AView: TTBView);
  94. destructor Destroy; override;
  95. procedure ClientIsDestroying; override;
  96. end;
  97. TTBItemViewerAccObject = class(TTBCustomAccObject, IUnknown, IDispatch, ITBAccessible)
  98. private
  99. FViewer: TTBItemViewer;
  100. function Check(const varChild: OleVariant; var ErrorCode: HRESULT): Boolean;
  101. function IsActionable: Boolean;
  102. function IsAvailable: Boolean;
  103. function IsFocusable: Boolean;
  104. { ITBAccessible }
  105. function accDoDefaultAction(varChild: OleVariant): HRESULT; stdcall;
  106. function accHitTest(xLeft: Integer; yTop: Integer; out pvarID: OleVariant): HRESULT; stdcall;
  107. function accLocation(out pxLeft: Integer; out pyTop: Integer; out pcxWidth: Integer;
  108. out pcyHeight: Integer; varChild: OleVariant): HRESULT; stdcall;
  109. function accNavigate(navDir: Integer; varStart: OleVariant; out pvarEnd: OleVariant): HRESULT; stdcall;
  110. function accSelect(flagsSelect: Integer; varChild: OleVariant): HRESULT; stdcall;
  111. function get_accChild(varChild: OleVariant; out ppdispChild: IDispatch): HRESULT; stdcall;
  112. function get_accChildCount(out pcountChildren: Integer): HRESULT; stdcall;
  113. function get_accDefaultAction(varChild: OleVariant; out pszDefaultAction: WideString): HRESULT; stdcall;
  114. function get_accDescription(varChild: OleVariant; out pszDescription: WideString): HRESULT; stdcall;
  115. function get_accFocus(out pvarID: OleVariant): HRESULT; stdcall;
  116. function get_accHelp(varChild: OleVariant; out pszHelp: WideString): HRESULT; stdcall;
  117. function get_accHelpTopic(out pszHelpFile: WideString; varChild: OleVariant; out pidTopic: Integer): HRESULT; stdcall;
  118. function get_accKeyboardShortcut(varChild: OleVariant; out pszKeyboardShortcut: WideString): HRESULT; stdcall;
  119. function get_accName(varChild: OleVariant; out pszName: WideString): HRESULT; stdcall;
  120. function get_accParent(out ppdispParent: IDispatch): HRESULT; stdcall;
  121. function get_accRole(varChild: OleVariant; out pvarRole: OleVariant): HRESULT; stdcall;
  122. function get_accSelection(out pvarChildren: OleVariant): HRESULT; stdcall;
  123. function get_accState(varChild: OleVariant; out pvarState: OleVariant): HRESULT; stdcall;
  124. function get_accValue(varChild: OleVariant; out pszValue: WideString): HRESULT; stdcall;
  125. function put_accName(varChild: OleVariant; const pszName: WideString): HRESULT; stdcall;
  126. function put_accValue(varChild: OleVariant; const pszValue: WideString): HRESULT; stdcall;
  127. public
  128. constructor Create(AViewer: TTBItemViewer);
  129. destructor Destroy; override;
  130. procedure ClientIsDestroying; override;
  131. procedure HandleAccSelect(const AExecute: Boolean);
  132. end;
  133. var
  134. { For debugging purposes only: }
  135. ViewAccObjectInstances: Integer = 0;
  136. ItemViewerAccObjectInstances: Integer = 0;
  137. implementation
  138. uses
  139. Variants, ActiveX, Menus, TB2Common, Winapi.oleacc, Types;
  140. const
  141. { Constants from OleAcc.h }
  142. ROLE_SYSTEM_MENUBAR = $2;
  143. ROLE_SYSTEM_CLIENT = $a;
  144. ROLE_SYSTEM_MENUPOPUP = $b;
  145. ROLE_SYSTEM_MENUITEM = $c;
  146. ROLE_SYSTEM_SEPARATOR = $15;
  147. ROLE_SYSTEM_TOOLBAR = $16;
  148. ROLE_SYSTEM_PUSHBUTTON = $2b;
  149. ROLE_SYSTEM_BUTTONMENU = $39;
  150. STATE_SYSTEM_HASPOPUP = $40000000;
  151. NAVDIR_UP = 1;
  152. NAVDIR_DOWN = 2;
  153. NAVDIR_LEFT = 3;
  154. NAVDIR_RIGHT = 4;
  155. NAVDIR_NEXT = 5;
  156. NAVDIR_PREVIOUS = 6;
  157. NAVDIR_FIRSTCHILD = 7;
  158. NAVDIR_LASTCHILD = 8;
  159. SELFLAG_TAKEFOCUS = 1;
  160. type
  161. TControlAccess = class(TControl);
  162. TTBViewAccess = class(TTBView);
  163. TTBCustomItemAccess = class(TTBCustomItem);
  164. TTBItemViewerAccess = class(TTBItemViewer);
  165. var
  166. LastAccObject: TTBCustomAccObject; { last object in the linked list }
  167. LastAccObjectCritSect: TRTLCriticalSection;
  168. function AccObjectFromWindow(const Wnd: HWND; out ADisp: IDispatch): Boolean;
  169. var
  170. P: Pointer;
  171. begin
  172. if AccessibleObjectFromWindow(Wnd, OBJID_WINDOW, IDispatch, P) = S_OK then begin
  173. ADisp := IDispatch(P);
  174. IDispatch(P)._Release;
  175. Result := True;
  176. end
  177. else
  178. Result := False;
  179. end;
  180. procedure DisconnectAccObjects;
  181. { This procedure calls CoDisconnectObject() on all acc. objects still
  182. allocated. This is needed to prevent potential AV's when TB2k is compiled
  183. into a DLL, since a DLL may be freed by the application while an MSAA
  184. client still holds acc. object references. }
  185. var
  186. Obj, PrevObj: TTBCustomAccObject;
  187. begin
  188. Obj := LastAccObject;
  189. while Assigned(Obj) do begin
  190. { Make a copy of Obj.FPrevious since CoDisconnectObject may cause Obj
  191. to be freed }
  192. PrevObj := Obj.FPrevious;
  193. { CoDisconnectObject should cause remote MSAA clients to release all
  194. references to the object, thus destroying it (assuming the local
  195. application doesn't have references of its own). }
  196. CoDisconnectObject(Obj, 0);
  197. Obj := PrevObj;
  198. end;
  199. end;
  200. function GetAltKeyName: String;
  201. { This silly function is needed since ShortCutToText(VK_MENU) fails on Delphi
  202. and C++Builder versions <= 4 }
  203. var
  204. ScanCode: UINT;
  205. KeyName: array[0..255] of Char;
  206. begin
  207. ScanCode := MapVirtualKey(VK_MENU, 0) shl 16;
  208. if (ScanCode <> 0) and
  209. (GetKeyNameText(ScanCode, KeyName, SizeOf(KeyName)) > 0) then
  210. Result := KeyName
  211. else
  212. Result := 'Alt'; { shouldn't get here, but just in case... }
  213. end;
  214. { TTBCustomAccObject }
  215. constructor TTBCustomAccObject.Create;
  216. begin
  217. inherited Create;
  218. { Add Self to linked list of objects }
  219. EnterCriticalSection(LastAccObjectCritSect);
  220. try
  221. FPrevious := LastAccObject;
  222. if Assigned(FPrevious) then
  223. FPrevious.FNext := Self;
  224. LastAccObject := Self;
  225. finally
  226. LeaveCriticalSection(LastAccObjectCritSect);
  227. end;
  228. end;
  229. destructor TTBCustomAccObject.Destroy;
  230. begin
  231. { Remove Self from linked list of objects }
  232. EnterCriticalSection(LastAccObjectCritSect);
  233. try
  234. if LastAccObject = Self then
  235. LastAccObject := FPrevious;
  236. if Assigned(FPrevious) then
  237. FPrevious.FNext := FNext;
  238. if Assigned(FNext) then
  239. FNext.FPrevious := FPrevious;
  240. finally
  241. LeaveCriticalSection(LastAccObjectCritSect);
  242. end;
  243. inherited;
  244. end;
  245. { TTBViewAccObject }
  246. constructor TTBViewAccObject.Create(AView: TTBView);
  247. begin
  248. inherited Create;
  249. FView := AView;
  250. InterlockedIncrement(ViewAccObjectInstances);
  251. end;
  252. destructor TTBViewAccObject.Destroy;
  253. begin
  254. InterlockedDecrement(ViewAccObjectInstances);
  255. if Assigned(FView) then begin
  256. TTBViewAccess(FView).FAccObjectInstance := nil;
  257. FView := nil;
  258. end;
  259. inherited;
  260. end;
  261. procedure TTBViewAccObject.ClientIsDestroying;
  262. begin
  263. FView := nil;
  264. end;
  265. function TTBViewAccObject.Check(const varChild: OleVariant;
  266. var ErrorCode: HRESULT): Boolean;
  267. begin
  268. if FView = nil then begin
  269. ErrorCode := E_FAIL;
  270. Result := False;
  271. end
  272. else if (VarType(varChild) <> varInteger) or (varChild <> CHILDID_SELF) then begin
  273. ErrorCode := E_INVALIDARG;
  274. Result := False;
  275. end
  276. else
  277. Result := True;
  278. end;
  279. function TTBViewAccObject.accDoDefaultAction(varChild: OleVariant): HRESULT;
  280. begin
  281. Result := S_FALSE;
  282. end;
  283. function TTBViewAccObject.accHitTest(xLeft, yTop: Integer;
  284. out pvarID: OleVariant): HRESULT;
  285. var
  286. ViewWnd, W: HWND;
  287. R: TRect;
  288. P: TPoint;
  289. D: IDispatch;
  290. V: TTBItemViewer;
  291. begin
  292. try
  293. if FView = nil then begin
  294. Result := E_FAIL;
  295. Exit;
  296. end;
  297. ViewWnd := FView.Window.Handle;
  298. GetWindowRect(ViewWnd, R);
  299. P.X := xLeft;
  300. P.Y := yTop;
  301. if PtInRect(R, P) then begin
  302. P := FView.Window.ScreenToClient(P);
  303. W := ChildWindowFromPointEx(ViewWnd, P, CWP_SKIPINVISIBLE);
  304. if (W <> 0) and (W <> ViewWnd) then begin
  305. { Point is inside a child window (most likely belonging to a
  306. TTBControlItem) }
  307. if AccObjectFromWindow(W, D) then begin
  308. pvarID := D;
  309. Result := S_OK;
  310. end
  311. else
  312. Result := E_UNEXPECTED;
  313. end
  314. else begin
  315. V := FView.ViewerFromPoint(P);
  316. if Assigned(V) then
  317. pvarID := V.GetAccObject
  318. else
  319. pvarID := CHILDID_SELF;
  320. Result := S_OK;
  321. end;
  322. end
  323. else
  324. Result := S_FALSE;
  325. except
  326. Result := E_UNEXPECTED;
  327. end;
  328. end;
  329. function TTBViewAccObject.accLocation(out pxLeft, pyTop, pcxWidth,
  330. pcyHeight: Integer; varChild: OleVariant): HRESULT;
  331. var
  332. R: TRect;
  333. begin
  334. try
  335. if not Check(varChild, Result) then
  336. Exit;
  337. GetWindowRect(FView.Window.Handle, R);
  338. pxLeft := R.Left;
  339. pyTop := R.Top;
  340. pcxWidth := R.Right - R.Left;
  341. pcyHeight := R.Bottom - R.Top;
  342. Result := S_OK;
  343. except
  344. Result := E_UNEXPECTED;
  345. end;
  346. end;
  347. function TTBViewAccObject.accNavigate(navDir: Integer; varStart: OleVariant;
  348. out pvarEnd: OleVariant): HRESULT;
  349. var
  350. I: Integer;
  351. begin
  352. try
  353. if not Check(varStart, Result) then
  354. Exit;
  355. Result := S_FALSE;
  356. case navDir of
  357. NAVDIR_FIRSTCHILD: begin
  358. for I := 0 to FView.ViewerCount-1 do
  359. if FView.Viewers[I].IsAccessible then begin
  360. pvarEnd := FView.Viewers[I].GetAccObject;
  361. Result := S_OK;
  362. Break;
  363. end;
  364. end;
  365. NAVDIR_LASTCHILD: begin
  366. for I := FView.ViewerCount-1 downto 0 do
  367. if FView.Viewers[I].IsAccessible then begin
  368. pvarEnd := FView.Viewers[I].GetAccObject;
  369. Result := S_OK;
  370. Break;
  371. end;
  372. end;
  373. end;
  374. except
  375. Result := E_UNEXPECTED;
  376. end;
  377. end;
  378. function TTBViewAccObject.accSelect(flagsSelect: Integer;
  379. varChild: OleVariant): HRESULT;
  380. begin
  381. Result := DISP_E_MEMBERNOTFOUND;
  382. end;
  383. function TTBViewAccObject.get_accChild(varChild: OleVariant;
  384. out ppdispChild: IDispatch): HRESULT;
  385. var
  386. I, J: Integer;
  387. Viewer: TTBItemViewer;
  388. Ctl: TControl;
  389. begin
  390. try
  391. if FView = nil then begin
  392. Result := E_FAIL;
  393. Exit;
  394. end;
  395. if VarType(varChild) <> varInteger then begin
  396. Result := E_INVALIDARG;
  397. Exit;
  398. end;
  399. I := varChild;
  400. if I = CHILDID_SELF then begin
  401. ppdispChild := Self;
  402. Result := S_OK;
  403. end
  404. else begin
  405. { Convert a one-based child index (I) into a real viewer index (J) }
  406. J := 0;
  407. while J < FView.ViewerCount do begin
  408. if FView.Viewers[J].IsAccessible then begin
  409. if I = 1 then Break;
  410. Dec(I);
  411. end;
  412. Inc(J);
  413. end;
  414. if J >= FView.ViewerCount then begin
  415. { 'I' was either negative or too high }
  416. Result := E_INVALIDARG;
  417. Exit;
  418. end;
  419. Viewer := FView.Viewers[J];
  420. if Viewer.Item is TTBControlItem then begin
  421. { For windowed controls, return the window's accessible object instead
  422. of the item viewer's }
  423. Ctl := TTBControlItem(Viewer.Item).Control;
  424. if (Ctl is TWinControl) and TWinControl(Ctl).HandleAllocated then begin
  425. if AccObjectFromWindow(TWinControl(Ctl).Handle, ppdispChild) then
  426. Result := S_OK
  427. else
  428. Result := E_UNEXPECTED;
  429. Exit;
  430. end;
  431. end;
  432. ppdispChild := Viewer.GetAccObject;
  433. Result := S_OK;
  434. end;
  435. except
  436. Result := E_UNEXPECTED;
  437. end;
  438. end;
  439. function TTBViewAccObject.get_accChildCount(out pcountChildren: Integer): HRESULT;
  440. var
  441. Count, I: Integer;
  442. begin
  443. try
  444. if Assigned(FView) then begin
  445. Count := 0;
  446. for I := 0 to FView.ViewerCount-1 do
  447. if FView.Viewers[I].IsAccessible then
  448. Inc(Count);
  449. pCountChildren := Count;
  450. Result := S_OK;
  451. end
  452. else
  453. Result := E_FAIL;
  454. except
  455. Result := E_UNEXPECTED;
  456. end;
  457. end;
  458. function TTBViewAccObject.get_accDefaultAction(varChild: OleVariant;
  459. out pszDefaultAction: WideString): HRESULT;
  460. begin
  461. Result := S_FALSE;
  462. end;
  463. function TTBViewAccObject.get_accDescription(varChild: OleVariant;
  464. out pszDescription: WideString): HRESULT;
  465. begin
  466. Result := S_FALSE;
  467. end;
  468. function TTBViewAccObject.get_accFocus(out pvarID: OleVariant): HRESULT;
  469. begin
  470. Result := S_FALSE;
  471. end;
  472. function TTBViewAccObject.get_accHelp(varChild: OleVariant;
  473. out pszHelp: WideString): HRESULT;
  474. begin
  475. Result := S_FALSE;
  476. end;
  477. function TTBViewAccObject.get_accHelpTopic(out pszHelpFile: WideString;
  478. varChild: OleVariant; out pidTopic: Integer): HRESULT;
  479. begin
  480. pidTopic := 0; { Delphi doesn't implicitly clear Integer 'out' parameters }
  481. Result := S_FALSE;
  482. end;
  483. function TTBViewAccObject.get_accKeyboardShortcut(varChild: OleVariant;
  484. out pszKeyboardShortcut: WideString): HRESULT;
  485. begin
  486. try
  487. if not Check(varChild, Result) then
  488. Exit;
  489. if vsMenuBar in FView.Style then begin
  490. pszKeyboardShortcut := GetAltKeyName;
  491. Result := S_OK;
  492. end
  493. else
  494. Result := S_FALSE;
  495. except
  496. Result := E_UNEXPECTED;
  497. end;
  498. end;
  499. function TTBViewAccObject.get_accName(varChild: OleVariant;
  500. out pszName: WideString): HRESULT;
  501. var
  502. S: String;
  503. begin
  504. try
  505. if not Check(varChild, Result) then
  506. Exit;
  507. if Assigned(FView.ParentView) and Assigned(FView.ParentView.OpenViewer) then
  508. S := StripAccelChars(TTBItemViewerAccess(FView.ParentView.OpenViewer).GetCaptionText);
  509. if S = '' then
  510. S := TControlAccess(FView.Window).Caption;
  511. pszName := S;
  512. Result := S_OK;
  513. except
  514. Result := E_UNEXPECTED;
  515. end;
  516. end;
  517. function TTBViewAccObject.get_accParent(out ppdispParent: IDispatch): HRESULT;
  518. begin
  519. try
  520. if Assigned(FView) then begin
  521. if Assigned(FView.ParentView) and Assigned(FView.ParentView.OpenViewer) then begin
  522. ppdispParent := FView.ParentView.OpenViewer.GetAccObject;
  523. Result := S_OK;
  524. end
  525. else begin
  526. if AccObjectFromWindow(FView.Window.Handle, ppdispParent) then
  527. Result := S_OK
  528. else
  529. Result := E_UNEXPECTED;
  530. end;
  531. end
  532. else
  533. Result := E_FAIL;
  534. except
  535. Result := E_UNEXPECTED;
  536. end;
  537. end;
  538. function TTBViewAccObject.get_accRole(varChild: OleVariant;
  539. out pvarRole: OleVariant): HRESULT;
  540. begin
  541. try
  542. if not Check(varChild, Result) then
  543. Exit;
  544. if FView.IsPopup then
  545. pvarRole := ROLE_SYSTEM_MENUPOPUP
  546. else begin
  547. if vsMenuBar in FView.Style then
  548. pvarRole := ROLE_SYSTEM_MENUBAR
  549. else
  550. pvarRole := ROLE_SYSTEM_TOOLBAR;
  551. end;
  552. Result := S_OK;
  553. except
  554. Result := E_UNEXPECTED;
  555. end;
  556. end;
  557. function TTBViewAccObject.get_accSelection(out pvarChildren: OleVariant): HRESULT;
  558. begin
  559. Result := S_FALSE;
  560. end;
  561. function TTBViewAccObject.get_accState(varChild: OleVariant;
  562. out pvarState: OleVariant): HRESULT;
  563. begin
  564. try
  565. if not Check(varChild, Result) then
  566. Exit;
  567. pvarState := 0;
  568. Result := S_OK;
  569. except
  570. Result := E_UNEXPECTED;
  571. end;
  572. end;
  573. function TTBViewAccObject.get_accValue(varChild: OleVariant;
  574. out pszValue: WideString): HRESULT;
  575. begin
  576. Result := S_FALSE;
  577. end;
  578. function TTBViewAccObject.put_accName(varChild: OleVariant;
  579. const pszName: WideString): HRESULT;
  580. begin
  581. Result := S_FALSE;
  582. end;
  583. function TTBViewAccObject.put_accValue(varChild: OleVariant;
  584. const pszValue: WideString): HRESULT;
  585. begin
  586. Result := S_FALSE;
  587. end;
  588. { TTBItemViewerAccObject }
  589. constructor TTBItemViewerAccObject.Create(AViewer: TTBItemViewer);
  590. begin
  591. inherited Create;
  592. FViewer := AViewer;
  593. InterlockedIncrement(ItemViewerAccObjectInstances);
  594. end;
  595. destructor TTBItemViewerAccObject.Destroy;
  596. begin
  597. InterlockedDecrement(ItemViewerAccObjectInstances);
  598. if Assigned(FViewer) then begin
  599. TTBItemViewerAccess(FViewer).FAccObjectInstance := nil;
  600. FViewer := nil;
  601. end;
  602. inherited;
  603. end;
  604. procedure TTBItemViewerAccObject.ClientIsDestroying;
  605. begin
  606. FViewer := nil;
  607. end;
  608. function TTBItemViewerAccObject.Check(const varChild: OleVariant;
  609. var ErrorCode: HRESULT): Boolean;
  610. begin
  611. if FViewer = nil then begin
  612. ErrorCode := E_FAIL;
  613. Result := False;
  614. end
  615. else if (VarType(varChild) <> varInteger) or (varChild <> CHILDID_SELF) then begin
  616. ErrorCode := E_INVALIDARG;
  617. Result := False;
  618. end
  619. else
  620. Result := True;
  621. end;
  622. function TTBItemViewerAccObject.IsActionable: Boolean;
  623. { Returns True if 'doDefaultAction' may be performed on the viewer, i.e. if
  624. it's visible/off-edge/clipped, enabled & selectable, and the view is
  625. focusable. }
  626. begin
  627. Result := FViewer.IsAccessible and IsAvailable and IsFocusable;
  628. end;
  629. function TTBItemViewerAccObject.IsAvailable: Boolean;
  630. { Returns True if the viewer's item is enabled and selectable }
  631. begin
  632. Result := FViewer.Item.Enabled and
  633. (tbisSelectable in TTBCustomItemAccess(FViewer.Item).ItemStyle);
  634. end;
  635. function TTBItemViewerAccObject.IsFocusable: Boolean;
  636. { Returns True if viewers on the view can be 'focused' (i.e. the view's window
  637. doesn't have the csDesigning state, the window is visible and enabled, and
  638. the application is active). }
  639. function IsWindowAndParentsEnabled(W: HWND): Boolean;
  640. begin
  641. Result := True;
  642. repeat
  643. if not IsWindowEnabled(W) then begin
  644. Result := False;
  645. Break;
  646. end;
  647. W := GetParent(W);
  648. until W = 0;
  649. end;
  650. var
  651. ViewWnd, ActiveWnd: HWND;
  652. begin
  653. Result := False;
  654. if csDesigning in FViewer.View.Window.ComponentState then
  655. Exit;
  656. ViewWnd := FViewer.View.Window.Handle;
  657. if IsWindowVisible(ViewWnd) and IsWindowAndParentsEnabled(ViewWnd) then begin
  658. if vsModal in FViewer.View.State then
  659. Result := True
  660. else begin
  661. ActiveWnd := GetActiveWindow;
  662. if (ActiveWnd <> 0) and
  663. ((ActiveWnd = ViewWnd) or IsChild(ActiveWnd, ViewWnd)) then
  664. Result := True;
  665. end;
  666. end;
  667. end;
  668. procedure TTBItemViewerAccObject.HandleAccSelect(const AExecute: Boolean);
  669. begin
  670. if Assigned(FViewer) and
  671. ((AExecute and IsActionable) or (not AExecute and IsFocusable)) then begin
  672. FViewer.View.Selected := FViewer;
  673. FViewer.View.ScrollSelectedIntoView;
  674. if vsModal in FViewer.View.State then begin
  675. if AExecute then
  676. FViewer.View.ExecuteSelected(False);
  677. end
  678. else if (FViewer.View.ParentView = nil) and (GetCapture = 0) then begin
  679. if AExecute then
  680. FViewer.View.EnterToolbarLoop([tbetExecuteSelected, tbetFromMSAA])
  681. else
  682. FViewer.View.EnterToolbarLoop([tbetFromMSAA]);
  683. end;
  684. end;
  685. end;
  686. function TTBItemViewerAccObject.accDoDefaultAction(varChild: OleVariant): HRESULT;
  687. begin
  688. try
  689. if not Check(varChild, Result) then
  690. Exit;
  691. { NOTE: This must be kept in synch with get_accDefaultAction }
  692. if IsActionable then begin
  693. Result := S_OK;
  694. if FViewer.View.OpenViewer = FViewer then begin
  695. FViewer.View.CancelChildPopups;
  696. { Like standard menus, cancel the modal loop when a top-level menu
  697. is closed }
  698. if (vsModal in FViewer.View.State) and not FViewer.View.IsPopup then
  699. FViewer.View.EndModal;
  700. end
  701. else begin
  702. FViewer.View.Selected := FViewer;
  703. FViewer.View.ScrollSelectedIntoView;
  704. TTBItemViewerAccess(FViewer).PostAccSelect(True);
  705. end;
  706. end
  707. else
  708. { Note: Standard menus return DISP_E_MEMBERNOTFOUND in this case but
  709. that doesn't make much sense. The member is there but just isn't
  710. currently available. }
  711. Result := E_FAIL;
  712. except
  713. Result := E_UNEXPECTED;
  714. end;
  715. end;
  716. function TTBItemViewerAccObject.accHitTest(xLeft, yTop: Integer;
  717. out pvarID: OleVariant): HRESULT;
  718. var
  719. P: TPoint;
  720. begin
  721. try
  722. if FViewer = nil then begin
  723. Result := E_FAIL;
  724. Exit;
  725. end;
  726. P := FViewer.View.Window.ScreenToClient(Point(xLeft, yTop));
  727. if PtInRect(FViewer.BoundsRect, P) then begin
  728. pvarID := CHILDID_SELF;
  729. Result := S_OK;
  730. end
  731. else
  732. Result := S_FALSE;
  733. except
  734. Result := E_UNEXPECTED;
  735. end;
  736. end;
  737. function TTBItemViewerAccObject.accLocation(out pxLeft, pyTop, pcxWidth,
  738. pcyHeight: Integer; varChild: OleVariant): HRESULT;
  739. var
  740. R: TRect;
  741. P: TPoint;
  742. begin
  743. try
  744. if not Check(varChild, Result) then
  745. Exit;
  746. R := FViewer.BoundsRect;
  747. P := FViewer.View.Window.ClientToScreen(Point(0, 0));
  748. OffsetRect(R, P.X, P.Y);
  749. pxLeft := R.Left;
  750. pyTop := R.Top;
  751. pcxWidth := R.Right - R.Left;
  752. pcyHeight := R.Bottom - R.Top;
  753. Result := S_OK;
  754. except
  755. Result := E_UNEXPECTED;
  756. end;
  757. end;
  758. function TTBItemViewerAccObject.accNavigate(navDir: Integer; varStart: OleVariant;
  759. out pvarEnd: OleVariant): HRESULT;
  760. var
  761. I, J: Integer;
  762. View: TTBView;
  763. begin
  764. try
  765. if not Check(varStart, Result) then
  766. Exit;
  767. Result := S_FALSE;
  768. if (navDir = NAVDIR_FIRSTCHILD) or (navDir = NAVDIR_LASTCHILD) then begin
  769. { Return the child view's acc. object }
  770. View := FViewer.View.OpenViewerView;
  771. if Assigned(View) then begin
  772. pvarEnd := View.GetAccObject;
  773. Result := S_OK;
  774. end;
  775. end
  776. else begin
  777. I := FViewer.View.IndexOf(FViewer);
  778. if I >= 0 then begin
  779. case navDir of
  780. NAVDIR_UP, NAVDIR_LEFT, NAVDIR_PREVIOUS:
  781. for J := I-1 downto 0 do
  782. if FViewer.View.Viewers[J].IsAccessible then begin
  783. pvarEnd := FViewer.View.Viewers[J].GetAccObject;
  784. Result := S_OK;
  785. Break;
  786. end;
  787. NAVDIR_DOWN, NAVDIR_RIGHT, NAVDIR_NEXT:
  788. for J := I+1 to FViewer.View.ViewerCount-1 do
  789. if FViewer.View.Viewers[J].IsAccessible then begin
  790. pvarEnd := FViewer.View.Viewers[J].GetAccObject;
  791. Result := S_OK;
  792. Break;
  793. end;
  794. end;
  795. end;
  796. end;
  797. except
  798. Result := E_UNEXPECTED;
  799. end;
  800. end;
  801. function TTBItemViewerAccObject.accSelect(flagsSelect: Integer;
  802. varChild: OleVariant): HRESULT;
  803. begin
  804. try
  805. if not Check(varChild, Result) then
  806. Exit;
  807. if flagsSelect <> SELFLAG_TAKEFOCUS then begin
  808. Result := E_INVALIDARG;
  809. Exit;
  810. end;
  811. if IsFocusable and (FViewer.Show or FViewer.Clipped) then begin
  812. FViewer.View.Selected := FViewer;
  813. FViewer.View.ScrollSelectedIntoView;
  814. if not(vsModal in FViewer.View.State) and
  815. (FViewer.View.ParentView = nil) then
  816. TTBItemViewerAccess(FViewer).PostAccSelect(False);
  817. end
  818. else
  819. Result := E_FAIL;
  820. { ^ what Office XP returns when you try focusing an off-edge item }
  821. except
  822. Result := E_UNEXPECTED;
  823. end;
  824. end;
  825. function TTBItemViewerAccObject.get_accChild(varChild: OleVariant;
  826. out ppdispChild: IDispatch): HRESULT;
  827. var
  828. View: TTBView;
  829. begin
  830. try
  831. if FViewer = nil then begin
  832. Result := E_FAIL;
  833. Exit;
  834. end;
  835. Result := E_INVALIDARG;
  836. if VarType(varChild) = varInteger then begin
  837. if varChild = CHILDID_SELF then begin
  838. ppdispChild := Self;
  839. Result := S_OK;
  840. end
  841. else if varChild = 1 then begin
  842. { Return the child view's acc. object }
  843. View := FViewer.View.OpenViewerView;
  844. if Assigned(View) then begin
  845. ppdispChild := View.GetAccObject;
  846. Result := S_OK;
  847. end;
  848. end;
  849. end;
  850. except
  851. Result := E_UNEXPECTED;
  852. end;
  853. end;
  854. function TTBItemViewerAccObject.get_accChildCount(out pcountChildren: Integer): HRESULT;
  855. begin
  856. try
  857. if FViewer = nil then begin
  858. Result := E_FAIL;
  859. Exit;
  860. end;
  861. { Return 1 if the viewer has a child view }
  862. if FViewer.View.OpenViewer = FViewer then
  863. pCountChildren := 1
  864. else
  865. pCountChildren := 0;
  866. Result := S_OK;
  867. except
  868. Result := E_UNEXPECTED;
  869. end;
  870. end;
  871. function TTBItemViewerAccObject.get_accDefaultAction(varChild: OleVariant;
  872. out pszDefaultAction: WideString): HRESULT;
  873. begin
  874. try
  875. if not Check(varChild, Result) then
  876. Exit;
  877. if IsActionable then begin
  878. { I'm not sure if these should be localized, or even if any screen
  879. readers make use of this text...
  880. NOTE: This must be kept in synch with accDoDefaultAction }
  881. if FViewer.View.OpenViewer = FViewer then
  882. pszDefaultAction := 'Close'
  883. else if tbisSubmenu in TTBCustomItemAccess(FViewer.Item).ItemStyle then
  884. pszDefaultAction := 'Open'
  885. else if FViewer.View.IsPopup or (vsMenuBar in FViewer.View.Style) then
  886. pszDefaultAction := 'Execute'
  887. else
  888. pszDefaultAction := 'Press';
  889. Result := S_OK;
  890. end
  891. else
  892. Result := S_FALSE;
  893. except
  894. Result := E_UNEXPECTED;
  895. end;
  896. end;
  897. function TTBItemViewerAccObject.get_accDescription(varChild: OleVariant;
  898. out pszDescription: WideString): HRESULT;
  899. begin
  900. Result := S_FALSE;
  901. end;
  902. function TTBItemViewerAccObject.get_accFocus(out pvarID: OleVariant): HRESULT;
  903. begin
  904. try
  905. if FViewer = nil then begin
  906. Result := E_FAIL;
  907. Exit;
  908. end;
  909. if (vsModal in FViewer.View.State) and
  910. (FViewer.View.Selected = FViewer) then begin
  911. pvarID := CHILDID_SELF;
  912. Result := S_OK;
  913. end
  914. else
  915. Result := S_FALSE;
  916. except
  917. Result := E_UNEXPECTED;
  918. end;
  919. end;
  920. function TTBItemViewerAccObject.get_accHelp(varChild: OleVariant;
  921. out pszHelp: WideString): HRESULT;
  922. begin
  923. Result := S_FALSE;
  924. end;
  925. function TTBItemViewerAccObject.get_accHelpTopic(out pszHelpFile: WideString;
  926. varChild: OleVariant; out pidTopic: Integer): HRESULT;
  927. begin
  928. pidTopic := 0; { Delphi doesn't implicitly clear Integer 'out' parameters }
  929. Result := S_FALSE;
  930. end;
  931. function TTBItemViewerAccObject.get_accKeyboardShortcut(varChild: OleVariant;
  932. out pszKeyboardShortcut: WideString): HRESULT;
  933. var
  934. C: Char;
  935. begin
  936. try
  937. if not Check(varChild, Result) then
  938. Exit;
  939. Result := S_FALSE;
  940. if TTBItemViewerAccess(FViewer).CaptionShown then begin
  941. C := FindAccelChar(TTBItemViewerAccess(FViewer).GetCaptionText);
  942. if C <> #0 then begin
  943. CharLowerBuff(@C, 1); { like standard menus, always use lowercase... }
  944. if FViewer.View.IsPopup then
  945. pszKeyboardShortcut := C
  946. else begin
  947. { Prefix 'Alt+' }
  948. pszKeyboardShortcut := GetAltKeyName + '+' + C;
  949. end;
  950. Result := S_OK;
  951. end;
  952. end;
  953. except
  954. Result := E_UNEXPECTED;
  955. end;
  956. end;
  957. function TTBItemViewerAccObject.get_accName(varChild: OleVariant;
  958. out pszName: WideString): HRESULT;
  959. var
  960. C, S: String;
  961. begin
  962. try
  963. if not Check(varChild, Result) then
  964. Exit;
  965. C := StripAccelChars(TTBItemViewerAccess(FViewer).GetCaptionText);
  966. if not FViewer.IsToolbarStyle then
  967. S := FViewer.Item.GetShortCutText;
  968. if S = '' then
  969. pszName := C
  970. else
  971. pszName := C + #9 + S;
  972. Result := S_OK;
  973. except
  974. Result := E_UNEXPECTED;
  975. end;
  976. end;
  977. function TTBItemViewerAccObject.get_accParent(out ppdispParent: IDispatch): HRESULT;
  978. begin
  979. try
  980. if Assigned(FViewer) then begin
  981. ppdispParent := FViewer.View.GetAccObject;
  982. Result := S_OK;
  983. end
  984. else
  985. Result := E_FAIL;
  986. except
  987. Result := E_UNEXPECTED;
  988. end;
  989. end;
  990. function TTBItemViewerAccObject.get_accRole(varChild: OleVariant;
  991. out pvarRole: OleVariant): HRESULT;
  992. begin
  993. try
  994. if not Check(varChild, Result) then
  995. Exit;
  996. pvarRole := TTBItemViewerAccess(FViewer).GetAccRole;
  997. Result := S_OK;
  998. except
  999. Result := E_UNEXPECTED;
  1000. end;
  1001. end;
  1002. function TTBItemViewerAccObject.get_accSelection(out pvarChildren: OleVariant): HRESULT;
  1003. begin
  1004. Result := S_FALSE;
  1005. end;
  1006. function TTBItemViewerAccObject.get_accState(varChild: OleVariant;
  1007. out pvarState: OleVariant): HRESULT;
  1008. var
  1009. Flags: Integer;
  1010. begin
  1011. try
  1012. if not Check(varChild, Result) then
  1013. Exit;
  1014. Flags := 0;
  1015. if FViewer.View.Selected = FViewer then begin
  1016. Flags := Flags or STATE_SYSTEM_HOTTRACKED;
  1017. if vsModal in FViewer.View.State then
  1018. Flags := Flags or STATE_SYSTEM_FOCUSED;
  1019. if FViewer.View.MouseOverSelected and FViewer.View.Capture then
  1020. { ^ based on "IsPushed :=" code in TTBView.DrawItem }
  1021. Flags := Flags or STATE_SYSTEM_PRESSED;
  1022. end;
  1023. if tbisSubmenu in TTBCustomItemAccess(FViewer.Item).ItemStyle then
  1024. Flags := Flags or STATE_SYSTEM_HASPOPUP;
  1025. if FViewer.Show or FViewer.Clipped then begin
  1026. if IsFocusable then
  1027. Flags := Flags or STATE_SYSTEM_FOCUSABLE;
  1028. end
  1029. else begin
  1030. { Mark off-edge items as invisible, like Office }
  1031. Flags := Flags or STATE_SYSTEM_INVISIBLE;
  1032. end;
  1033. if not IsAvailable then
  1034. Flags := Flags or STATE_SYSTEM_UNAVAILABLE;
  1035. if FViewer.Item.Checked then
  1036. Flags := Flags or STATE_SYSTEM_CHECKED;
  1037. pvarState := Flags;
  1038. Result := S_OK;
  1039. except
  1040. Result := E_UNEXPECTED;
  1041. end;
  1042. end;
  1043. function TTBItemViewerAccObject.get_accValue(varChild: OleVariant;
  1044. out pszValue: WideString): HRESULT;
  1045. begin
  1046. try
  1047. if not Check(varChild, Result) then
  1048. Exit;
  1049. if TTBItemViewerAccess(FViewer).GetAccValue(pszValue) then
  1050. Result := S_OK
  1051. else begin
  1052. pszValue := '';
  1053. Result := S_FALSE;
  1054. end;
  1055. except
  1056. Result := E_UNEXPECTED;
  1057. end;
  1058. end;
  1059. function TTBItemViewerAccObject.put_accName(varChild: OleVariant;
  1060. const pszName: WideString): HRESULT;
  1061. begin
  1062. Result := S_FALSE;
  1063. end;
  1064. function TTBItemViewerAccObject.put_accValue(varChild: OleVariant;
  1065. const pszValue: WideString): HRESULT;
  1066. begin
  1067. Result := S_FALSE;
  1068. end;
  1069. { Note: This COM initialization code based on code from DBTables }
  1070. var
  1071. SaveInitProc: Pointer;
  1072. NeedToUninitialize: Boolean;
  1073. procedure InitCOM;
  1074. begin
  1075. if SaveInitProc <> nil then TProcedure(SaveInitProc);
  1076. NeedToUninitialize := SUCCEEDED(CoInitialize(nil));
  1077. end;
  1078. initialization
  1079. InitializeCriticalSection(LastAccObjectCritSect);
  1080. if not IsLibrary then begin
  1081. SaveInitProc := InitProc;
  1082. InitProc := @InitCOM;
  1083. end;
  1084. finalization
  1085. DisconnectAccObjects;
  1086. if NeedToUninitialize then
  1087. CoUninitialize;
  1088. DeleteCriticalSection(LastAccObjectCritSect);
  1089. end.