Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3501 lines
109 KiB

  1. /*************************************************************************
  2. Project: Narrator
  3. Module: keys.cpp
  4. Author: Paul Blenkhorn
  5. Date: April 1997
  6. Notes: Credit to be given to MSAA team - bits of code have been
  7. lifted from:
  8. Babble, Inspect, and Snapshot.
  9. Copyright (C) 1997-1998 by Microsoft Corporation. All rights reserved.
  10. See bottom of file for disclaimer
  11. History: Add features, Bug fixes : 1999 Anil Kumar
  12. *************************************************************************/
  13. #define STRICT
  14. #include <windows.h>
  15. #include <windowsx.h>
  16. #include <ole2.h>
  17. #include <ole2ver.h>
  18. #include <commctrl.h>
  19. #include "commdlg.h"
  20. #include <string.h>
  21. #include <initguid.h>
  22. #include <oleacc.h>
  23. #include <TCHAR.H>
  24. #include "..\Narrator\Narrator.h"
  25. #include "keys.h"
  26. #include "w95trace.c"
  27. #include "getprop.h"
  28. #include "..\Narrator\resource.h"
  29. #include "resource.h"
  30. #include "list.h" // include list.h before helpthd.h, GINFO needs CList
  31. #include "HelpThd.h"
  32. #include <stdio.h>
  33. #include "mappedfile.cpp"
  34. template<class _Ty> class CAutoArray
  35. {
  36. public:
  37. explicit CAutoArray(_Ty *_P = 0) : _Ptr(_P) {}
  38. ~CAutoArray()
  39. {
  40. if ( _Ptr )
  41. delete [] _Ptr;
  42. }
  43. _Ty *Get()
  44. {
  45. return _Ptr;
  46. }
  47. private:
  48. _Ty *_Ptr;
  49. };
  50. #define ARRAYSIZE(x) (sizeof(x) / sizeof(*x))
  51. // ROBSI: 99-10-09
  52. #define MAX_NAME 4196 // 4K (beyond Max of MSAA)
  53. #define MAX_VALUE 512
  54. // When building with VC5, we need winable.h since the active
  55. // accessibility structures are not in VC5's winuser.h. winable.h can
  56. // be found in the active accessibility SDK
  57. #ifdef VC5_BUILD___NOT_NT_BUILD_ENVIRONMENT
  58. #include <winable.h>
  59. #endif
  60. #define STATE_MASK (STATE_SYSTEM_CHECKED | STATE_SYSTEM_MIXED | STATE_SYSTEM_READONLY | STATE_SYSTEM_BUSY | STATE_SYSTEM_MARQUEED | STATE_SYSTEM_ANIMATED | STATE_SYSTEM_INVISIBLE | STATE_SYSTEM_UNAVAILABLE)
  61. // Local functions
  62. void Home(int x);
  63. void MoveToEnd(int x);
  64. void SpeakKeyboard(int nOption);
  65. void SpeakWindow(int nOption);
  66. void SpeakRepeat(int nOption);
  67. void SpeakItem(int nOption);
  68. void SpeakMainItems(int nOption);
  69. void SpeakMute(int nOption);
  70. void GetObjectProperty(IAccessible*, long, int, LPTSTR, UINT);
  71. void AddAccessibleObjects(IAccessible*, const VARIANT &);
  72. BOOL AddItem(IAccessible*, const VARIANT &);
  73. void SpeakObjectInfo(LPOBJINFO poiObj, BOOL SpeakExtra);
  74. BOOL Object_Normalize( IAccessible * pAcc,
  75. VARIANT * pvarChild,
  76. IAccessible ** ppAccOut,
  77. VARIANT * pvarChildOut);
  78. _inline void InitChildSelf(VARIANT *pvar)
  79. {
  80. pvar->vt = VT_I4;
  81. pvar->lVal = CHILDID_SELF;
  82. }
  83. // MSAA event handlers
  84. BOOL OnFocusChangedEvent(DWORD event, HWND hwnd, LONG idObject, LONG idChild,
  85. DWORD dwmsTimeStamp);
  86. BOOL OnValueChangedEvent(DWORD event, HWND hwnd, LONG idObject, LONG idChild,
  87. DWORD dwmsTimeStamp);
  88. BOOL OnSelectChangedEvent(DWORD event, HWND hwnd, LONG idObject, LONG idChild,
  89. DWORD dwmsTimeStamp);
  90. BOOL OnLocationChangedEvent(DWORD event, HWND hwnd, LONG idObject,
  91. LONG idChild, DWORD dwmsTimeStamp);
  92. BOOL OnStateChangedEvent(DWORD event, HWND hwnd, LONG idObject, LONG idChild,
  93. DWORD dwmsTimeStamp);
  94. BOOL OnObjectShowEvent(DWORD event, HWND hwnd, LONG idObject, LONG idChild,
  95. DWORD dwmsTimeStamp);
  96. // More local routines
  97. LRESULT CALLBACK KeyboardProc(int code, WPARAM wParam, LPARAM lParam);
  98. LRESULT CALLBACK MouseProc(int code, WPARAM wParam, LPARAM lParam);
  99. BOOL IsFocussedItem( HWND hWnd, IAccessible * pAcc, VARIANT varChild );
  100. void FilterGUID(TCHAR* szSpeak);
  101. // Hot keys
  102. HOTK rgHotKeys[] =
  103. { //Key SHIFT Function Parameter
  104. { VK_F12, MSR_CTRL | MSR_SHIFT, SpeakKeyboard, 0},
  105. { VK_SPACE, MSR_CTRL | MSR_SHIFT, SpeakWindow, 1},
  106. { VK_RETURN,MSR_CTRL | MSR_SHIFT, SpeakMainItems, 0},
  107. { VK_INSERT,MSR_CTRL | MSR_SHIFT, SpeakItem, 0},
  108. { VK_HOME, MSR_ALT, Home, 0},
  109. { VK_END, MSR_ALT, MoveToEnd, 0},
  110. };
  111. // a-anilk: this is better than defining as a constant - you don't have to worry
  112. // about making the table and the count match up.
  113. #define CKEYS_HOT (sizeof(rgHotKeys)/sizeof(HOTK))
  114. #define MSR_DONOWT 0
  115. #define MSR_DOCHAR 1
  116. #define MSR_DOWORD 2
  117. #define MSR_DOLINE 3
  118. #define MSR_DOLINED 4
  119. #define MSR_DOCHARR 5
  120. #define MSR_DOWORDR 6
  121. #define MSR_DOOBJECT 4
  122. #define MSR_DOWINDOW 5
  123. #define MAX_TEXT_ROLE 128
  124. HHOOK g_hhookKey = 0;
  125. HHOOK g_hhookMouse = 0;
  126. HWINEVENTHOOK g_hEventHook = 0;
  127. POINT g_ptMoveCursor = {0,0};
  128. UINT_PTR g_uTimer = 0;
  129. // Global Variables stored in memory mapped file
  130. struct GLOBALDATA
  131. {
  132. int nAutoRead; // Did we get to ReadWindow through focus change or CTRL_ALT_SPACE flag
  133. int nSpeakWindowSoon; // Flag to indicate the we have a new window ... speak it when sensible
  134. int nLeftHandSide; // Store left hand side of HTML window we want to read
  135. BOOL fDoingPassword;
  136. int nMsrDoNext; // keyboard flag set when curor keys are used ... let us know what to read when caret has moved
  137. HWND hwndMSR;
  138. // Global variables to control events and speech
  139. BOOL fInternetExplorer;
  140. BOOL fHTML_Help;
  141. UINT uMSG_MSR_Cursor;
  142. POINT ptCurrentMouse;
  143. BOOL fMouseUp; // flag for mouse up/down
  144. HWND hwndHelp;
  145. BOOL fJustHadSpace;
  146. BOOL fJustHadShiftKeys;
  147. BOOL fListFocus; // To avoid double speaking of list items...
  148. BOOL fStartPressed;
  149. TCHAR pszTextLocal[2000]; // PB: 22 Nov 1998. Make it work!!! Make this Global and Shared!
  150. // Global data that used to be exported from the DLL
  151. TCHAR szCurrentText[MAX_TEXT];
  152. int fTrackSecondary;
  153. int fTrackCaret;
  154. int fTrackInputFocus;
  155. int nEchoChars;
  156. int fAnnounceWindow;
  157. int fAnnounceMenu;
  158. int fAnnouncePopup;
  159. int fAnnounceToolTips;
  160. int fReviewStyle;
  161. int nReviewLevel;
  162. };
  163. // pointer to shared global data
  164. GLOBALDATA *g_pGlobalData = 0;
  165. // pointer to mem mapped file handle
  166. CMemMappedFile *g_CMappedFile = 0;
  167. // the number of times to try to create mem mapped file must be < 10
  168. const int c_cMappedFileTries = 3;
  169. // name of memory mapped file
  170. TCHAR g_szMappedFileName[] = TEXT("NarratorShared0");
  171. // mutex to access mem mapped file and wait time
  172. TCHAR g_szMutexNarrator[] = TEXT("NarratorMutex0");
  173. const int c_nMutexWait = 5000;
  174. void InitGlobalData()
  175. {
  176. CScopeMutex csMutex;
  177. if (csMutex.Create(g_szMutexNarrator, c_nMutexWait) && g_pGlobalData)
  178. {
  179. DBPRINTF(TEXT("InitGlobalData\r\n"));
  180. g_pGlobalData->nMsrDoNext = MSR_DONOWT; // keyboard flag set when curor keys are used
  181. g_pGlobalData->ptCurrentMouse.x = -1;
  182. g_pGlobalData->ptCurrentMouse.y = -1;
  183. g_pGlobalData->fMouseUp = TRUE; // flag for mouse up/down
  184. g_pGlobalData->fTrackSecondary = TRUE;
  185. g_pGlobalData->fTrackCaret = TRUE;
  186. g_pGlobalData->fTrackInputFocus = FALSE;
  187. g_pGlobalData->nEchoChars = MSR_ECHOALNUM | MSR_ECHOSPACE | MSR_ECHODELETE | MSR_ECHOMODIFIERS | MSR_ECHOENTER | MSR_ECHOBACK | MSR_ECHOTAB;
  188. g_pGlobalData->fAnnounceWindow = TRUE;
  189. g_pGlobalData->fAnnounceMenu = TRUE;
  190. g_pGlobalData->fAnnouncePopup = TRUE;
  191. g_pGlobalData->fAnnounceToolTips = FALSE; // this ain't working properly - taken out!
  192. g_pGlobalData->fReviewStyle = TRUE;
  193. g_pGlobalData->nReviewLevel = 0;
  194. }
  195. }
  196. BOOL CreateMappedFile()
  197. {
  198. g_CMappedFile = new CMemMappedFile;
  199. if (g_CMappedFile)
  200. {
  201. // Append a number thus avoiding restart timing issue
  202. // on desktop switches but only try 3 times
  203. // ISSUE (micw 08/22/00)
  204. // - this code has potential problem of ending up with two or more mapped
  205. // files open. A mapped file for narrator and one for each hook. Hooks will
  206. // cause the DLL to get loaded for that process. If narrator has NarratorShared1
  207. // opened and the hook loads this DLL then the hooked process will have
  208. // NarratorShared0 open. Could use narrator's hwnd as the thing to append to the
  209. // file and mutex name. This code could find the narrator hwnd and use that to
  210. // open. Letting this go for now since the above hasn't been observed in testing.
  211. int iPos1 = lstrlen(g_szMappedFileName) - 1;
  212. int iPos2 = lstrlen(g_szMutexNarrator) - 1;
  213. for (int i=0;i<c_cMappedFileTries;i++)
  214. {
  215. if (TRUE == g_CMappedFile->Open(g_szMappedFileName, sizeof(GLOBALDATA)))
  216. {
  217. CScopeMutex csMutex;
  218. if (csMutex.Create(g_szMutexNarrator, c_nMutexWait))
  219. {
  220. g_CMappedFile->AccessMem((void **)&g_pGlobalData);
  221. if (g_CMappedFile->FirstOpen())
  222. InitGlobalData();
  223. DBPRINTF(TEXT("CreateMappedFile: Succeeded %d try!\r\n"), i);
  224. return TRUE;
  225. }
  226. g_CMappedFile->Close();
  227. break; // fail if get to here
  228. }
  229. Sleep(500);
  230. g_szMappedFileName[iPos1] = '1'+i;
  231. g_szMutexNarrator[iPos2] = '1'+i;
  232. }
  233. }
  234. DBPRINTF(TEXT("CreateMappedFile: Unable to create the mapped file!\r\n"));
  235. return FALSE;
  236. }
  237. void CloseMappedFile()
  238. {
  239. if (g_CMappedFile)
  240. {
  241. g_CMappedFile->Close();
  242. delete g_CMappedFile;
  243. g_CMappedFile = 0;
  244. }
  245. }
  246. //
  247. // Accessor functions for what used to be exported, shared, variables
  248. //
  249. #define SIMPLE_FUNC_IMPL(type, prefix, name, error) \
  250. type Get ## name() \
  251. { \
  252. CScopeMutex csMutex; \
  253. if (csMutex.Create(g_szMutexNarrator, c_nMutexWait)) \
  254. { \
  255. return g_pGlobalData->prefix ## name; \
  256. } else \
  257. { \
  258. return error; \
  259. } \
  260. } \
  261. void Set ## name(type value) \
  262. { \
  263. CScopeMutex csMutex; \
  264. if (csMutex.Create(g_szMutexNarrator, c_nMutexWait)) \
  265. { \
  266. g_pGlobalData->prefix ## name = value; \
  267. } \
  268. }
  269. SIMPLE_FUNC_IMPL(BOOL, f, TrackSecondary, FALSE)
  270. SIMPLE_FUNC_IMPL(BOOL, f, TrackCaret, FALSE)
  271. SIMPLE_FUNC_IMPL(BOOL, f, TrackInputFocus, FALSE)
  272. SIMPLE_FUNC_IMPL(int, n, EchoChars, 0)
  273. SIMPLE_FUNC_IMPL(BOOL, f, AnnounceWindow, FALSE)
  274. SIMPLE_FUNC_IMPL(BOOL, f, AnnounceMenu, FALSE)
  275. SIMPLE_FUNC_IMPL(BOOL, f, AnnouncePopup, FALSE)
  276. SIMPLE_FUNC_IMPL(BOOL, f, AnnounceToolTips, FALSE)
  277. SIMPLE_FUNC_IMPL(BOOL, f, ReviewStyle, FALSE)
  278. SIMPLE_FUNC_IMPL(int, n, ReviewLevel, 0)
  279. void GetCurrentText(LPTSTR psz, int cch)
  280. {
  281. CScopeMutex csMutex;
  282. if (csMutex.Create(g_szMutexNarrator, c_nMutexWait))
  283. {
  284. lstrcpyn(psz, g_pGlobalData->szCurrentText, cch);
  285. psz[cch-1] = TEXT('\0');
  286. }
  287. }
  288. void SetCurrentText(LPCTSTR psz)
  289. {
  290. CScopeMutex csMutex;
  291. if (csMutex.Create(g_szMutexNarrator, c_nMutexWait))
  292. {
  293. lstrcpyn(g_pGlobalData->szCurrentText, psz, MAX_TEXT);
  294. g_pGlobalData->szCurrentText[MAX_TEXT-1] = TEXT('\0');
  295. }
  296. }
  297. HINSTANCE g_Hinst = NULL;
  298. DWORD g_tidMain=0; // ROBSI: 10-10-99
  299. // These are class names, This could change from one OS to another and in
  300. // different OS releases.I have grouped them here : Anil.
  301. // These names may have to changed for Win9x and other releases
  302. #define CLASS_WINSWITCH TEXT("#32771") // This is WinSwitch class. Disguises itself :-)AK
  303. #define CLASS_HTMLHELP_IE TEXT("HTML_Internet Explorer")
  304. #define CLASS_IE_FRAME TEXT("IEFrame")
  305. #define CLASS_IE_MAINWND TEXT("Internet Explorer_Server")
  306. #define CLASS_LISTVIEW TEXT("SysListView32")
  307. #define CLASS_HTMLHELP TEXT("HH Parent")
  308. #define CLASS_TOOLBAR TEXT("ToolbarWindow32")
  309. #define CLASS_MS_WINNOTE TEXT("MS_WINNOTE")
  310. #define CLASS_HH_POPUP TEXT("hh_popup")
  311. BOOL IsTridentWindow( LPCTSTR szClass )
  312. {
  313. return lstrcmpi(szClass, CLASS_HTMLHELP_IE) == 0
  314. || lstrcmpi(szClass, CLASS_IE_FRAME) == 0
  315. || lstrcmpi(szClass, CLASS_IE_MAINWND) == 0
  316. || lstrcmpi(szClass, TEXT("PCHShell Window")) == 0 // Help & Support
  317. || lstrcmpi(szClass, TEXT("Internet Explorer_TridentDlgFrame")) == 0; // Trident popup windows
  318. }
  319. // Check if the pAcc, varChild refer to a balloon tip. If so, it places the corresponding
  320. // IAccessible and childID in the out ppAcc/pvarChild params.
  321. // The in pAcc/varChild params are always consumed, so should not be freed by caller.
  322. BOOL CheckIsBalloonTipElseRelease( IAccessible * pAcc, VARIANT varChild, IAccessible ** ppAcc, VARIANT * pvarChild )
  323. {
  324. VARIANT varRole;
  325. HRESULT hr = pAcc->get_accRole( varChild, &varRole );
  326. if ( hr == S_OK && varRole.vt == VT_I4 &&
  327. ( varRole.lVal == ROLE_SYSTEM_TOOLTIP || varRole.lVal == ROLE_SYSTEM_HELPBALLOON ) )
  328. {
  329. // Got it...
  330. *ppAcc = pAcc;
  331. pvarChild->vt = VT_I4;
  332. pvarChild->lVal = CHILDID_SELF;
  333. return TRUE;
  334. }
  335. pAcc->Release();
  336. return FALSE;
  337. }
  338. IAccessible * GetFocusedIAccessibile( HWND hwndFocus, VARIANT * varChild )
  339. {
  340. IAccessible *pIAcc = NULL;
  341. HRESULT hr = AccessibleObjectFromWindow(hwndFocus, OBJID_CLIENT,
  342. IID_IAccessible, (void**)&pIAcc);
  343. InitChildSelf(varChild);
  344. if (S_OK == hr)
  345. {
  346. while ( pIAcc )
  347. {
  348. HRESULT hr = pIAcc->get_accFocus(varChild);
  349. switch ( varChild->vt )
  350. {
  351. case VT_I4:
  352. return pIAcc;
  353. break;
  354. case VT_DISPATCH:
  355. {
  356. IAccessible * pAccTemp = NULL;
  357. hr = varChild->pdispVal->QueryInterface( IID_IAccessible, (void**) &pAccTemp );
  358. VariantClear( varChild );
  359. pIAcc->Release();
  360. if ( hr != S_OK )
  361. {
  362. pIAcc = NULL;
  363. break;
  364. }
  365. pIAcc = pAccTemp;
  366. break;
  367. }
  368. default:
  369. pIAcc->Release();
  370. pIAcc = NULL;
  371. break;
  372. }
  373. }
  374. }
  375. return NULL;
  376. }
  377. /*************************************************************************
  378. Function: SpeakString
  379. Purpose: Send speak string message back to original application
  380. Inputs: TCHAR *str
  381. Returns: void
  382. History:
  383. Uses sendmessage to avoid other messages firing and overwriting this one.
  384. *************************************************************************/
  385. void SpeakString(TCHAR * str)
  386. {
  387. DBPRINTF(TEXT("SpeakString '%s'\r\n"), str);
  388. lstrcpyn(g_pGlobalData->szCurrentText,str,MAX_TEXT);
  389. g_pGlobalData->szCurrentText[MAX_TEXT-1] = TEXT('\0');
  390. SendMessage(g_pGlobalData->hwndMSR, WM_MSRSPEAK, 0, 0);
  391. }
  392. /*************************************************************************
  393. Function: SpeakStr
  394. Purpose: Send speak string message back to original application
  395. Inputs: TCHAR *str
  396. Returns: void
  397. History:
  398. This one uses Postmessage to make focus change work for ALT-TAB???????
  399. *************************************************************************/
  400. void SpeakStr(TCHAR * str)
  401. {
  402. lstrcpyn(g_pGlobalData->szCurrentText,str,MAX_TEXT);
  403. g_pGlobalData->szCurrentText[MAX_TEXT-1] = TEXT('\0');
  404. PostMessage(g_pGlobalData->hwndMSR, WM_MSRSPEAK, 0, 0);
  405. }
  406. /*************************************************************************
  407. Function: SpeakStringAll
  408. Purpose: Speak the string, but put out a space first to make sure the
  409. string is fresh - i.e. stop duplicate string pruning from
  410. occuring
  411. Inputs: TCHAR *str
  412. Returns: void
  413. History:
  414. *************************************************************************/
  415. void SpeakStringAll(TCHAR * str)
  416. {
  417. SpeakString(TEXT(" ")); // stop speech filter losing duplicates
  418. SpeakString(str);
  419. }
  420. /*************************************************************************
  421. Function: SpeakStringId
  422. Purpose: Speak a string loaded as a resource ID
  423. Inputs: UINT id
  424. Returns: void
  425. History:
  426. *************************************************************************/
  427. void SpeakStringId(UINT id)
  428. {
  429. if (LoadString(g_Hinst, id, g_pGlobalData->szCurrentText, 256) == 0)
  430. {
  431. DBPRINTF (TEXT("LoadString failed on hinst %lX id %u\r\n"),g_Hinst,id);
  432. SpeakString(TEXT("TEXT NOT FOUND!"));
  433. }
  434. else
  435. {
  436. SendMessage(g_pGlobalData->hwndMSR, WM_MSRSPEAK, 0, 0);
  437. SpeakString(TEXT(" ")); // stop speech filter losing duplicates
  438. }
  439. }
  440. /*************************************************************************
  441. Function: SetSecondary
  442. Purpose: Set secondary focus position & posibly move mouse pointer
  443. Inputs: Position: x & y
  444. Whether to move cursor: MoveCursor
  445. Returns: void
  446. History:
  447. *************************************************************************/
  448. void SetSecondary(long x, long y, int MoveCursor)
  449. {
  450. g_pGlobalData->ptCurrentMouse.x = x;
  451. g_pGlobalData->ptCurrentMouse.y = y;
  452. if (MoveCursor)
  453. {
  454. // Check if co-ordinates are valid, At many places causes
  455. // the cursor to vanish...
  456. if ( x > 0 && y > 0 )
  457. SetCursorPos(x,y);
  458. }
  459. // Tell everyone where the cursor is.
  460. // g_pGlobalData->uMSG_MSR_Cursor set using RegisterWindowMessage below in InitMSAA
  461. SendMessage(HWND_BROADCAST,g_pGlobalData->uMSG_MSR_Cursor,x,y);
  462. }
  463. /*************************************************************************
  464. Function: TrackCursor
  465. Purpose: This is a callback in responce to a SetTimer it calls SetSecondary
  466. then kills the timer and resets the global timer flag.
  467. Returns: void
  468. History:
  469. *************************************************************************/
  470. VOID CALLBACK TrackCursor(HWND hwnd, // handle to window
  471. UINT uMsg, // WM_TIMER message
  472. UINT_PTR idEvent, // timer identifier
  473. DWORD dwTime ) // current system time
  474. {
  475. KillTimer( NULL, g_uTimer );
  476. g_uTimer = 0;
  477. SetSecondary(g_ptMoveCursor.x, g_ptMoveCursor.y, TRUE);
  478. return;
  479. }
  480. VOID GetStateString(LONG lState,
  481. LONG lStateMask,
  482. LPTSTR szState,
  483. UINT cchState )
  484. {
  485. int iStateBit;
  486. DWORD lStateBits;
  487. LPTSTR lpszT;
  488. UINT cchT;
  489. bool fFirstTime = true;
  490. cchState -= 1; // leave room for the null
  491. if ( !szState )
  492. return;
  493. *szState = TEXT('\0');
  494. for ( iStateBit = 0, lStateBits = 1; iStateBit < 32; iStateBit++, (lStateBits <<= 1) )
  495. {
  496. if ( !fFirstTime && cchState > 2)
  497. {
  498. *szState++ = TEXT(',');
  499. *szState++ = TEXT(' ');
  500. cchState -= 2;
  501. }
  502. *szState = TEXT('\0'); // make sure we are always null terminated
  503. if (lState & lStateBits & lStateMask)
  504. {
  505. cchT = GetStateText(lStateBits, szState, cchState);
  506. szState += cchT;
  507. cchState -= cchT;
  508. fFirstTime = false;
  509. }
  510. }
  511. }
  512. /*************************************************************************
  513. Function: BackToApplication
  514. Purpose: Set the focus back to the application that we came from with F12
  515. Inputs: void
  516. Returns: void
  517. History:
  518. *************************************************************************/
  519. void BackToApplication(void)
  520. {
  521. CScopeMutex csMutex;
  522. if (csMutex.Create(g_szMutexNarrator, c_nMutexWait))
  523. SetForegroundWindow(g_pGlobalData->hwndHelp);
  524. }
  525. /*************************************************************************
  526. Function: InitKeys
  527. Purpose: Set up processing for global hot keys
  528. Inputs: HWND hwnd
  529. Returns: BOOL - TRUE if successful
  530. History:
  531. *************************************************************************/
  532. BOOL InitKeys(HWND hwnd)
  533. {
  534. HMODULE hModSelf;
  535. CScopeMutex csMutex;
  536. if (!csMutex.Create(g_szMutexNarrator, c_nMutexWait))
  537. return FALSE;
  538. // If someone else has a hook installed, fail.
  539. if (g_pGlobalData->hwndMSR)
  540. return FALSE;
  541. // Save off the hwnd to send messages to
  542. g_pGlobalData->hwndMSR = hwnd;
  543. DBPRINTF(TEXT("InitKeys: hwndMSR = 0x%x hwnd = 0x%x\r\n"), g_pGlobalData->hwndMSR, hwnd);
  544. // Get the module handle for this DLL
  545. hModSelf = GetModuleHandle(TEXT("NarrHook.dll"));
  546. if(!hModSelf)
  547. return FALSE;
  548. // Set up the global keyboard hook
  549. g_hhookKey = SetWindowsHookEx(WH_KEYBOARD, // What kind of hook
  550. KeyboardProc,// Proc to send to
  551. hModSelf, // Our Module
  552. 0); // For all threads
  553. // and set up the global mouse hook
  554. g_hhookMouse = SetWindowsHookEx(WH_MOUSE, // What kind of hook
  555. MouseProc, // Proc to send to
  556. hModSelf, // Our Module
  557. 0); // For all threads
  558. // Return TRUE|FALSE based on result
  559. return g_hhookKey != NULL && g_hhookMouse != NULL;
  560. }
  561. /*************************************************************************
  562. Function: UninitKeys
  563. Purpose: Deinstall the hooks
  564. Inputs: void
  565. Returns: BOOL - TRUE if successful
  566. History:
  567. *************************************************************************/
  568. BOOL UninitKeys(void)
  569. {
  570. CScopeMutex csMutex;
  571. if (!csMutex.Create(g_szMutexNarrator, c_nMutexWait))
  572. return FALSE;
  573. // Reset
  574. DBPRINTF(TEXT("UninitKeys setting hwndMSR NULL\r\n"));
  575. g_pGlobalData->hwndMSR = NULL;
  576. // Unhook keyboard if that was hooked
  577. if (g_hhookKey)
  578. {
  579. UnhookWindowsHookEx(g_hhookKey);
  580. g_hhookKey = NULL;
  581. }
  582. // Unhook mouse if that was hooked
  583. if (g_hhookMouse)
  584. {
  585. UnhookWindowsHookEx(g_hhookMouse);
  586. g_hhookMouse = NULL;
  587. }
  588. return TRUE;
  589. }
  590. /*************************************************************************
  591. Function: KeyboardProc
  592. Purpose: Gets called for keys hit
  593. Inputs: void
  594. Returns: BOOL - TRUE if successful
  595. History:
  596. *************************************************************************/
  597. LRESULT CALLBACK KeyboardProc(int code, // hook code
  598. WPARAM wParam, // virtual-key code
  599. LPARAM lParam) // keystroke-message information
  600. {
  601. int state = 0;
  602. int ihotk;
  603. g_pGlobalData->fDoingPassword = FALSE;
  604. if (code == HC_ACTION)
  605. {
  606. // If this is a key up, bail out now.
  607. if (!(lParam & 0x80000000))
  608. {
  609. g_pGlobalData->fMouseUp = TRUE;
  610. g_pGlobalData->nSpeakWindowSoon = FALSE;
  611. g_pGlobalData->fJustHadSpace = FALSE;
  612. if (lParam & 0x20000000)
  613. { // get ALT state
  614. state = MSR_ALT;
  615. SpeakMute(0);
  616. }
  617. if (GetKeyState(VK_SHIFT) < 0)
  618. state |= MSR_SHIFT;
  619. if (GetKeyState(VK_CONTROL) < 0)
  620. state |= MSR_CTRL;
  621. for (ihotk = 0; ihotk < CKEYS_HOT; ihotk++)
  622. {
  623. if ((rgHotKeys[ihotk].keyVal == wParam) &&
  624. (state == rgHotKeys[ihotk].status))
  625. {
  626. // Call the function
  627. SpeakMute(0);
  628. (*rgHotKeys[ihotk].lFunction)(rgHotKeys[ihotk].nOption);
  629. return(1);
  630. }
  631. }
  632. // ROBSI: 10-11-99 -- Work Item: Should be able to use the code in OnFocusChangedEvent
  633. // that sets the fDoingPassword flag, but that means
  634. // changing the handling of StateChangeEvents to prevent
  635. // calling OnFocusChangedEvent. For now, we'll just use
  636. // call GetGUIThreadInfo to determine the focused window
  637. // and then rely on OLEACC to tell us if it is a PWD field.
  638. // ROBSI <begin>
  639. HWND hwndFocus = NULL;
  640. GUITHREADINFO gui;
  641. // Use the foreground thread. If nobody is the foreground, nobody has
  642. // the focus either.
  643. gui.cbSize = sizeof(GUITHREADINFO);
  644. if ( GetGUIThreadInfo(0, &gui) )
  645. {
  646. hwndFocus = gui.hwndFocus;
  647. }
  648. if (hwndFocus != NULL)
  649. {
  650. // Use OLEACC to detect password fields. It turns out to be more
  651. // reliable than SendMessage(GetFocus(), EM_GETPASSWORDCHAR, 0, 0L).
  652. VARIANT varChild;
  653. IAccessible *pIAcc = GetFocusedIAccessibile( hwndFocus, &varChild );
  654. if ( pIAcc )
  655. {
  656. // Test the password bit...
  657. VARIANT varState;
  658. VariantInit(&varState);
  659. HRESULT hr = pIAcc->get_accState(varChild, &varState);
  660. if ((S_OK == hr) && (varState.vt == VT_I4) && (varState.lVal & STATE_SYSTEM_PROTECTED))
  661. {
  662. g_pGlobalData->fDoingPassword = TRUE;
  663. }
  664. pIAcc->Release();
  665. }
  666. // ROBSI: OLEACC does not always properly detect password fields.
  667. // Therefore, we use Win32 as a backup.
  668. if (!g_pGlobalData->fDoingPassword)
  669. {
  670. TCHAR szClassName[256];
  671. // Verify this control is an Edit or RichEdit control to avoid
  672. // sending EM_ messages to random controls.
  673. // POTENTIAL BUG? If login dialog changes to another class, we'll break.
  674. if ( RealGetWindowClass( hwndFocus, szClassName, ARRAYSIZE(szClassName)) )
  675. {
  676. if ((0 == lstrcmp(szClassName, TEXT("Edit"))) ||
  677. (0 == lstrcmp(szClassName, TEXT("RICHEDIT"))) ||
  678. (0 == lstrcmp(szClassName, TEXT("RichEdit20A"))) ||
  679. (0 == lstrcmp(szClassName, TEXT("RichEdit20W")))
  680. )
  681. {
  682. g_pGlobalData->fDoingPassword = (SendMessage(hwndFocus, EM_GETPASSWORDCHAR, 0, 0L) != NULL);
  683. }
  684. }
  685. }
  686. }
  687. // ROBSI <end>
  688. if (g_pGlobalData->fDoingPassword)
  689. {
  690. // ROBSI: 10-11-99
  691. // Go ahead and speak keys that are not printable but will
  692. // help the user understand what state they are in.
  693. switch (wParam)
  694. {
  695. case VK_CAPITAL:
  696. if (g_pGlobalData->nEchoChars & MSR_ECHOMODIFIERS)
  697. {
  698. SpeakMute(0);
  699. if ( GetKeyState(VK_CAPITAL) & 0x0F )
  700. SpeakStringId(IDS_CAPS_ON);
  701. else
  702. SpeakStringId(IDS_CAPS_OFF);
  703. }
  704. break;
  705. case VK_NUMLOCK:
  706. if (g_pGlobalData->nEchoChars & MSR_ECHOMODIFIERS)
  707. {
  708. SpeakMute(0);
  709. if ( GetKeyState(VK_NUMLOCK) & 0x0F )
  710. SpeakStringId(IDS_NUM_ON);
  711. else
  712. SpeakStringId(IDS_NUM_OFF);
  713. }
  714. break;
  715. case VK_DELETE:
  716. if (g_pGlobalData->nEchoChars & MSR_ECHODELETE)
  717. {
  718. SpeakMute(0);
  719. SpeakStringId(IDS_DELETE);
  720. }
  721. break;
  722. case VK_INSERT:
  723. if (g_pGlobalData->nEchoChars & MSR_ECHODELETE)
  724. {
  725. SpeakMute(0);
  726. SpeakStringId(IDS_INSERT);
  727. }
  728. break;
  729. case VK_BACK:
  730. if (g_pGlobalData->nEchoChars & MSR_ECHOBACK)
  731. {
  732. SpeakMute(0);
  733. SpeakStringId(IDS_BACKSPACE);
  734. }
  735. break;
  736. case VK_TAB:
  737. SpeakMute(0);
  738. if (g_pGlobalData->nEchoChars & MSR_ECHOTAB)
  739. SpeakStringId(IDS_TAB);
  740. break;
  741. case VK_CONTROL:
  742. SpeakMute(0); // always mute when control held down!
  743. if ((g_pGlobalData->nEchoChars & MSR_ECHOMODIFIERS) && !(g_pGlobalData->fJustHadShiftKeys & MSR_CTRL))
  744. {
  745. SpeakStringId(IDS_CONTROL);
  746. // ROBSI: Commenting out to avoid modifying Global State
  747. // g_pGlobalData->fJustHadShiftKeys |= MSR_CTRL;
  748. }
  749. break;
  750. default:
  751. SpeakMute(0);
  752. SpeakStringId(IDS_PASS);
  753. break;
  754. }
  755. return (CallNextHookEx(g_hhookKey, code, wParam, lParam));
  756. }
  757. TCHAR buff[20];
  758. BYTE KeyState[256];
  759. UINT ScanCode;
  760. GetKeyboardState(KeyState);
  761. if ((g_pGlobalData->nEchoChars & MSR_ECHOALNUM) &&
  762. (ScanCode = MapVirtualKeyEx((UINT)wParam, 2,GetKeyboardLayout(0))))
  763. {
  764. #ifdef UNICODE
  765. ToUnicode((UINT)wParam,ScanCode,KeyState, buff,10,0);
  766. #else
  767. ToAscii((UINT)wParam,ScanCode,KeyState,(unsigned short *)buff,0);
  768. #endif
  769. // Use 'GetStringTypeEx()' instead of _istprint()
  770. buff[1] = 0;
  771. WORD wCharType;
  772. WORD fPrintable = C1_UPPER|C1_LOWER|C1_DIGIT|C1_SPACE|C1_PUNCT|C1_BLANK|C1_XDIGIT|C1_ALPHA;
  773. GetStringTypeEx(LOCALE_USER_DEFAULT, CT_CTYPE1, buff, 1, &wCharType);
  774. if (wCharType & fPrintable)
  775. {
  776. SpeakMute(0);
  777. SpeakStringAll(buff);
  778. }
  779. }
  780. // All new: Add speech for all keys...AK
  781. switch (wParam) {
  782. case VK_SPACE:
  783. g_pGlobalData->fJustHadSpace = TRUE;
  784. if (g_pGlobalData->nEchoChars & MSR_ECHOSPACE)
  785. {
  786. SpeakMute(0);
  787. SpeakStringId(IDS_SPACE);
  788. }
  789. break;
  790. case VK_LWIN:
  791. case VK_RWIN:
  792. if (g_pGlobalData->nEchoChars & MSR_ECHOMODIFIERS)
  793. {
  794. SpeakMute(0);
  795. g_pGlobalData->fStartPressed = TRUE;
  796. SpeakStringId(IDS_WINKEY);
  797. }
  798. break;
  799. case VK_CAPITAL:
  800. if (g_pGlobalData->nEchoChars & MSR_ECHOMODIFIERS)
  801. {
  802. SpeakMute(0);
  803. if ( GetKeyState(VK_CAPITAL) & 0x0F )
  804. SpeakStringId(IDS_CAPS_ON);
  805. else
  806. SpeakStringId(IDS_CAPS_OFF);
  807. }
  808. break;
  809. case VK_SNAPSHOT:
  810. if (g_pGlobalData->nEchoChars & MSR_ECHOMODIFIERS)
  811. {
  812. SpeakMute(0);
  813. SpeakStringId(IDS_PRINT);
  814. }
  815. break;
  816. case VK_ESCAPE:
  817. if (g_pGlobalData->nEchoChars & MSR_ECHOMODIFIERS)
  818. {
  819. SpeakMute(0);
  820. SpeakStringId(IDS_ESC);
  821. }
  822. break;
  823. case VK_NUMLOCK:
  824. if (g_pGlobalData->nEchoChars & MSR_ECHOMODIFIERS)
  825. {
  826. SpeakMute(0);
  827. if ( GetKeyState(VK_NUMLOCK) & 0x0F )
  828. SpeakStringId(IDS_NUM_ON);
  829. else
  830. SpeakStringId(IDS_NUM_OFF);
  831. }
  832. break;
  833. case VK_DELETE:
  834. if (g_pGlobalData->nEchoChars & MSR_ECHODELETE)
  835. {
  836. SpeakMute(0);
  837. SpeakStringId(IDS_DELETE);
  838. }
  839. break;
  840. case VK_INSERT:
  841. if (g_pGlobalData->nEchoChars & MSR_ECHODELETE)
  842. {
  843. SpeakMute(0);
  844. SpeakStringId(IDS_INSERT);
  845. }
  846. break;
  847. case VK_HOME:
  848. if (g_pGlobalData->nEchoChars & MSR_ECHODELETE)
  849. {
  850. SpeakMute(0);
  851. SpeakStringId(IDS_HOME);
  852. }
  853. break;
  854. case VK_END:
  855. if (g_pGlobalData->nEchoChars & MSR_ECHODELETE)
  856. {
  857. SpeakMute(0);
  858. SpeakStringId(IDS_END);
  859. }
  860. break;
  861. case VK_PRIOR:
  862. if (g_pGlobalData->nEchoChars & MSR_ECHODELETE)
  863. {
  864. SpeakMute(0);
  865. SpeakStringId(IDS_PAGEUP);
  866. }
  867. break;
  868. case VK_NEXT:
  869. if (g_pGlobalData->nEchoChars & MSR_ECHODELETE)
  870. {
  871. SpeakMute(0);
  872. SpeakStringId(IDS_PAGEDOWN);
  873. }
  874. break;
  875. case VK_BACK:
  876. if (g_pGlobalData->nEchoChars & MSR_ECHOBACK)
  877. {
  878. SpeakMute(0);
  879. SpeakStringId(IDS_BACKSPACE);
  880. }
  881. break;
  882. case VK_TAB:
  883. SpeakMute(0);
  884. if (g_pGlobalData->nEchoChars & MSR_ECHOTAB)
  885. SpeakStringId(IDS_TAB);
  886. break;
  887. case VK_CONTROL:
  888. SpeakMute(0); // always mute when control held down!
  889. if ((g_pGlobalData->nEchoChars & MSR_ECHOMODIFIERS) && !(g_pGlobalData->fJustHadShiftKeys & MSR_CTRL))
  890. {
  891. SpeakStringId(IDS_CONTROL);
  892. g_pGlobalData->fJustHadShiftKeys |= MSR_CTRL;
  893. }
  894. break;
  895. case VK_MENU:
  896. if ((g_pGlobalData->nEchoChars & MSR_ECHOMODIFIERS) && !(g_pGlobalData->fJustHadShiftKeys & MSR_ALT))
  897. {
  898. SpeakMute(0);
  899. SpeakStringId(IDS_ALT);
  900. }
  901. break;
  902. case VK_SHIFT:
  903. if ((g_pGlobalData->nEchoChars & MSR_ECHOMODIFIERS) && !(g_pGlobalData->fJustHadShiftKeys & MSR_SHIFT))
  904. {
  905. SpeakMute(0);
  906. SpeakStringId(IDS_SHIFT);
  907. g_pGlobalData->fJustHadShiftKeys |= MSR_SHIFT;
  908. }
  909. break;
  910. case VK_RETURN:
  911. if (g_pGlobalData->nEchoChars & MSR_ECHOENTER)
  912. {
  913. SpeakMute(0);
  914. SpeakStringId(IDS_RETURN);
  915. }
  916. break;
  917. }
  918. // set flags for moving around edit controls
  919. g_pGlobalData->nMsrDoNext = MSR_DONOWT;
  920. if (state == MSR_CTRL && (wParam == VK_LEFT || wParam == VK_RIGHT))
  921. {
  922. SpeakMute(0);
  923. g_pGlobalData->nMsrDoNext = MSR_DOWORD;
  924. }
  925. else if ((state & MSR_CTRL) && (state & MSR_SHIFT) && (wParam == VK_LEFT))
  926. g_pGlobalData->nMsrDoNext = MSR_DOWORD;
  927. else if ((state & MSR_CTRL) && (state & MSR_SHIFT) && (wParam == VK_RIGHT))
  928. g_pGlobalData->nMsrDoNext = MSR_DOWORDR;
  929. else if ((state & MSR_SHIFT) && (wParam == VK_LEFT))
  930. g_pGlobalData->nMsrDoNext = MSR_DOCHAR;
  931. else if ((state & MSR_SHIFT) && (wParam == VK_RIGHT))
  932. g_pGlobalData->nMsrDoNext = MSR_DOCHARR;
  933. else if ((state & MSR_CTRL) && (wParam == VK_UP || wParam == VK_DOWN))
  934. g_pGlobalData->nMsrDoNext = MSR_DOLINE;
  935. else if ((state & MSR_SHIFT) && (wParam == VK_UP))
  936. g_pGlobalData->nMsrDoNext = MSR_DOLINE;
  937. else if ((state & MSR_SHIFT) && (wParam == VK_DOWN))
  938. g_pGlobalData->nMsrDoNext = MSR_DOLINED;
  939. else if (state == 0)
  940. { // i.e. no shift keys
  941. switch (wParam)
  942. {
  943. case VK_LEFT:
  944. case VK_RIGHT:
  945. g_pGlobalData->nMsrDoNext = MSR_DOCHAR;
  946. SpeakMute(0);
  947. break;
  948. case VK_DOWN:
  949. case VK_UP:
  950. g_pGlobalData->nMsrDoNext = MSR_DOLINE;
  951. SpeakMute(0);
  952. break;
  953. case VK_F3:
  954. if (GetForegroundWindow() == g_pGlobalData->hwndMSR)
  955. {
  956. PostMessage(g_pGlobalData->hwndMSR, WM_MSRCONFIGURE, 0, 0);
  957. return(1);
  958. }
  959. break;
  960. case VK_F9:
  961. if (GetForegroundWindow() == g_pGlobalData->hwndMSR)
  962. {
  963. PostMessage(g_pGlobalData->hwndMSR, WM_MSRQUIT, 0, 0);
  964. return(1);
  965. }
  966. break;
  967. } // end switch wParam (keycode)
  968. } // end if no shift keys pressed
  969. } // end if key down
  970. } // end if code == HC_ACTION
  971. g_pGlobalData->fJustHadShiftKeys = state;
  972. return (CallNextHookEx(g_hhookKey, code, wParam, lParam));
  973. }
  974. /*************************************************************************
  975. Function: MouseProc
  976. Purpose: Gets called for mouse eventshit
  977. Inputs: void
  978. Returns: BOOL - TRUE if successful
  979. History:
  980. *************************************************************************/
  981. LRESULT CALLBACK MouseProc(int code, // hook code
  982. WPARAM wParam, // virtual-key code
  983. LPARAM lParam) // keystroke-message information
  984. {
  985. CScopeMutex csMutex;
  986. if (!csMutex.Create(g_szMutexNarrator, c_nMutexWait))
  987. return 1; // TODO not sure what to do here; MSDN is unclear about retval
  988. LRESULT retVal = CallNextHookEx(g_hhookMouse, code, wParam, lParam);
  989. if (code == HC_ACTION)
  990. {
  991. switch (wParam)
  992. { // want to know if mouse is down
  993. case WM_NCLBUTTONDOWN:
  994. case WM_LBUTTONDOWN:
  995. case WM_NCRBUTTONDOWN:
  996. case WM_RBUTTONDOWN:
  997. // to keep sighted people happy when using mouse shut up
  998. // the speech on mouse down
  999. // SpeakMute(0);
  1000. // Chnage to PostMessage works for now: a-anilk
  1001. PostMessage(g_pGlobalData->hwndMSR, WM_MUTE, 0, 0);
  1002. // If it is then don't move mouse pointer when focus set!
  1003. g_pGlobalData->fMouseUp = FALSE;
  1004. break;
  1005. case WM_NCLBUTTONUP:
  1006. case WM_LBUTTONUP:
  1007. case WM_NCRBUTTONUP:
  1008. case WM_RBUTTONUP:
  1009. // g_pGlobalData->fMouseUp = TRUE; Don't clear flag here - wait until key pressed before enabling auto mouse movemens again
  1010. break;
  1011. }
  1012. }
  1013. return(retVal);
  1014. }
  1015. // --------------------------------------------------------------------------
  1016. //
  1017. // Entry point: DllMain()
  1018. //
  1019. // Some stuff only needs to be done the first time the DLL is loaded, and the
  1020. // last time it is unloaded, which is to set up the values for things in the
  1021. // shared data segment, including SharedMemory support.
  1022. //
  1023. // InterlockedIncrement() and Decrement() return 1 if the result is
  1024. // positive, 0 if zero, and -1 if negative. Therefore, the only
  1025. // way to use them practically is to start with a counter at -1.
  1026. // Then incrementing from -1 to 0, the first time, will give you
  1027. // a unique value of 0. And decrementing the last time from 0 to -1
  1028. // will give you a unique value of -1.
  1029. //
  1030. // --------------------------------------------------------------------------
  1031. BOOL WINAPI DllMain(HINSTANCE hinst, DWORD dwReason, LPVOID pvReserved)
  1032. {
  1033. switch (dwReason)
  1034. {
  1035. case DLL_PROCESS_ATTACH:
  1036. g_Hinst = hinst;
  1037. // Create the memory mapped file for shared global data
  1038. CreateMappedFile();
  1039. break;
  1040. case DLL_PROCESS_DETACH:
  1041. // Close the memory mapped file for shared global data
  1042. CloseMappedFile();
  1043. break;
  1044. }
  1045. return(TRUE);
  1046. }
  1047. /*************************************************************************
  1048. Function: WinEventProc
  1049. Purpose: Callback function handles events
  1050. Inputs: HWINEVENTHOOK hEvent - Handle of the instance of the event proc
  1051. DWORD event - Event type constant
  1052. HWND hwndMsg - HWND of window generating event
  1053. LONG idObject - ID of object generating event
  1054. LONG idChild - ID of child generating event (0 if object)
  1055. DWORD idThread - ID of thread generating event
  1056. DWORD dwmsEventTime - Timestamp of event
  1057. Returns:
  1058. History:
  1059. *************************************************************************/
  1060. void CALLBACK WinEventProc(HWINEVENTHOOK hEvent, DWORD event, HWND hwndMsg,
  1061. LONG idObject, LONG idChild, DWORD idThread,
  1062. DWORD dwmsEventTime)
  1063. {
  1064. // NOTE: If any more events are handled by ProcessWinEvent, they must be
  1065. // added to this switch statement.
  1066. // no longer will we get an IAccessible here - the helper thread will
  1067. // get the info from the Stack and get and use the IAccessible there.
  1068. switch (event)
  1069. {
  1070. case EVENT_OBJECT_STATECHANGE:
  1071. case EVENT_OBJECT_VALUECHANGE:
  1072. case EVENT_OBJECT_SELECTION:
  1073. case EVENT_OBJECT_FOCUS:
  1074. case EVENT_OBJECT_LOCATIONCHANGE:
  1075. case EVENT_SYSTEM_MENUSTART:
  1076. case EVENT_SYSTEM_MENUEND:
  1077. case EVENT_SYSTEM_MENUPOPUPSTART:
  1078. case EVENT_SYSTEM_MENUPOPUPEND:
  1079. case EVENT_SYSTEM_SWITCHSTART:
  1080. case EVENT_SYSTEM_FOREGROUND:
  1081. case EVENT_OBJECT_SHOW:
  1082. AddEventInfoToStack(event, hwndMsg, idObject, idChild,
  1083. idThread, dwmsEventTime);
  1084. break;
  1085. } // end switch (event)
  1086. }
  1087. /*************************************************************************
  1088. Function:
  1089. Purpose:
  1090. Inputs:
  1091. Returns:
  1092. History:
  1093. *************************************************************************/
  1094. void ProcessWinEvent(DWORD event, HWND hwndMsg, LONG idObject, LONG
  1095. idChild, DWORD idThread,DWORD dwmsEventTime)
  1096. {
  1097. TCHAR szName[256];
  1098. // What type of event is coming through?
  1099. // bring secondary focus here: Get from Object inspector
  1100. // bring mouse pointer here if flag set.
  1101. if (g_pGlobalData->nReviewLevel != 2)
  1102. {
  1103. switch (event)
  1104. {
  1105. case EVENT_SYSTEM_SWITCHSTART:
  1106. SpeakMute(0);
  1107. SpeakString(TEXT("ALT TAB"));
  1108. break;
  1109. case EVENT_SYSTEM_MENUSTART:
  1110. break;
  1111. case EVENT_SYSTEM_MENUEND:
  1112. SpeakMute(0);
  1113. if (g_pGlobalData->fAnnounceMenu)
  1114. SpeakStringId(IDS_MENUEND);
  1115. break;
  1116. case EVENT_SYSTEM_MENUPOPUPSTART:
  1117. if (g_pGlobalData->fAnnouncePopup)
  1118. {
  1119. SpeakMute(0);
  1120. SpeakStringId(IDS_POPUP);
  1121. }
  1122. break;
  1123. case EVENT_SYSTEM_MENUPOPUPEND:
  1124. SpeakMute(0);
  1125. if (g_pGlobalData->fAnnouncePopup)
  1126. SpeakStringId(IDS_POPUPEND);
  1127. break;
  1128. case EVENT_OBJECT_STATECHANGE :
  1129. DBPRINTF(TEXT("EVENT_OBJECT_STATECHANGE\r\n"));
  1130. // want to catch state changes on spacebar pressed
  1131. switch (g_pGlobalData->fJustHadSpace)
  1132. {
  1133. case 0 : // get out - only do this code if space just been pressed
  1134. break;
  1135. case 1 :
  1136. case 2 : // ignore the first and second time round!
  1137. g_pGlobalData->fJustHadSpace++;
  1138. break;
  1139. case 3 : // second time around speak the item
  1140. OnFocusChangedEvent(event, hwndMsg, idObject, idChild, dwmsEventTime);
  1141. g_pGlobalData->fJustHadSpace = 0;
  1142. break;
  1143. }
  1144. OnStateChangedEvent(event, hwndMsg, idObject, idChild, dwmsEventTime);
  1145. break;
  1146. case EVENT_OBJECT_VALUECHANGE :
  1147. OnValueChangedEvent(event, hwndMsg, idObject, idChild, dwmsEventTime);
  1148. break;
  1149. case EVENT_OBJECT_SELECTION :
  1150. if (GetParent(hwndMsg) == g_pGlobalData->hwndMSR)
  1151. {
  1152. // don't do this for our own or list box throws a wobbler!
  1153. break;
  1154. }
  1155. // this comes in for list items a second time after the focus
  1156. // changes BUT that gets filtered by the double speak check.
  1157. // What this catches is list item changes when cursor down in
  1158. // combo boxes!
  1159. // Make it just works for them.
  1160. OnSelectChangedEvent(event, hwndMsg, idObject, idChild, dwmsEventTime);
  1161. break;
  1162. case EVENT_OBJECT_FOCUS:
  1163. DBPRINTF(TEXT("EVENT_OBJECT_FOCUS\r\n"));
  1164. OnFocusChangedEvent(event, hwndMsg, idObject, idChild, dwmsEventTime);
  1165. break;
  1166. case EVENT_SYSTEM_FOREGROUND: // Window comes to front - speak its name!
  1167. SpeakMute(0);
  1168. SpeakStringId(IDS_FOREGROUND);
  1169. TCHAR szClassName[100];
  1170. // if the class name is CLASS_MS_WINNOTE or CLASS_HH_POPUP it's context senceitive help
  1171. // and the text will be read in OnFocusChangeEvent by SpeakObjectInfo. So we don't need to
  1172. // read the same text here and in SpeakWindow
  1173. GetClassName( hwndMsg, szClassName, ARRAYSIZE(szClassName) );
  1174. if ( (lstrcmpi(szClassName, CLASS_MS_WINNOTE ) == 0) || (lstrcmpi(szClassName, CLASS_HH_POPUP ) == 0) )
  1175. break;
  1176. GetWindowText(hwndMsg, szName, sizeof(szName)/sizeof(TCHAR)); // raid #113789
  1177. SpeakString(szName);
  1178. if (g_pGlobalData->fAnnounceWindow)
  1179. {
  1180. g_pGlobalData->nSpeakWindowSoon = TRUE; // read window when next focus set
  1181. }
  1182. break;
  1183. case EVENT_OBJECT_LOCATIONCHANGE:
  1184. // Only the caret
  1185. if (idObject != OBJID_CARET)
  1186. return;
  1187. OnLocationChangedEvent(event, hwndMsg, idObject, idChild, dwmsEventTime);
  1188. break;
  1189. case EVENT_OBJECT_SHOW:
  1190. OnObjectShowEvent(event, hwndMsg, idObject, idChild, dwmsEventTime);
  1191. break;
  1192. } // end switch (event)
  1193. } // end if review level != 2
  1194. return;
  1195. }
  1196. /*************************************************************************
  1197. Function: OnValueChangedEvent
  1198. Purpose: Receives value events
  1199. Inputs: DWORD event - What event are we processing
  1200. HWND hwnd - HWND of window generating event
  1201. LONG idObject - ID of object generating event
  1202. LONG idChild - ID of child generating event (0 if object)
  1203. DWORD idThread - ID of thread generating event
  1204. DWORD dwmsEventTime - Timestamp of event
  1205. Returns: BOOL - TRUE if succeeded
  1206. *************************************************************************/
  1207. BOOL OnValueChangedEvent(DWORD event, HWND hwnd, LONG idObject, LONG idChild,
  1208. DWORD dwmsTimeStamp)
  1209. {
  1210. HRESULT hr;
  1211. OBJINFO objCurrent;
  1212. VARIANT varRole;
  1213. IAccessible* pIAcc;
  1214. VARIANT varChild;
  1215. TCHAR szName[200];
  1216. hr = AccessibleObjectFromEvent (hwnd, idObject, idChild, &pIAcc, &varChild);
  1217. if (SUCCEEDED(hr))
  1218. {
  1219. objCurrent.hwnd = hwnd;
  1220. objCurrent.plObj = (long*)pIAcc;
  1221. objCurrent.varChild = varChild;
  1222. VariantInit(&varRole);
  1223. hr = pIAcc->get_accRole(varChild, &varRole);
  1224. if( FAILED(hr) ||
  1225. varRole.lVal != ROLE_SYSTEM_SPINBUTTON && g_pGlobalData->nMsrDoNext == MSR_DONOWT)
  1226. {
  1227. pIAcc->Release();
  1228. return(FALSE);
  1229. }
  1230. g_pGlobalData->nMsrDoNext = MSR_DONOWT; // PB 22 Nov 1998 stop this firing more than once (probably)
  1231. if (varRole.vt == VT_I4 && (
  1232. (varRole.lVal == ROLE_SYSTEM_TEXT && g_pGlobalData->nMsrDoNext != MSR_DOLINE) ||
  1233. varRole.lVal == ROLE_SYSTEM_PUSHBUTTON ||
  1234. varRole.lVal == ROLE_SYSTEM_SCROLLBAR))
  1235. {
  1236. DBPRINTF (TEXT("Don't Speak <%s>\r\n"), szName);
  1237. // don't speak 'cos it's an edit box (or others) changing value!
  1238. }
  1239. else if (!g_pGlobalData->fInternetExplorer) // don't do this for IE .. it speaks edit box too much.
  1240. {
  1241. DBPRINTF (TEXT("Now Speak!\r\n"));
  1242. SpeakMute(0);
  1243. SpeakObjectInfo(&objCurrent, FALSE);
  1244. }
  1245. else
  1246. DBPRINTF (TEXT("Do nowt!\r\n"));
  1247. pIAcc->Release();
  1248. }
  1249. return(TRUE);
  1250. }
  1251. /*************************************************************************
  1252. Function: OnSelectChangedEvent
  1253. Purpose: Receives selection change events - not from MSR though
  1254. Inputs: DWORD event - What event are we processing
  1255. HWND hwnd - HWND of window generating event
  1256. LONG idObject - ID of object generating event
  1257. LONG idChild - ID of child generating event (0 if object)
  1258. DWORD idThread - ID of thread generating event
  1259. DWORD dwmsEventTime - Timestamp of event
  1260. Returns: BOOL - TRUE if succeeded
  1261. Notes: Maybe change this to only take combo-boxes?
  1262. *************************************************************************/
  1263. BOOL OnSelectChangedEvent(DWORD event, HWND hwnd, LONG idObject, LONG idChild,
  1264. DWORD dwmsTimeStamp)
  1265. {
  1266. HRESULT hr;
  1267. IAccessible* pIAcc;
  1268. OBJINFO objCurrent;
  1269. VARIANT varRole;
  1270. VARIANT varChild;
  1271. // if we've not had a cursor style movement then sack this as it could be
  1272. // scroll bar chaging or slider moving etc to reflect rapidy moving events
  1273. hr = AccessibleObjectFromEvent (hwnd, idObject, idChild, &pIAcc, &varChild);
  1274. if (SUCCEEDED(hr))
  1275. {
  1276. objCurrent.hwnd = hwnd;
  1277. objCurrent.plObj = (long*)pIAcc;
  1278. objCurrent.varChild = varChild;
  1279. VariantInit(&varRole); // heuristic!
  1280. hr = pIAcc->get_accRole(varChild, &varRole);
  1281. if ( FAILED(hr) )
  1282. {
  1283. pIAcc->Release();
  1284. return FALSE;
  1285. }
  1286. if (varRole.vt == VT_I4 &&
  1287. varRole.lVal == ROLE_SYSTEM_LISTITEM)
  1288. {
  1289. TCHAR buffer[100];
  1290. GetClassName(hwnd,buffer,100); // Is it sysListView32
  1291. // "Don't mute here ... we lose the previous speech message which will
  1292. // have spoken the list item IF we were cursoring to list item.
  1293. // SpeakMute(0);
  1294. // don't speak unless it's a listitem
  1295. // e.g. Current Selection for Joystick from Joystick setup.
  1296. // this does mean that some list items get spoken twice!:AK
  1297. // if ( lstrcmpi(buffer, CLASS_LISTVIEW) != 0)
  1298. if ( !g_pGlobalData->fListFocus )
  1299. SpeakObjectInfo(&objCurrent,FALSE);
  1300. g_pGlobalData->fListFocus = FALSE;
  1301. }
  1302. pIAcc->Release();
  1303. }
  1304. return(TRUE);
  1305. }
  1306. /*************************************************************************
  1307. Function: OnFocusChangedEvent
  1308. Purpose: Receives focus events
  1309. Inputs: DWORD event - What event are we processing
  1310. HWND hwnd - HWND of window generating event
  1311. LONG idObject - ID of object generating event
  1312. LONG idChild - ID of child generating event (0 if object)
  1313. DWORD idThread - ID of thread generating event
  1314. DWORD dwmsEventTime - Timestamp of event
  1315. Returns: BOOL - TRUE if succeeded
  1316. *************************************************************************/
  1317. BOOL OnFocusChangedEvent(DWORD event, HWND hwnd, LONG idObject,
  1318. LONG idChild, DWORD dwmsTimeStamp)
  1319. {
  1320. HRESULT hr;
  1321. TCHAR szName[256];
  1322. TCHAR buffer[100];
  1323. IAccessible* pIAcc;
  1324. VARIANT varChild;
  1325. VARIANT varRole;
  1326. VARIANT varState;
  1327. BOOL switchWnd = FALSE;
  1328. hr = AccessibleObjectFromEvent (hwnd, idObject, idChild, &pIAcc, &varChild);
  1329. if (FAILED(hr))
  1330. return FALSE;
  1331. // Check for Bogus events...
  1332. if( !IsFocussedItem(hwnd, pIAcc, varChild) )
  1333. {
  1334. pIAcc->Release();
  1335. return FALSE;
  1336. }
  1337. // Ignore the first Start pressed events...
  1338. if ( g_pGlobalData->fStartPressed )
  1339. {
  1340. g_pGlobalData->fStartPressed = FALSE;
  1341. pIAcc->Release();
  1342. return FALSE;
  1343. }
  1344. g_pGlobalData->fDoingPassword = FALSE;
  1345. // Have we got a password char in this one
  1346. // if so then tell them and get out
  1347. VariantInit(&varState);
  1348. hr = pIAcc->get_accState(varChild, &varState);
  1349. if ( FAILED(hr) )
  1350. {
  1351. pIAcc->Release();
  1352. return FALSE;
  1353. }
  1354. if ( varState.vt == VT_EMPTY )
  1355. varState.lVal = 0;
  1356. g_pGlobalData->fDoingPassword = (varState.lVal & STATE_SYSTEM_PROTECTED);
  1357. GetClassName(hwnd,buffer,100); // is it Internet Explorer in any of its many forms?
  1358. DBPRINTF(TEXT("OnFocusChangedEvent: class name = %s\r\n"), buffer);
  1359. g_pGlobalData->fInternetExplorer = IsTridentWindow(buffer);
  1360. g_pGlobalData->fHTML_Help = FALSE;
  1361. if (lstrcmpi(buffer, CLASS_WINSWITCH) == 0)
  1362. switchWnd = TRUE;
  1363. GetClassName(GetForegroundWindow(),buffer,100);
  1364. if ((lstrcmpi(buffer, CLASS_HTMLHELP) == 0)|| (lstrcmpi(buffer, CLASS_IE_FRAME) == 0) ) { // have we got HTML Help?
  1365. g_pGlobalData->fInternetExplorer = TRUE;
  1366. g_pGlobalData->fHTML_Help = TRUE;
  1367. }
  1368. // Check to see if we are getting rapid focus changes
  1369. // Consider using the Time stamp and saving away the last object
  1370. VariantInit(&varRole);
  1371. // If the focus is being set to a list, a combo, or a dialog,
  1372. // don't say anything. We'll say something when the focus gets
  1373. // set to one of the children.
  1374. hr = pIAcc->get_accRole(varChild, &varRole); // heuristic!
  1375. if ( FAILED(hr) )
  1376. {
  1377. pIAcc->Release();
  1378. return FALSE;
  1379. }
  1380. // Special casing stuff.. Avoid repeatation for list items...
  1381. // Required to correctly process Auto suggest list boxes.
  1382. // As list items also send SelectionChange : AK
  1383. if (varRole.vt == VT_I4 )
  1384. {
  1385. switch ( varRole.lVal )
  1386. {
  1387. case ROLE_SYSTEM_DIALOG:
  1388. pIAcc->Release();
  1389. return FALSE;
  1390. break;
  1391. case ROLE_SYSTEM_TITLEBAR:
  1392. g_pGlobalData->fMouseUp = FALSE;
  1393. break;
  1394. case ROLE_SYSTEM_LISTITEM:
  1395. g_pGlobalData->fListFocus = TRUE;
  1396. break;
  1397. default:
  1398. break;
  1399. }
  1400. }
  1401. if (idObject == OBJID_WINDOW)
  1402. {
  1403. SpeakMute(0);
  1404. SpeakStringId(IDS_WINDOW);
  1405. GetWindowText(hwnd, szName, sizeof(szName)/sizeof(TCHAR)); // raid #113789
  1406. SpeakString(szName);
  1407. }
  1408. RECT rcCursor;
  1409. if ( pIAcc->accLocation(&rcCursor.left, &rcCursor.top, &rcCursor.right, &rcCursor.bottom, varChild) == S_OK )
  1410. {
  1411. const POINT ptLoc = { rcCursor.left + (rcCursor.right/2), rcCursor.top + (rcCursor.bottom/2) };
  1412. if (g_pGlobalData->fTrackInputFocus && g_pGlobalData->fMouseUp)
  1413. {
  1414. POINT CursorPosition;
  1415. GetCursorPos(&CursorPosition);
  1416. // mouse to follow if it's not already in rectangle
  1417. // (e.g manually moving mouse in menu) and mouse button up
  1418. if (CursorPosition.x < rcCursor.left
  1419. || CursorPosition.x > (rcCursor.left+rcCursor.right)
  1420. || CursorPosition.y < rcCursor.top
  1421. || CursorPosition.y > (rcCursor.top+rcCursor.bottom))
  1422. {
  1423. g_ptMoveCursor.x = ptLoc.x;
  1424. g_ptMoveCursor.y = ptLoc.y;
  1425. // If we set the cursor immediately extraneous events the
  1426. // hovering on menu items causes feed back which results
  1427. // in the cursor going back and forth between menu items.
  1428. // This code sets a timer so that the cursor is set after things settle down
  1429. if ( g_uTimer == 0 )
  1430. g_uTimer = SetTimer( NULL, 0, 100, TrackCursor );
  1431. // If the focus events are from cursor movement this will ignore the extra
  1432. // event that cause the feed back
  1433. if ( g_pGlobalData->nMsrDoNext != MSR_DONOWT )
  1434. g_pGlobalData->fMouseUp = FALSE;
  1435. }
  1436. }
  1437. else
  1438. {
  1439. SetSecondary(ptLoc.x, ptLoc.y, FALSE);
  1440. }
  1441. }
  1442. OBJINFO objCurrent;
  1443. objCurrent.hwnd = hwnd;
  1444. objCurrent.plObj = (long*)pIAcc;
  1445. objCurrent.varChild = varChild;
  1446. // If the event is from the switch window,
  1447. // Then mute the current speech before proceeding...AK
  1448. if ( switchWnd && g_pGlobalData->fListFocus )
  1449. SpeakMute(0);
  1450. DBPRINTF(TEXT("OnFocusChangedEvent: Calling SpeakObjectInfo...\r\n"));
  1451. SpeakObjectInfo(&objCurrent,TRUE);
  1452. if (g_pGlobalData->fDoingPassword)
  1453. {
  1454. pIAcc->Release();
  1455. return FALSE;
  1456. }
  1457. if (g_pGlobalData->nSpeakWindowSoon)
  1458. {
  1459. DBPRINTF(TEXT("OnFocusChangedEvent: Calling SpeakWindow\r\n"));
  1460. SpeakWindow(0);
  1461. g_pGlobalData->nSpeakWindowSoon = FALSE;
  1462. }
  1463. pIAcc->Release();
  1464. return TRUE;
  1465. }
  1466. /*************************************************************************
  1467. Function: OnStateChangedEvent
  1468. Purpose: Receives focus events
  1469. Inputs: DWORD event - What event are we processing
  1470. HWND hwnd - HWND of window generating event
  1471. LONG idObject - ID of object generating event
  1472. LONG idChild - ID of child generating event (0 if object)
  1473. DWORD idThread - ID of thread generating event
  1474. DWORD dwmsEventTime - Timestamp of event
  1475. Returns: BOOL - TRUE if succeeded
  1476. *************************************************************************/
  1477. BOOL OnStateChangedEvent(DWORD event, HWND hwnd, LONG idObject,
  1478. LONG idChild, DWORD dwmsTimeStamp)
  1479. {
  1480. HRESULT hr;
  1481. IAccessible* pIAcc;
  1482. VARIANT varChild;
  1483. VARIANT varRole;
  1484. hr = AccessibleObjectFromEvent (hwnd, idObject, idChild, &pIAcc, &varChild);
  1485. if (FAILED(hr))
  1486. return (FALSE);
  1487. // Check for Bogus events...
  1488. if( !IsFocussedItem(hwnd, pIAcc, varChild) )
  1489. {
  1490. pIAcc->Release();
  1491. return (FALSE);
  1492. }
  1493. VariantInit(&varRole);
  1494. hr = pIAcc->get_accRole(varChild, &varRole);
  1495. if ( FAILED(hr) )
  1496. {
  1497. pIAcc->Release();
  1498. return FALSE;
  1499. }
  1500. // Special casing stuff.. Handle State change for
  1501. // Outline items only for now
  1502. if (varRole.vt == VT_I4 )
  1503. {
  1504. switch ( varRole.lVal )
  1505. {
  1506. case ROLE_SYSTEM_OUTLINEITEM:
  1507. {
  1508. OBJINFO objCurrent;
  1509. objCurrent.hwnd = hwnd;
  1510. objCurrent.plObj = (long*)pIAcc;
  1511. objCurrent.varChild = varChild;
  1512. SpeakObjectInfo(&objCurrent,TRUE);
  1513. }
  1514. break;
  1515. default:
  1516. break;
  1517. }
  1518. }
  1519. pIAcc->Release();
  1520. return(TRUE);
  1521. }
  1522. /*************************************************************************
  1523. Function: OnLocationChangedEvent
  1524. Purpose: Receives location change events - for the caret
  1525. Inputs: DWORD event - What event are we processing
  1526. HWND hwnd - HWND of window generating event
  1527. LONG idObject - ID of object generating event
  1528. LONG idChild - ID of child generating event (0 if object)
  1529. DWORD idThread - ID of thread generating event
  1530. DWORD dwmsEventTime - Timestamp of event
  1531. Returns: BOOL - TRUE if succeeded
  1532. *************************************************************************/
  1533. BOOL OnLocationChangedEvent(DWORD event, HWND hwnd, LONG idObject,
  1534. LONG idChild, DWORD dwmsTimeStamp)
  1535. {
  1536. //
  1537. // Get the caret position and save it.
  1538. //
  1539. // flag set by key down code - here do appropriate action after
  1540. // caret has moved
  1541. if (g_pGlobalData->nMsrDoNext)
  1542. { // read char, word etc.
  1543. WORD wLineNumber;
  1544. WORD wLineIndex;
  1545. WORD wLineLength;
  1546. DWORD dwGetSel;
  1547. DWORD wStart;
  1548. DWORD wEnd;
  1549. WORD wColNumber;
  1550. WORD wEndWord;
  1551. LPTSTR pszTextShared;
  1552. HANDLE hProcess;
  1553. int nSomeInt;
  1554. int *p; // PB 22 Nov 1998 Use this to get the size of the buffer in to array
  1555. DWORD LineStart;
  1556. // Send the EM_GETSEL message to the edit control.
  1557. // The low-order word of the return value is the character
  1558. // position of the caret relative to the first character in the
  1559. // edit control.
  1560. dwGetSel = (WORD)SendMessage(hwnd, EM_GETSEL, (WPARAM)(LPDWORD) &wStart, (LPARAM)(LPDWORD) &wEnd);
  1561. if (dwGetSel == -1)
  1562. {
  1563. return FALSE;
  1564. }
  1565. LineStart = wStart;
  1566. // New: Check for the selected text: AK
  1567. if ( g_pGlobalData->nMsrDoNext == MSR_DOCHARR )
  1568. LineStart = wEnd;
  1569. else if ( g_pGlobalData->nMsrDoNext == MSR_DOLINED )
  1570. LineStart = wEnd - 1;
  1571. else if ( g_pGlobalData->nMsrDoNext == MSR_DOWORDR )
  1572. LineStart = wEnd;
  1573. // SteveDon: get the line for the start of the selection
  1574. wLineNumber = (WORD)SendMessage(hwnd,EM_LINEFROMCHAR, LineStart, 0L);
  1575. // get the first character on that line that we're on.
  1576. wLineIndex = (WORD)SendMessage(hwnd,EM_LINEINDEX, wLineNumber, 0L);
  1577. // get the length of the line we're on
  1578. wLineLength = (WORD)SendMessage(hwnd,EM_LINELENGTH, LineStart, 0L);
  1579. // Subtract the LineIndex from the start of the selection,
  1580. // This result is the column number of the caret position.
  1581. wColNumber = LineStart - wLineIndex;
  1582. // if we can't hold the text we want, say nothing.
  1583. if (wLineLength > MAX_TEXT)
  1584. {
  1585. return FALSE;
  1586. }
  1587. // To get the text of a line, send the EM_GETLINE message. When
  1588. // the message is sent, wParam is the line number to get and lParam
  1589. // is a pointer to the buffer that will hold the text. When the message
  1590. // is sent, the first word of the buffer specifies the maximum number
  1591. // of characters that can be copied to the buffer.
  1592. // We'll allocate the memory for the buffer in "shared" space so
  1593. // we can all see it.
  1594. // Allocate a buffer to hold it
  1595. // PB 22 Nov 1998 Make it work!!! next 6 lines new. Use global shared memory to do this!!!
  1596. nSomeInt = wLineLength+1;
  1597. if (nSomeInt >= 2000)
  1598. nSomeInt = 1999;
  1599. p = (int *) g_pGlobalData->pszTextLocal;
  1600. *p = nSomeInt;
  1601. SendMessage(hwnd, EM_GETLINE, (WPARAM)wLineNumber, (LPARAM)g_pGlobalData->pszTextLocal);
  1602. g_pGlobalData->pszTextLocal[nSomeInt] = 0;
  1603. // At this stage, pszTextLocal points to a (possibly) empty string.
  1604. // We deal with that later...
  1605. switch (g_pGlobalData->nMsrDoNext)
  1606. {
  1607. case MSR_DOWORDR:
  1608. case MSR_DOWORD:
  1609. if (wColNumber >= wLineLength)
  1610. {
  1611. SpeakMute(0);
  1612. SpeakStringId(IDS_LINEEND);
  1613. break;
  1614. }
  1615. else
  1616. {
  1617. for (wEndWord = wColNumber; wEndWord < wLineLength; wEndWord++)
  1618. {
  1619. if (g_pGlobalData->pszTextLocal[wEndWord] <= ' ')
  1620. {
  1621. break;
  1622. }
  1623. }
  1624. wEndWord++;
  1625. if (wEndWord-wColNumber < ARRAYSIZE(g_pGlobalData->pszTextLocal))
  1626. {
  1627. lstrcpyn(g_pGlobalData->pszTextLocal,g_pGlobalData->pszTextLocal+wColNumber,wEndWord-wColNumber);
  1628. g_pGlobalData->pszTextLocal[wEndWord-wColNumber] = TEXT('\0');
  1629. }
  1630. SpeakMute(0);
  1631. SpeakStringAll(g_pGlobalData->pszTextLocal);
  1632. }
  1633. break;
  1634. case MSR_DOCHARR:
  1635. wColNumber = LineStart - wLineIndex - 1;
  1636. // Fall Through
  1637. case MSR_DOCHAR: // OK now read character to left and right
  1638. if (wColNumber >= wLineLength)
  1639. {
  1640. SpeakMute(0);
  1641. SpeakStringId(IDS_LINEEND);
  1642. }
  1643. else if (g_pGlobalData->pszTextLocal[wColNumber] == TEXT(' '))
  1644. {
  1645. SpeakMute(0);
  1646. SpeakStringId(IDS_SPACE);
  1647. }
  1648. else
  1649. {
  1650. g_pGlobalData->pszTextLocal[0] = g_pGlobalData->pszTextLocal[wColNumber];
  1651. g_pGlobalData->pszTextLocal[1] = 0;
  1652. SpeakMute(0);
  1653. SpeakStringAll(g_pGlobalData->pszTextLocal);
  1654. }
  1655. break;
  1656. case MSR_DOLINED:
  1657. // Fall through
  1658. case MSR_DOLINE:
  1659. g_pGlobalData->pszTextLocal[wLineLength] = 0; // add null
  1660. SpeakMute(0);
  1661. SpeakStringAll(g_pGlobalData->pszTextLocal);
  1662. break;
  1663. } // end switch (g_pGlobalData->nMsrDoNext)
  1664. } // end if (g_pGlobalData->nMsrDoNext)
  1665. RECT rcCursor;
  1666. IAccessible* pIAcc;
  1667. HRESULT hr;
  1668. VARIANT varChild;
  1669. SetRectEmpty(&rcCursor); // now sort out mouse position as apprpropriate
  1670. hr = AccessibleObjectFromEvent (hwnd, idObject, idChild, &pIAcc, &varChild);
  1671. if (SUCCEEDED(hr))
  1672. {
  1673. hr = pIAcc->accLocation(&rcCursor.left, &rcCursor.top,
  1674. &rcCursor.right, &rcCursor.bottom,
  1675. varChild);
  1676. // Move mouse cursor, Only when Track mouse option is selcted: AK
  1677. if (SUCCEEDED(hr) && g_pGlobalData->fTrackInputFocus && g_pGlobalData->fTrackCaret && g_pGlobalData->fMouseUp )
  1678. {
  1679. const POINT ptLoc = { rcCursor.left + (rcCursor.right/2), rcCursor.top + (rcCursor.bottom/2) };
  1680. g_ptMoveCursor.x = ptLoc.x;
  1681. g_ptMoveCursor.y = ptLoc.y;
  1682. if ( g_uTimer == 0 )
  1683. g_uTimer = SetTimer( NULL, 0, 100, TrackCursor );
  1684. else
  1685. SetSecondary( ptLoc.x, ptLoc.y, FALSE );
  1686. }
  1687. pIAcc->Release();
  1688. }
  1689. return TRUE;
  1690. }
  1691. /*************************************************************************
  1692. Function: OnObjectShowEvent
  1693. Purpose: Receives object show events - This is used for balloon tips
  1694. Inputs: DWORD event - What event are we processing
  1695. HWND hwnd - HWND of window generating event
  1696. LONG idObject - ID of object generating event
  1697. LONG idChild - ID of child generating event (0 if object)
  1698. DWORD idThread - ID of thread generating event
  1699. DWORD dwmsEventTime - Timestamp of event
  1700. Returns: BOOL - TRUE if succeeded
  1701. *************************************************************************/
  1702. BOOL OnObjectShowEvent(DWORD event, HWND hwnd, LONG idObject,
  1703. LONG idChild, DWORD dwmsTimeStamp)
  1704. {
  1705. IAccessible* pAcc = NULL;
  1706. HRESULT hr;
  1707. VARIANT varChild;
  1708. varChild.vt = VT_EMPTY;
  1709. IAccessible* pAccTemp = NULL;
  1710. VARIANT varChildTemp;
  1711. varChildTemp.vt = VT_I4;
  1712. varChildTemp.lVal = CHILDID_SELF;
  1713. if( idObject == OBJID_WINDOW )
  1714. {
  1715. // Most common case - get the client object, check if role is balloon tip...
  1716. hr = AccessibleObjectFromWindow( hwnd, OBJID_CLIENT, IID_IAccessible, (void **) &pAccTemp );
  1717. if( hr == S_OK && pAccTemp )
  1718. {
  1719. if( !CheckIsBalloonTipElseRelease( pAccTemp, varChild, &pAcc, &varChild ) )
  1720. return FALSE;
  1721. }
  1722. }
  1723. // if we didn't find a balloon tip try and get it from the event instead
  1724. if ( !pAcc && varChild.vt != VT_I4 )
  1725. {
  1726. hr = AccessibleObjectFromEvent( hwnd, idObject, idChild, &pAccTemp, &varChildTemp );
  1727. if( hr == S_OK && pAccTemp )
  1728. {
  1729. if( !CheckIsBalloonTipElseRelease( pAccTemp, varChildTemp, &pAcc, &varChild ) )
  1730. return FALSE;
  1731. }
  1732. else
  1733. {
  1734. return FALSE;
  1735. }
  1736. }
  1737. TCHAR szRole[ 128 ] = TEXT("");
  1738. VARIANT varRole;
  1739. hr = pAcc->get_accRole( varChild, & varRole );
  1740. if( hr == S_OK && varRole.vt == VT_I4 )
  1741. GetRoleText( varRole.lVal, szRole, ARRAYSIZE( szRole ) );
  1742. BSTR bstrName = NULL;
  1743. TCHAR szName [ 1025 ] = TEXT("");
  1744. TCHAR * pszName;
  1745. hr = pAcc->get_accName( varChild, & bstrName );
  1746. if( hr == S_OK && bstrName != NULL && bstrName[ 0 ] != '\0' )
  1747. {
  1748. #ifdef UNICODE
  1749. pszName = bstrName;
  1750. #else
  1751. WideCharToMultiByte( CP_ACP, 0, bstrName, -1, szName, ARRAYSIZE( szName ), NULL, NULL );
  1752. pszName = szName;
  1753. #endif
  1754. }
  1755. TCHAR szText[ 1025 ];
  1756. lstrcpyn(szText, szRole, ARRAYSIZE(szText));
  1757. lstrcatn(szText, TEXT(": "), ARRAYSIZE(szText));
  1758. lstrcatn(szText, pszName, ARRAYSIZE(szText));
  1759. szText[ARRAYSIZE(szText)-1] = TEXT('\0');
  1760. SpeakString(szText);
  1761. SysFreeString(bstrName);
  1762. return TRUE;
  1763. }
  1764. /*************************************************************************
  1765. Function: InitMSAA
  1766. Purpose: Initalize the Active Accessibility subsystem, including
  1767. initializing the helper thread, installing the WinEvent
  1768. hook, and registering custom messages.
  1769. Inputs: none
  1770. Returns: BOOL - TRUE if successful
  1771. History:
  1772. *************************************************************************/
  1773. BOOL InitMSAA(void)
  1774. {
  1775. CScopeMutex csMutex;
  1776. if (!csMutex.Create(g_szMutexNarrator, c_nMutexWait))
  1777. return FALSE;
  1778. // Call this FIRST to initialize the helper thread
  1779. InitHelperThread();
  1780. // Set up event call back
  1781. g_hEventHook = SetWinEventHook(EVENT_MIN, // We want all events
  1782. EVENT_MAX,
  1783. GetModuleHandle(TEXT("NarrHook.dll")), // Use our own module
  1784. WinEventProc, // Our callback function
  1785. 0, // All processes
  1786. 0, // All threads
  1787. WINEVENT_OUTOFCONTEXT /* WINEVENT_INCONTEXT */);
  1788. // Receive async events
  1789. // JMC: For Safety, lets always be 'out of context'. Who cares if there is a
  1790. // performance penalty.
  1791. // By being out of context, we guarantee the we won't bring down other apps if
  1792. // there is a bug in our event hook.
  1793. // Did we install correctly?
  1794. if (g_hEventHook)
  1795. {
  1796. //
  1797. // register own own message for giving the cursor position
  1798. //
  1799. g_pGlobalData->uMSG_MSR_Cursor = RegisterWindowMessage(TEXT("MSR cursor"));
  1800. return TRUE;
  1801. }
  1802. // Did not install properly - clean up and fail
  1803. UnInitHelperThread();
  1804. return FALSE;
  1805. }
  1806. /*************************************************************************
  1807. Function: UnInitMSAA
  1808. Purpose: Shuts down the Active Accessibility subsystem
  1809. Inputs: none
  1810. Returns: BOOL - TRUE if successful
  1811. History:
  1812. *************************************************************************/
  1813. BOOL UnInitMSAA(void)
  1814. {
  1815. CScopeMutex csMutex;
  1816. if (csMutex.Create(g_szMutexNarrator, c_nMutexWait))
  1817. {
  1818. // Remove the WinEvent hook
  1819. UnhookWinEvent(g_hEventHook);
  1820. // Call this LAST so that the helper thread can finish up.
  1821. UnInitHelperThread();
  1822. }
  1823. // return true; we're exiting and there's not much that can be done
  1824. return TRUE;
  1825. }
  1826. // --------------------------------------------------------------------------
  1827. //
  1828. // GetObjectAtCursor()
  1829. //
  1830. // Gets the object the cursor is over.
  1831. //
  1832. // --------------------------------------------------------------------------
  1833. IAccessible * GetObjectAtCursor(VARIANT * pvarChild,HRESULT* pResult)
  1834. {
  1835. POINT pt;
  1836. IAccessible * pIAcc;
  1837. HRESULT hr;
  1838. //
  1839. // Get cursor object & position
  1840. //
  1841. if (g_pGlobalData->ptCurrentMouse.x < 0)
  1842. GetCursorPos(&pt);
  1843. else
  1844. pt = g_pGlobalData->ptCurrentMouse;
  1845. //
  1846. // Get object here.
  1847. //
  1848. VariantInit(pvarChild);
  1849. hr = AccessibleObjectFromPoint(pt, &pIAcc, pvarChild);
  1850. *pResult = hr;
  1851. if (!SUCCEEDED(hr)) {
  1852. return NULL;
  1853. }
  1854. return pIAcc;
  1855. }
  1856. /*************************************************************************
  1857. Function: SpeakItem
  1858. Purpose:
  1859. Inputs:
  1860. Returns:
  1861. History:
  1862. *************************************************************************/
  1863. void SpeakItem(int nOption)
  1864. {
  1865. TCHAR tszDesc[256];
  1866. VARIANT varChild;
  1867. IAccessible* pIAcc;
  1868. HRESULT hr;
  1869. POINT ptMouse;
  1870. BSTR bstr;
  1871. SpeakString(TEXT(" ")); // reset last utterence
  1872. // Important to init variants
  1873. VariantInit(&varChild);
  1874. //
  1875. // Get cursor object & position
  1876. //
  1877. if (g_pGlobalData->ptCurrentMouse.x < 0)
  1878. GetCursorPos(&ptMouse);
  1879. else
  1880. ptMouse = g_pGlobalData->ptCurrentMouse;
  1881. hr = AccessibleObjectFromPoint(ptMouse, &pIAcc, &varChild);
  1882. // Check to see if we got a valid pointer
  1883. if (SUCCEEDED(hr))
  1884. {
  1885. hr = pIAcc->get_accDescription(varChild, &bstr);
  1886. if ( FAILED(hr) )
  1887. bstr = NULL;
  1888. if (bstr)
  1889. {
  1890. #ifdef UNICODE
  1891. lstrcpyn(tszDesc,bstr,ARRAYSIZE(tszDesc));
  1892. tszDesc[ARRAYSIZE(tszDesc)-1] = TEXT('\0');
  1893. #else
  1894. // If we got back a string, use that instead.
  1895. WideCharToMultiByte(CP_ACP, 0, bstr, -1, tszDesc, sizeof(tszDesc), NULL, NULL);
  1896. #endif
  1897. SysFreeString(bstr);
  1898. SpeakStringAll(tszDesc);
  1899. }
  1900. if (pIAcc)
  1901. pIAcc->Release();
  1902. }
  1903. return;
  1904. }
  1905. /*************************************************************************
  1906. Function: SpeakMute
  1907. Purpose: causes the system to shut up.
  1908. Inputs:
  1909. Returns:
  1910. History:
  1911. *************************************************************************/
  1912. void SpeakMute(int nOption)
  1913. {
  1914. SendMessage(g_pGlobalData->hwndMSR, WM_MUTE, 0, 0);
  1915. }
  1916. /*************************************************************************
  1917. Function: SpeakObjectInfo
  1918. Purpose:
  1919. Inputs:
  1920. Returns:
  1921. History:
  1922. *************************************************************************/
  1923. void SpeakObjectInfo(LPOBJINFO poiObj, BOOL ReadExtra)
  1924. {
  1925. BSTR bstrName;
  1926. IAccessible* pIAcc;
  1927. long* pl;
  1928. HRESULT hr;
  1929. CAutoArray<TCHAR> aaName( new TCHAR[MAX_TEXT] );
  1930. TCHAR * szName = aaName.Get();
  1931. CAutoArray<TCHAR> aaSpeak( new TCHAR[MAX_TEXT] );
  1932. TCHAR * szSpeak = aaSpeak.Get();
  1933. if ( !szName || !szSpeak )
  1934. return; // no memory
  1935. TCHAR szRole[MAX_TEXT_ROLE];
  1936. TCHAR szState[MAX_TEXT_ROLE];
  1937. TCHAR szValue[MAX_TEXT_ROLE];
  1938. VARIANT varRole;
  1939. VARIANT varState;
  1940. BOOL bSayValue = TRUE;
  1941. BOOL bReadHTMLEdit = FALSE;
  1942. DWORD Role = 0;
  1943. bstrName = NULL;
  1944. // Truncate them
  1945. szName[0] = TEXT('\0');
  1946. szSpeak[0] = TEXT('\0');
  1947. szRole[0] = TEXT('\0');
  1948. szState[0] = TEXT('\0');
  1949. szValue[0] = TEXT('\0');
  1950. // Get the object out of the struct
  1951. pl = poiObj->plObj;
  1952. pIAcc =(IAccessible*)pl;
  1953. GetObjectProperty(pIAcc, poiObj->varChild.lVal, ID_NAME, szName, MAX_NAME);
  1954. if (szName[0] == -1) // name going to be garbage
  1955. {
  1956. LoadString(g_Hinst, IDS_NAMELESS, szSpeak, MAX_TEXT); // For now change "IDS_NAMELESS" in Resources to be just space!
  1957. }
  1958. else
  1959. {
  1960. lstrcpyn(szSpeak, szName, MAX_TEXT);
  1961. szSpeak[MAX_TEXT-1] = TEXT('\0');
  1962. }
  1963. szName[0] = TEXT('\0');
  1964. VariantInit(&varRole);
  1965. hr = pIAcc->get_accRole(poiObj->varChild, &varRole);
  1966. if (FAILED(hr))
  1967. {
  1968. DBPRINTF (TEXT("Failed role!\r\n"));
  1969. MessageBeep(MB_OK);
  1970. return;
  1971. }
  1972. if (varRole.vt == VT_I4)
  1973. {
  1974. Role = varRole.lVal; // save for use below (if ReadExtra)
  1975. GetRoleText(varRole.lVal,szRole, ARRAYSIZE(szRole));
  1976. // Special casing stuff:
  1977. // Outline Items give out their level No. in the tree in the Value
  1978. // field, So Don't speak it.
  1979. switch(varRole.lVal)
  1980. {
  1981. case ROLE_SYSTEM_STATICTEXT:
  1982. case ROLE_SYSTEM_OUTLINEITEM:
  1983. {
  1984. bSayValue = FALSE; // don't speak value for text - it may be HTML link
  1985. }
  1986. break;
  1987. // If the text is from combo -box then speak up
  1988. case ROLE_SYSTEM_TEXT:
  1989. bReadHTMLEdit = TRUE;
  1990. bSayValue = TRUE; // Speak text in combo box
  1991. break;
  1992. case ROLE_SYSTEM_LISTITEM:
  1993. {
  1994. FilterGUID(szSpeak);
  1995. }
  1996. break;
  1997. case ROLE_SYSTEM_SPINBUTTON:
  1998. // Remove the Wizard97 spin box utterances....AK
  1999. {
  2000. HWND hWnd, hWndP;
  2001. WindowFromAccessibleObject(pIAcc, &hWnd);
  2002. if ( hWnd != NULL)
  2003. {
  2004. hWndP = GetParent(hWnd);
  2005. LONG_PTR style = GetWindowLongPtr(hWndP, GWL_STYLE);
  2006. if ( style & WS_DISABLED)
  2007. return;
  2008. }
  2009. }
  2010. break;
  2011. default:
  2012. break;
  2013. }
  2014. }
  2015. if (g_pGlobalData->fDoingPassword)
  2016. LoadString(g_Hinst, IDS_PASSWORD, szRole, 128);
  2017. // This will free a BSTR, etc.
  2018. VariantClear(&varRole);
  2019. if ( (lstrlen(szRole) > 0) &&
  2020. (varRole.lVal != ROLE_SYSTEM_CLIENT) )
  2021. {
  2022. lstrcatn(szSpeak, TEXT(", "),MAX_TEXT);
  2023. lstrcatn(szSpeak, szRole, MAX_TEXT);
  2024. szRole[0] = TEXT('\0');
  2025. }
  2026. //
  2027. // add value string if there is one
  2028. //
  2029. hr = pIAcc->get_accValue(poiObj->varChild, &bstrName);
  2030. if ( FAILED(hr) )
  2031. bstrName = NULL;
  2032. if (bstrName)
  2033. {
  2034. #ifdef UNICODE
  2035. lstrcpyn(szName, bstrName, MAX_TEXT);
  2036. szName[MAX_TEXT-1] = TEXT('\0');
  2037. #else
  2038. // If we got back a string, use that instead.
  2039. WideCharToMultiByte(CP_ACP, 0, bstrName,-1, szName, MAX_TEXT, NULL, NULL);
  2040. #endif
  2041. SysFreeString(bstrName);
  2042. }
  2043. // ROBSI: 10-10-99, Bug?
  2044. // We are not properly testing bSayValue here. Therefore, outline items are
  2045. // speaking their indentation level -- their accValue. According to comments
  2046. // above, this should be skipped. However, below we are explicitly loading
  2047. // IDS_TREELEVEL and using this level. Which is correct?
  2048. // If not IE, read values for combo box, Edit etc.., For IE, read only for edit boxes
  2049. if ( ((!g_pGlobalData->fInternetExplorer && bSayValue )
  2050. || ( g_pGlobalData->fInternetExplorer && bReadHTMLEdit ) )
  2051. && lstrlen(szName) > 0)
  2052. { // i.e. got a value
  2053. lstrcatn(szSpeak,TEXT(", "),MAX_TEXT);
  2054. lstrcatn(szSpeak,szName,MAX_TEXT);
  2055. szName[0] = TEXT('\0');
  2056. }
  2057. hr = pIAcc->get_accState(poiObj->varChild, &varState);
  2058. if (FAILED(hr))
  2059. {
  2060. MessageBeep(MB_OK);
  2061. return;
  2062. }
  2063. if (varState.vt == VT_I4)
  2064. {
  2065. GetStateString(varState.lVal, STATE_MASK, szState, ARRAYSIZE(szState) );
  2066. }
  2067. if (lstrlen(szState) > 0)
  2068. {
  2069. lstrcatn(szSpeak, TEXT(", "), MAX_TEXT);
  2070. lstrcatn(szSpeak, szState, MAX_TEXT);
  2071. szState[0] = TEXT('\0');
  2072. }
  2073. if (ReadExtra && ( // Speak extra information if just got focus on this item
  2074. Role == ROLE_SYSTEM_CHECKBUTTON ||
  2075. Role == ROLE_SYSTEM_PUSHBUTTON ||
  2076. Role == ROLE_SYSTEM_RADIOBUTTON ||
  2077. Role == ROLE_SYSTEM_MENUITEM ||
  2078. Role == ROLE_SYSTEM_OUTLINEITEM ||
  2079. Role == ROLE_SYSTEM_LISTITEM ||
  2080. Role == ROLE_SYSTEM_OUTLINEBUTTON)
  2081. ) {
  2082. switch (Role) {
  2083. case ROLE_SYSTEM_CHECKBUTTON:
  2084. {
  2085. // Change due to localization issues:a-anilk
  2086. TCHAR szTemp[MAX_TEXT_ROLE];
  2087. if (varState.lVal & STATE_SYSTEM_CHECKED)
  2088. LoadString(g_Hinst, IDS_TO_UNCHECK, szTemp, MAX_TEXT_ROLE);
  2089. else
  2090. LoadString(g_Hinst, IDS_TO_CHECK, szTemp, MAX_TEXT_ROLE);
  2091. // GetObjectProperty(pIAcc, poiObj->varChild.lVal, ID_DEFAULT, szName, 256);
  2092. // wsprintf(szTemp, szTempLate, szName);
  2093. lstrcatn(szSpeak, szTemp, MAX_TEXT);
  2094. }
  2095. break;
  2096. case ROLE_SYSTEM_PUSHBUTTON:
  2097. {
  2098. if ( !(varState.lVal & STATE_SYSTEM_UNAVAILABLE) )
  2099. {
  2100. LoadString(g_Hinst, IDS_TOPRESS, szName, 256);
  2101. lstrcatn(szSpeak, szName, MAX_TEXT);
  2102. }
  2103. }
  2104. break;
  2105. case ROLE_SYSTEM_RADIOBUTTON:
  2106. LoadString(g_Hinst, IDS_TOSELECT, szName, 256);
  2107. lstrcatn(szSpeak, szName, MAX_TEXT);
  2108. break;
  2109. // To distinguish between menu items with sub-menu and without one.
  2110. // For submenus, It speaks - ', Has a sub-menu': a-anilk
  2111. case ROLE_SYSTEM_MENUITEM:
  2112. {
  2113. long count = 0;
  2114. pIAcc->get_accChildCount(&count);
  2115. // count = 1 for all menu items with sub menus
  2116. if ( count == 1 || varState.lVal & STATE_SYSTEM_HASPOPUP )
  2117. {
  2118. LoadString(g_Hinst, IDS_SUBMENU, szName, 256);
  2119. lstrcatn(szSpeak, szName, MAX_TEXT);
  2120. }
  2121. }
  2122. break;
  2123. case ROLE_SYSTEM_OUTLINEITEM:
  2124. {
  2125. // Read out the level in the tree....
  2126. // And also the status as Expanded or Collapsed....:AK
  2127. TCHAR buffer[64];
  2128. if ( varState.lVal & STATE_SYSTEM_COLLAPSED )
  2129. {
  2130. LoadString(g_Hinst, IDS_TEXPAND, szName, 256);
  2131. lstrcatn(szSpeak, szName, MAX_TEXT);
  2132. }
  2133. else if ( varState.lVal & STATE_SYSTEM_EXPANDED )
  2134. {
  2135. LoadString(g_Hinst, IDS_TCOLLAP, szName, 256);
  2136. lstrcatn(szSpeak, szName, MAX_TEXT);
  2137. }
  2138. hr = pIAcc->get_accValue(poiObj->varChild, &bstrName);
  2139. LoadString(g_Hinst, IDS_TREELEVEL, szName, 256);
  2140. wsprintf(buffer, szName, bstrName);
  2141. lstrcatn(szSpeak, buffer, MAX_TEXT);
  2142. SysFreeString(bstrName);
  2143. }
  2144. break;
  2145. case ROLE_SYSTEM_LISTITEM:
  2146. {
  2147. // The list item is selectable, But not selected...:a-anilk
  2148. if ( (varState.lVal & STATE_SYSTEM_SELECTABLE ) &&
  2149. (!(varState.lVal & STATE_SYSTEM_SELECTED)) )
  2150. {
  2151. LoadString(g_Hinst, IDS_NOTSEL, szName, 256);
  2152. lstrcatn(szSpeak, szName, MAX_TEXT);
  2153. }
  2154. }
  2155. break;
  2156. case ROLE_SYSTEM_OUTLINEBUTTON:
  2157. {
  2158. if ( varState.lVal & STATE_SYSTEM_COLLAPSED )
  2159. {
  2160. LoadString(g_Hinst, IDS_OB_EXPAND, szName, 256);
  2161. lstrcatn(szSpeak, szName, MAX_TEXT);
  2162. }
  2163. else if ( varState.lVal & STATE_SYSTEM_EXPANDED )
  2164. {
  2165. LoadString(g_Hinst, IDS_OB_COLLAPSE, szName, 256);
  2166. lstrcatn(szSpeak, szName, MAX_TEXT);
  2167. }
  2168. }
  2169. }
  2170. }
  2171. SpeakString(szSpeak);
  2172. return;
  2173. }
  2174. /*************************************************************************
  2175. Function: SpeakMainItems
  2176. Purpose:
  2177. Inputs:
  2178. Returns:
  2179. History:
  2180. *************************************************************************/
  2181. void SpeakMainItems(int nOption)
  2182. {
  2183. VARIANT varChild;
  2184. IAccessible* pIAcc=NULL;
  2185. HRESULT hr;
  2186. POINT ptMouse;
  2187. SpeakString(TEXT(" "));
  2188. //
  2189. // Get cursor object & position
  2190. //
  2191. if (g_pGlobalData->ptCurrentMouse.x < 0)
  2192. GetCursorPos(&ptMouse);
  2193. else
  2194. ptMouse = g_pGlobalData->ptCurrentMouse;
  2195. // Important to init variants
  2196. VariantInit(&varChild);
  2197. hr = AccessibleObjectFromPoint(ptMouse, &pIAcc, &varChild);
  2198. // Check to see if we got a valid pointer
  2199. if (SUCCEEDED(hr))
  2200. {
  2201. OBJINFO objCurrent;
  2202. objCurrent.hwnd = WindowFromPoint(ptMouse);
  2203. objCurrent.plObj = (long*)pIAcc;
  2204. objCurrent.varChild = varChild;
  2205. SpeakObjectInfo(&objCurrent,FALSE);
  2206. pIAcc->Release();
  2207. }
  2208. return;
  2209. }
  2210. /*************************************************************************
  2211. Function: SpeakKeyboard
  2212. Purpose:
  2213. Inputs:
  2214. Returns:
  2215. History:
  2216. *************************************************************************/
  2217. void SpeakKeyboard(int nOption)
  2218. {
  2219. TCHAR szName[128];
  2220. VARIANT varChild;
  2221. IAccessible* pIAcc;
  2222. HRESULT hr;
  2223. POINT ptMouse;
  2224. //
  2225. // Get cursor object & position
  2226. //
  2227. if (g_pGlobalData->ptCurrentMouse.x < 0)
  2228. GetCursorPos(&ptMouse);
  2229. else
  2230. ptMouse = g_pGlobalData->ptCurrentMouse;
  2231. // Important to init variants
  2232. VariantInit(&varChild);
  2233. hr = AccessibleObjectFromPoint(ptMouse, &pIAcc, &varChild);
  2234. // Check to see if we got a valid pointer
  2235. if (SUCCEEDED(hr))
  2236. {
  2237. SpeakStringId(IDS_KEYBOARD);
  2238. GetObjectProperty(pIAcc, varChild.lVal, ID_SHORTCUT, szName, ARRAYSIZE(szName));
  2239. SpeakString(szName);
  2240. if (pIAcc)
  2241. pIAcc->Release();
  2242. }
  2243. return;
  2244. }
  2245. /*************************************************************************
  2246. Function: Home
  2247. Purpose:
  2248. Inputs:
  2249. Returns:
  2250. History:
  2251. ALT_HOME to take secondary cursor to top of this window
  2252. *************************************************************************/
  2253. void Home(int x)
  2254. {
  2255. RECT rect;
  2256. GetWindowRect(GetForegroundWindow(),&rect);
  2257. // Set it to show the title bar 48, max system icon size
  2258. SetSecondary(rect.left + 48/*(rect.right - rect.left)/2*/, rect.top + 5,g_pGlobalData->fTrackSecondary);
  2259. SpeakMainItems(0);
  2260. }
  2261. /*************************************************************************
  2262. Function: MoveToEnd
  2263. Purpose:
  2264. Inputs:
  2265. Returns:
  2266. History:
  2267. ALT_END to take secondary cursor to top of this window
  2268. *************************************************************************/
  2269. void MoveToEnd(int x)
  2270. {
  2271. RECT rect;
  2272. GetWindowRect(GetForegroundWindow(),&rect);
  2273. SetSecondary(rect.left+ 48 /*(rect.right - rect.left)/2*/,rect.bottom - 8,g_pGlobalData->fTrackSecondary);
  2274. SpeakMainItems(0);
  2275. }
  2276. #define LEFT_ID 0
  2277. #define RIGHT_ID 1
  2278. #define TOP_ID 2
  2279. #define BOTTOM_ID 3
  2280. #define SPOKEN_ID 4
  2281. #define SPATIAL_SIZE 2500
  2282. long ObjLocation[5][SPATIAL_SIZE];
  2283. int ObjIndex;
  2284. #define MAX_SPEAK 8192
  2285. /*************************************************************************
  2286. Function:
  2287. Purpose:
  2288. Inputs:
  2289. Returns:
  2290. History:
  2291. *************************************************************************/
  2292. void SpatialRead(RECT rc)
  2293. {
  2294. int left_min, top_min, width_min, height_min, index_min; // current minimum object
  2295. int i, j; // loop vars
  2296. for (i = 0; i < ObjIndex; i++)
  2297. {
  2298. left_min = 20000;
  2299. top_min = 20000;
  2300. index_min = -1;
  2301. if (g_pGlobalData->fInternetExplorer)
  2302. {
  2303. for (j = 0; j < ObjIndex; j++)
  2304. {
  2305. // Skip items that have been spoken before...
  2306. if (ObjLocation[SPOKEN_ID][j] != 0)
  2307. continue;
  2308. // if this is the first non-spoken object, just use it
  2309. if( index_min == -1 )
  2310. {
  2311. index_min = j;
  2312. top_min = ObjLocation[TOP_ID][j];
  2313. left_min = ObjLocation[LEFT_ID][j];
  2314. width_min = ObjLocation[RIGHT_ID][j];
  2315. height_min = ObjLocation[BOTTOM_ID][j];
  2316. }
  2317. else
  2318. {
  2319. // If same top, different heights, and overlapping widths, then give smaller one priority
  2320. if( ObjLocation[TOP_ID][j] == top_min
  2321. && ObjLocation[BOTTOM_ID][j] != height_min
  2322. && ObjLocation[LEFT_ID][j] < left_min + width_min
  2323. && ObjLocation[LEFT_ID][j] + ObjLocation[RIGHT_ID][j] > left_min )
  2324. {
  2325. if( ObjLocation[BOTTOM_ID][j] < height_min )
  2326. {
  2327. index_min = j;
  2328. top_min = ObjLocation[TOP_ID][j];
  2329. left_min = ObjLocation[LEFT_ID][j];
  2330. width_min = ObjLocation[RIGHT_ID][j];
  2331. height_min = ObjLocation[BOTTOM_ID][j];
  2332. }
  2333. }
  2334. else if ( (ObjLocation[TOP_ID][j] < top_min || // check if more top-left than previous ones - give or take on height (i.e. 5 pixels)
  2335. (ObjLocation[TOP_ID][j] == top_min && ObjLocation[LEFT_ID][j] < left_min ) ) ) // more left on this line
  2336. {
  2337. // OK got a candidate
  2338. index_min = j;
  2339. top_min = ObjLocation[TOP_ID][j];
  2340. left_min = ObjLocation[LEFT_ID][j];
  2341. width_min = ObjLocation[RIGHT_ID][j];
  2342. height_min = ObjLocation[BOTTOM_ID][j];
  2343. }
  2344. }
  2345. } // for j
  2346. } // end if Internet Explorer
  2347. else
  2348. {
  2349. for (j = 0; j < ObjIndex; j++)
  2350. {
  2351. if (ObjLocation[SPOKEN_ID][j] == 0 && // not been spoken before
  2352. // check if enclosed by current rectangle (semi-hierarcical - with recursion!)
  2353. (ObjLocation[LEFT_ID][j] >= rc.left && ObjLocation[LEFT_ID][j] <= rc.left + rc.right &&
  2354. ObjLocation[TOP_ID][j] >= rc.top && ObjLocation[TOP_ID][j] <= rc.top + rc.bottom
  2355. ) &&
  2356. // also check if more top-left than previous ones - give or take on height (i.e. 10 pixels)
  2357. ( (ObjLocation[TOP_ID][j] < top_min + 10 && ObjLocation[LEFT_ID][j] < left_min)
  2358. // or just higher up
  2359. || (ObjLocation[TOP_ID][j] < top_min)
  2360. )
  2361. )
  2362. { // OK got a candidate
  2363. index_min = j;
  2364. top_min = ObjLocation[TOP_ID][j];
  2365. left_min = ObjLocation[LEFT_ID][j];
  2366. }
  2367. } // for j
  2368. } // end not Internet Explorer
  2369. if (index_min >= 0)
  2370. { // got one!
  2371. HWND hwndList;
  2372. CAutoArray<TCHAR> aaText( new TCHAR[MAX_SPEAK] );
  2373. TCHAR * szText = aaText.Get();
  2374. RECT rect;
  2375. ObjLocation[SPOKEN_ID][index_min] = 1; // don't do this one again
  2376. hwndList = GetDlgItem(g_pGlobalData->hwndMSR, IDC_WINDOWINFO);
  2377. // if the data does not fit don't say anything.
  2378. if (SendMessage(hwndList, LB_GETTEXTLEN, index_min, NULL) <= MAX_SPEAK)
  2379. {
  2380. SendMessage(hwndList, LB_GETTEXT, index_min, (LPARAM) szText);
  2381. SpeakString(szText);
  2382. }
  2383. if (g_pGlobalData->fInternetExplorer) // no recursion for IE
  2384. continue;
  2385. rect.left = ObjLocation[LEFT_ID][index_min];
  2386. rect.right = ObjLocation[RIGHT_ID][index_min];
  2387. rect.top = ObjLocation[TOP_ID][index_min];
  2388. rect.bottom = ObjLocation[BOTTOM_ID][index_min];
  2389. SpatialRead(rect);
  2390. }
  2391. } // for i
  2392. }
  2393. //--------------------------------------------------------------------------
  2394. //
  2395. // SpeakWindow()
  2396. //
  2397. // Fills in a tree view with the descendants of the given top level window.
  2398. // If hwnd is 0, use the previously saved hwnd to build the tree.
  2399. //
  2400. //--------------------------------------------------------------------------
  2401. void SpeakWindow(int nOption)
  2402. {
  2403. IAccessible* pacc;
  2404. RECT rect;
  2405. TCHAR szName[128];
  2406. VARIANT varT;
  2407. HWND ForeWnd;
  2408. TCHAR buffer[100];
  2409. szName[0] = NULL;
  2410. buffer[0] = NULL;
  2411. g_pGlobalData->nAutoRead = nOption; // set global flag to tell code in AddItem if we're to read edit box contents (don't do it if just got focus as the edit box has probably been spoken already
  2412. ForeWnd = GetForegroundWindow(); // Check if we're in HTML Help
  2413. GetClassName(ForeWnd,buffer,100);
  2414. g_pGlobalData->fHTML_Help = 0;
  2415. if ((lstrcmpi(buffer, CLASS_HTMLHELP) == 0) )
  2416. {
  2417. g_pGlobalData->fInternetExplorer = TRUE;
  2418. g_pGlobalData->fHTML_Help = TRUE;
  2419. GetWindowRect(ForeWnd, &rect); // get the left hand side of our window to use later
  2420. g_pGlobalData->nLeftHandSide = rect.left;
  2421. }
  2422. else if ( IsTridentWindow(buffer) )
  2423. {
  2424. g_pGlobalData->fInternetExplorer = TRUE;
  2425. g_pGlobalData->fHTML_Help = FALSE;
  2426. GetWindowRect(ForeWnd, &rect); // get the left hand side of our window to use later
  2427. g_pGlobalData->nLeftHandSide = rect.left;
  2428. }
  2429. // Inititalise stack for tree information
  2430. ObjIndex = 0;
  2431. //
  2432. // Get the object for the root.
  2433. //
  2434. pacc = NULL;
  2435. AccessibleObjectFromWindow(GetForegroundWindow(), OBJID_WINDOW, IID_IAccessible, (void**)&pacc);
  2436. if (nOption == 1)
  2437. { // if it was a keyboard press then speak the window's name
  2438. SpeakStringId(IDS_WINDOW);
  2439. GetWindowText(GetForegroundWindow(), szName, sizeof(szName)/sizeof(TCHAR)); // raid #113789
  2440. SpeakString(szName);
  2441. }
  2442. if (pacc)
  2443. {
  2444. HWND hwndList; // first clear the list box used to store the window info
  2445. hwndList = GetDlgItem(g_pGlobalData->hwndMSR, IDC_WINDOWINFO);
  2446. SendMessage(hwndList, LB_RESETCONTENT, 0, 0);
  2447. // AddAccessibleObjects changes this - so need to save and restore it
  2448. // so that it is correct when we call SpatialRead...
  2449. BOOL fIsInternetExplorer = g_pGlobalData->fInternetExplorer;
  2450. InitChildSelf(&varT);
  2451. AddAccessibleObjects(pacc, varT); // recursively go off and get the information
  2452. pacc->Release();
  2453. GetWindowRect(GetForegroundWindow(),&rect);
  2454. if (g_pGlobalData->fReviewStyle)
  2455. {
  2456. g_pGlobalData->fInternetExplorer = fIsInternetExplorer;
  2457. SpatialRead(rect);
  2458. }
  2459. }
  2460. }
  2461. //--------------------------------------------------------------------------
  2462. //
  2463. // AddItem()
  2464. //
  2465. // Parameters: pacc - the IAccessible object to [maybe] add
  2466. // varChild - if pacc is parent, the child id
  2467. // Return Values: Returns TRUE if caller should continue to navigate the
  2468. // UI tree or FALSE if it should stop.
  2469. //
  2470. //--------------------------------------------------------------------------
  2471. BOOL AddItem(IAccessible* pacc, const VARIANT &varChild)
  2472. {
  2473. TCHAR szName[MAX_NAME] = TEXT(" ");
  2474. TCHAR szRole[128] = TEXT(" ");
  2475. TCHAR szState[128] = TEXT(" ");
  2476. TCHAR szValue[MAX_VALUE] = TEXT(" ");
  2477. TCHAR szLink[32];
  2478. VARIANT varT;
  2479. BSTR bszT;
  2480. BOOL DoMore = TRUE;
  2481. BOOL GotStaticText = FALSE;
  2482. BOOL GotGraphic = FALSE;
  2483. BOOL GotText = FALSE;
  2484. BOOL GotNameless = FALSE;
  2485. BOOL GotInvisible = FALSE;
  2486. BOOL GotOffScreen = FALSE;
  2487. BOOL GotLink = FALSE;
  2488. int lastRole = 0;
  2489. static TCHAR szLastName[MAX_NAME] = TEXT(" ");
  2490. BOOL fInternetExplorer = g_pGlobalData->fInternetExplorer;
  2491. int nAutoRead = g_pGlobalData->nAutoRead;
  2492. BOOL fHTMLHelp = g_pGlobalData->fHTML_Help;
  2493. int nLeftHandSide = g_pGlobalData->nLeftHandSide;
  2494. HWND hwndMSR = g_pGlobalData->hwndMSR;
  2495. HRESULT hr;
  2496. //
  2497. // Get object state first. If we are skipping invisible dudes, we want
  2498. // to bail out now.
  2499. //
  2500. VariantInit(&varT);
  2501. hr = pacc->get_accState(varChild, &varT);
  2502. if ( FAILED(hr) )
  2503. {
  2504. DBPRINTF( TEXT("AddItem get_accState returned 0x%x\r\n"), hr );
  2505. return FALSE;
  2506. }
  2507. DWORD dwState = 0;
  2508. if (varT.vt == VT_I4)
  2509. {
  2510. LONG lStateMask = STATE_SYSTEM_UNAVAILABLE | STATE_SYSTEM_INVISIBLE | STATE_SYSTEM_CHECKED;
  2511. GetStateString(varT.lVal, lStateMask, szState, ARRAYSIZE(szState) );
  2512. dwState = varT.lVal;
  2513. GotInvisible = varT.lVal & STATE_SYSTEM_INVISIBLE;
  2514. GotOffScreen = varT.lVal & STATE_SYSTEM_OFFSCREEN;
  2515. // Bail out if not shown. If it's not IE, ignore both invisible and scrolled-off...
  2516. if (!fInternetExplorer && GotInvisible)
  2517. return FALSE;
  2518. // ...but if it's IE, only ignore 'really' invisible (display:none), and allow
  2519. // scrolled-off (which has the offscreen bit set) to be read...
  2520. if (fInternetExplorer && GotInvisible && ! GotOffScreen )
  2521. return FALSE;
  2522. }
  2523. VariantClear(&varT);
  2524. //
  2525. // Get object role.
  2526. //
  2527. VariantInit(&varT);
  2528. hr = pacc->get_accRole(varChild, &varT);
  2529. if ( FAILED(hr) )
  2530. {
  2531. DBPRINTF( TEXT("AddItem get_accRole returned 0x%x\r\n"), hr );
  2532. return FALSE;
  2533. }
  2534. LONG lRole = varT.lVal;
  2535. if (varT.vt == VT_I4)
  2536. {
  2537. switch (varT.lVal)
  2538. {
  2539. case ROLE_SYSTEM_WINDOW:
  2540. case ROLE_SYSTEM_TABLE :
  2541. case ROLE_SYSTEM_DOCUMENT:
  2542. {
  2543. // it's a window - don't read it - read its kids
  2544. return TRUE; // but carry on searching down
  2545. }
  2546. case ROLE_SYSTEM_LIST:
  2547. case ROLE_SYSTEM_SLIDER:
  2548. case ROLE_SYSTEM_STATUSBAR:
  2549. case ROLE_SYSTEM_BUTTONMENU:
  2550. case ROLE_SYSTEM_COMBOBOX:
  2551. case ROLE_SYSTEM_DROPLIST:
  2552. case ROLE_SYSTEM_OUTLINE:
  2553. case ROLE_SYSTEM_TOOLBAR:
  2554. DoMore = FALSE; // i.e. speak it but no more children
  2555. break;
  2556. case ROLE_SYSTEM_GROUPING:
  2557. if (fInternetExplorer)
  2558. {
  2559. return TRUE;
  2560. }
  2561. else
  2562. {
  2563. DoMore = FALSE; // speak it but no more children
  2564. }
  2565. break;
  2566. // Some of the CLIENT fields in office2000 are not spoken because
  2567. // we don't add. We may need to specail case for office :a-anilk
  2568. // Micw: Special case for IE and let the rest thru (Whistler raid #28777)
  2569. case ROLE_SYSTEM_CLIENT : // for now work with this for IE ...???
  2570. if (fInternetExplorer)
  2571. {
  2572. return TRUE;
  2573. }
  2574. break;
  2575. case ROLE_SYSTEM_PANE :
  2576. if ( fInternetExplorer )
  2577. {
  2578. LONG lLeft = 0, lTop = 0, lHeight = 0, lWidth = 0;
  2579. HRESULT hr = pacc->accLocation( &lLeft, &lTop, &lHeight, &lWidth, varChild );
  2580. // If they don't know where they are don't speak them
  2581. // We do not want to read zero width or height and the elements like in
  2582. // Remote Assistance or location of 0,0,0,0 like in oobe
  2583. if ( hr != S_OK || lHeight == 0 || lWidth == 0 )
  2584. {
  2585. return FALSE;
  2586. }
  2587. }
  2588. return TRUE;
  2589. case ROLE_SYSTEM_CELL: // New - works for HTML Help!
  2590. return TRUE;
  2591. case ROLE_SYSTEM_SEPARATOR:
  2592. case ROLE_SYSTEM_TITLEBAR:
  2593. case ROLE_SYSTEM_GRIP:
  2594. case ROLE_SYSTEM_MENUBAR:
  2595. case ROLE_SYSTEM_SCROLLBAR:
  2596. return FALSE; // don't speak it or it's children
  2597. case ROLE_SYSTEM_GRAPHIC: // this works for doing icons!
  2598. GotGraphic = TRUE;
  2599. break;
  2600. case ROLE_SYSTEM_LINK:
  2601. GotLink = TRUE;
  2602. break;
  2603. case ROLE_SYSTEM_TEXT:
  2604. GotText = TRUE;
  2605. break;
  2606. case ROLE_SYSTEM_SPINBUTTON:
  2607. // Remove the Wizard97 spin box utterances....
  2608. {
  2609. HWND hWnd, hWndP;
  2610. WindowFromAccessibleObject(pacc, &hWnd);
  2611. if ( hWnd != NULL)
  2612. {
  2613. hWndP = GetParent(hWnd);
  2614. LONG_PTR style = GetWindowLongPtr(hWndP, GWL_STYLE);
  2615. if ( style & WS_DISABLED)
  2616. {
  2617. return FALSE;
  2618. }
  2619. }
  2620. DoMore = FALSE; // i.e. speak it but no more children
  2621. }
  2622. case ROLE_SYSTEM_PAGETAB:
  2623. // Hack to not read them if they are disabled...
  2624. // Needed for WIZARD97 style :AK
  2625. {
  2626. HWND hWnd;
  2627. WindowFromAccessibleObject(pacc, &hWnd);
  2628. if ( hWnd != NULL)
  2629. {
  2630. LONG_PTR style = GetWindowLongPtr(hWnd, GWL_STYLE);
  2631. if ( style & WS_DISABLED)
  2632. {
  2633. return FALSE;
  2634. }
  2635. }
  2636. }
  2637. break;
  2638. } // switch
  2639. GetRoleText(varT.lVal, szRole, 128);
  2640. // ROBSI: 10-10-99, BUG? Why (Role == Static) or (IE)??
  2641. if (varT.lVal == ROLE_SYSTEM_STATICTEXT || fInternetExplorer)
  2642. {
  2643. // don't speak role for this
  2644. // speech is better without
  2645. szRole[0] = 0;
  2646. GotStaticText = TRUE;
  2647. }
  2648. }
  2649. else
  2650. {
  2651. szRole[0] = 0; // lstrcpy(szRole, TEXT("UNKNOWN"));
  2652. }
  2653. VariantClear(&varT);
  2654. //
  2655. // Get object name.
  2656. //
  2657. bszT = NULL;
  2658. hr = pacc->get_accName(varChild, &bszT);
  2659. if ( FAILED(hr) )
  2660. bszT = NULL;
  2661. if (bszT)
  2662. {
  2663. #ifdef UNICODE
  2664. lstrcpyn(szName, bszT, MAX_NAME);
  2665. szName[MAX_NAME-1] = TEXT('\0');
  2666. #else
  2667. WideCharToMultiByte(CP_ACP, 0, bszT, -1, szName, MAX_NAME, NULL, NULL);
  2668. #endif
  2669. SysFreeString(bszT);
  2670. if (szName[0] == -1)
  2671. { // name going to be garbage
  2672. LoadString(g_Hinst, IDS_NAMELESS, szName, 256);
  2673. GotNameless = TRUE;
  2674. }
  2675. }
  2676. else
  2677. {
  2678. LoadString(g_Hinst, IDS_NAMELESS, szName, 256);
  2679. GotNameless = TRUE;
  2680. }
  2681. bszT = NULL;
  2682. hr = pacc->get_accValue(varChild, &bszT); // get value string if there is one
  2683. if ( FAILED(hr) )
  2684. bszT = NULL;
  2685. szValue[0] = 0;
  2686. if (bszT)
  2687. {
  2688. #ifdef UNICODE
  2689. lstrcpyn(szValue, bszT, MAX_VALUE);
  2690. szValue[MAX_VALUE-1] = TEXT('\0');
  2691. #else
  2692. WideCharToMultiByte(CP_ACP, 0, bszT, -1, szValue, MAX_VALUE, NULL, NULL);
  2693. #endif
  2694. SysFreeString(bszT);
  2695. }
  2696. // There is no reason for us to speak client client client all time
  2697. if ( GotNameless && lRole & ROLE_SYSTEM_CLIENT && szValue[0] == NULL )
  2698. return TRUE;
  2699. //
  2700. // make sure these are terminated for the compare
  2701. //
  2702. szLastName[MAX_NAME - 1]=TEXT('\0');
  2703. szName[MAX_NAME - 1]=TEXT('\0');
  2704. //
  2705. // don't want to repeat name that OLEACC got from static text
  2706. // so if this name is the same as the previous name - don't speak it.
  2707. //
  2708. if (lstrcmp(szName,szLastName) == 0)
  2709. szName[0] = 0;
  2710. if (GotStaticText)
  2711. {
  2712. lstrcpyn(szLastName, szName, MAX_NAME);
  2713. szLastName[MAX_NAME-1] = TEXT('\0');
  2714. }
  2715. else
  2716. {
  2717. szLastName[0] = 0;
  2718. }
  2719. CAutoArray<TCHAR> aaItemString( new TCHAR[MAX_TEXT] );
  2720. TCHAR * szItemString = aaItemString.Get();
  2721. if ( !szItemString )
  2722. return FALSE; // no memory
  2723. lstrcpyn(szItemString, szName, MAX_TEXT);
  2724. szItemString[MAX_TEXT-1] = TEXT('\0');
  2725. if (fInternetExplorer)
  2726. {
  2727. if (GotText && szName[0] == 0) // no real text
  2728. {
  2729. return FALSE;
  2730. }
  2731. if (GotNameless && szValue[0] == 0) // nameless with no link
  2732. {
  2733. return FALSE;
  2734. }
  2735. if (GotLink/*szValue[0]*/)
  2736. {
  2737. // got a link
  2738. // GotLink = TRUE;
  2739. LoadString(g_Hinst, IDS_LINK, szLink, 32);
  2740. lstrcatn(szItemString,szLink,MAX_TEXT);
  2741. }
  2742. }
  2743. else
  2744. {
  2745. // the focused item has already been read so if the item does not have
  2746. // focus it should be read in this case edit controls.
  2747. if (GotText && ( nAutoRead || !(dwState & STATE_SYSTEM_FOCUSED) ) )
  2748. lstrcatn(szItemString,szValue,MAX_TEXT);
  2749. }
  2750. if (!GotText && !GotLink && !GotGraphic)
  2751. {
  2752. if (lstrlen(szName) && lstrlen(szRole))
  2753. lstrcatn(szItemString,TEXT(", "),MAX_TEXT);
  2754. if (lstrlen(szRole))
  2755. {
  2756. lstrcatn(szItemString,szRole,MAX_TEXT);
  2757. if (lstrlen(szValue) || lstrlen(szState))
  2758. lstrcatn(szItemString, TEXT(", "),MAX_TEXT);
  2759. }
  2760. if (lstrlen(szValue))
  2761. {
  2762. lstrcatn(szItemString,szValue,MAX_TEXT);
  2763. if (lstrlen(szState))
  2764. lstrcatn(szItemString,TEXT(", "),MAX_TEXT);
  2765. }
  2766. if (lstrlen(szState))
  2767. lstrcatn(szItemString,szState,MAX_TEXT);
  2768. // Too much speech of period/comma. Just a space is fine...
  2769. lstrcatn(szItemString, TEXT(" "),MAX_TEXT);
  2770. }
  2771. if (g_pGlobalData->fReviewStyle)
  2772. {
  2773. HWND hwndList;
  2774. if (ObjIndex >= SPATIAL_SIZE) // only store so many
  2775. {
  2776. return DoMore;
  2777. }
  2778. pacc->accLocation(&ObjLocation[LEFT_ID][ObjIndex],
  2779. &ObjLocation[TOP_ID][ObjIndex],
  2780. &ObjLocation[RIGHT_ID][ObjIndex],
  2781. &ObjLocation[BOTTOM_ID][ObjIndex],
  2782. varChild);
  2783. // Dreadfull Hack/heuristic!
  2784. // bin information as it's the left hand side of the HTML help window
  2785. if (fHTMLHelp && (ObjLocation[LEFT_ID][ObjIndex] < nLeftHandSide + 220))
  2786. {
  2787. return DoMore;
  2788. }
  2789. hwndList = GetDlgItem(hwndMSR, IDC_WINDOWINFO);
  2790. SendMessage(hwndList, LB_ADDSTRING, 0, (LPARAM) szItemString);
  2791. ObjLocation[SPOKEN_ID][ObjIndex] = 0;
  2792. ObjIndex++;
  2793. }
  2794. else
  2795. SpeakString(szItemString);
  2796. return DoMore;
  2797. }
  2798. // --------------------------------------------------------------------------
  2799. //
  2800. // AddAccessibleObjects()
  2801. //
  2802. // This is a recursive function. It adds an item for the parent, then
  2803. // adds items for its children if it has any.
  2804. //
  2805. // Parameters:
  2806. // IAccessible* pacc Pointer to an IAccessible interface for the
  2807. // object being added. Start with the 'root' object.
  2808. // VARIANT varChild Variant that contains the ID of the child object
  2809. // to retrieve.
  2810. //
  2811. // The first call, pIAcc points to the top level window object,
  2812. // varChild is a variant that is VT_I4, CHILDID_SELF
  2813. //
  2814. // ISSUE: The calling code isn't prepared to deal with errors so I'm making
  2815. // the return void. A re-engineered version should do better error handling.
  2816. //
  2817. void AddAccessibleObjects(IAccessible* pIAcc, const VARIANT &varChild)
  2818. {
  2819. if (varChild.vt != VT_I4)
  2820. {
  2821. DBPRINTF(TEXT("BUG??: Got child ID other than VT_I4 (%d)\r\n"), varChild.vt);
  2822. return;
  2823. }
  2824. // Find the window class so we can find embedded trdent windows
  2825. HWND hwndCurrentObj;
  2826. TCHAR szClassName[64];
  2827. if ( WindowFromAccessibleObject( pIAcc, &hwndCurrentObj ) == S_OK )
  2828. {
  2829. if ( GetClassName( hwndCurrentObj, szClassName, ARRAYSIZE(szClassName) ) )
  2830. {
  2831. // is it Internet Explorer in any of its many forms?
  2832. g_pGlobalData->fInternetExplorer = IsTridentWindow(szClassName);
  2833. }
  2834. }
  2835. // Add the object itself and, if AddItem determines it wants children, do those
  2836. if (AddItem(pIAcc, varChild))
  2837. {
  2838. // Traverse the children to see if any of them should be added
  2839. if (varChild.lVal != CHILDID_SELF)
  2840. return; // only do this for container objects
  2841. // Loop through pIAcc's children.
  2842. long cChildren = 0;
  2843. pIAcc->get_accChildCount(&cChildren);
  2844. if (!cChildren)
  2845. return; // no children
  2846. // Allocate memory for the array of child variants
  2847. CAutoArray<VARIANT> aaChildren( new VARIANT [cChildren] );
  2848. VARIANT * pavarChildren = aaChildren.Get();
  2849. if( ! pavarChildren )
  2850. {
  2851. DBPRINTF(TEXT("Error: E_OUTOFMEMORY allocating pavarChildren\r\n"));
  2852. return;
  2853. }
  2854. long cObtained = 0;
  2855. HRESULT hr = AccessibleChildren( pIAcc, 0L, cChildren, pavarChildren, & cObtained );
  2856. if( hr != S_OK )
  2857. {
  2858. DBPRINTF(TEXT("Error: AccessibleChildren returns 0x%x\r\n"), hr);
  2859. return;
  2860. }
  2861. else if( cObtained != cChildren)
  2862. {
  2863. DBPRINTF(TEXT("Error: get_accChildCount returned %d but AccessibleChildren returned %d\r\n"), cChildren, cObtained);
  2864. return;
  2865. }
  2866. else
  2867. {
  2868. // Loop through VARIANTs in ARRAY. Object_Normalize returns a proper
  2869. // pAccChild and varAccChild pair regardless of whether the array
  2870. // element is VT_DISPATCH or VT_I4.
  2871. for( int i = 0 ; i < cChildren ; i++ )
  2872. {
  2873. IAccessible * pAccChild = NULL;
  2874. VARIANT varAccChild;
  2875. // Object_Normalize consumes the variant, so no VariantClear() needed.
  2876. if( Object_Normalize( pIAcc, & pavarChildren[ i ], & pAccChild, & varAccChild ) )
  2877. {
  2878. AddAccessibleObjects(pAccChild, varAccChild);
  2879. pAccChild->Release();
  2880. }
  2881. }
  2882. }
  2883. }
  2884. }
  2885. // --------------------------------------------------------------------------
  2886. // Helper method to filter out bogus focus events...
  2887. // Returns FALSE if the focus event is bogus, Otherwise returns TRUE
  2888. // a-anilk: 05-28-99
  2889. // --------------------------------------------------------------------------
  2890. BOOL IsFocussedItem( HWND hWnd, IAccessible * pAcc, VARIANT varChild )
  2891. {
  2892. TCHAR buffer[100];
  2893. GetClassName(hWnd,buffer,100);
  2894. // Is it toolbar, We cannot determine who had focus!!!
  2895. if ((lstrcmpi(buffer, CLASS_TOOLBAR) == 0) ||
  2896. (lstrcmpi(buffer, CLASS_IE_MAINWND) == 0))
  2897. return TRUE;
  2898. VARIANT varState;
  2899. HRESULT hr;
  2900. VariantInit(&varState);
  2901. hr = pAcc->get_accState(varChild, &varState);
  2902. if ( hr == S_OK)
  2903. {
  2904. if ( ! (varState.lVal & STATE_SYSTEM_FOCUSED) )
  2905. return FALSE;
  2906. }
  2907. else if (FAILED(hr)) // ROBSI: 10-11-99. If OLEACC returns an error, assume no focus.
  2908. {
  2909. return FALSE;
  2910. }
  2911. return TRUE;
  2912. }
  2913. #define TAB_KEY 0x09
  2914. #define CURLY_KEY 0x7B
  2915. // Helper method Filters GUID's that can appear in names: AK
  2916. void FilterGUID(TCHAR* szSpeak)
  2917. {
  2918. // the GUID's have a Tab followed by a {0087....
  2919. // If you find this pattern. Then donot speak that:AK
  2920. TCHAR *szSpeakBegin = szSpeak;
  2921. // make sure the we don't go over MAX_TEXT.
  2922. while(*szSpeak != NULL && (szSpeak-szSpeakBegin < MAX_TEXT-1))
  2923. {
  2924. if ( (*szSpeak == TAB_KEY) &&
  2925. (*(++szSpeak) == CURLY_KEY) )
  2926. {
  2927. *(--szSpeak) = NULL;
  2928. return;
  2929. }
  2930. szSpeak++;
  2931. }
  2932. }
  2933. ///////////////////////////////////////////////////////////////////////////
  2934. // Helper functions
  2935. // Convert an IDispatch to an IAccessible/varChild pair (Releases the IDispatch)
  2936. BOOL Object_IDispatchToIAccessible( IDispatch * pdisp,
  2937. IAccessible ** ppAccOut,
  2938. VARIANT * pvarChildOut)
  2939. {
  2940. IAccessible * pAccTemp = NULL;
  2941. HRESULT hr = pdisp->QueryInterface( IID_IAccessible, (void**) & pAccTemp );
  2942. pdisp->Release();
  2943. if( hr != S_OK || ! pAccTemp )
  2944. {
  2945. return FALSE;
  2946. }
  2947. *ppAccOut = pAccTemp;
  2948. if( pvarChildOut )
  2949. {
  2950. InitChildSelf(pvarChildOut);
  2951. }
  2952. return TRUE;
  2953. }
  2954. // Given an IAccessible and a variant (may be I4 or DISP), returns a 'canonical'
  2955. // IAccessible/varChild, using getChild, etc.
  2956. // The variant is consumed.
  2957. BOOL Object_Normalize( IAccessible * pAcc,
  2958. VARIANT * pvarChild,
  2959. IAccessible ** ppAccOut,
  2960. VARIANT * pvarChildOut)
  2961. {
  2962. BOOL fRv = FALSE;
  2963. if( pvarChild->vt == VT_DISPATCH )
  2964. {
  2965. fRv = Object_IDispatchToIAccessible( pvarChild->pdispVal, ppAccOut, pvarChildOut );
  2966. }
  2967. else if( pvarChild->vt == VT_I4 )
  2968. {
  2969. if( pvarChild->lVal == CHILDID_SELF )
  2970. {
  2971. // No normalization necessary...
  2972. pAcc->AddRef();
  2973. *ppAccOut = pAcc;
  2974. pvarChildOut->vt = VT_I4;
  2975. pvarChildOut->lVal = pvarChild->lVal;
  2976. fRv = TRUE;
  2977. } else
  2978. {
  2979. // Might still be a full object - try get_accChild...
  2980. IDispatch * pdisp = NULL;
  2981. HRESULT hr = pAcc->get_accChild( *pvarChild, & pdisp );
  2982. if( hr == S_OK && pdisp )
  2983. {
  2984. // It's a full object...
  2985. fRv = Object_IDispatchToIAccessible( pdisp, ppAccOut, pvarChildOut );
  2986. }
  2987. else
  2988. {
  2989. // Just a regular leaf node...
  2990. pAcc->AddRef();
  2991. *ppAccOut = pAcc;
  2992. pvarChildOut->vt = VT_I4;
  2993. pvarChildOut->lVal = pvarChild->lVal;
  2994. fRv = TRUE;
  2995. }
  2996. }
  2997. }
  2998. else
  2999. {
  3000. DBPRINTF( TEXT("Object_Normalize unexpected error") );
  3001. *ppAccOut = NULL; // unexpected error...
  3002. VariantClear( pvarChild );
  3003. fRv = FALSE;
  3004. }
  3005. return fRv;
  3006. }
  3007. /*************************************************************************
  3008. THE INFORMATION AND CODE PROVIDED HEREUNDER (COLLECTIVELY REFERRED TO
  3009. AS "SOFTWARE") IS PROVIDED AS IS WITHOUT WARRANTY OF ANY KIND, EITHER
  3010. EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
  3011. WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN
  3012. NO EVENT SHALL MICROSOFT CORPORATION OR ITS SUPPLIERS BE LIABLE FOR
  3013. ANY DAMAGES WHATSOEVER INCLUDING DIRECT, INDIRECT, INCIDENTAL,
  3014. CONSEQUENTIAL, LOSS OF BUSINESS PROFITS OR SPECIAL DAMAGES, EVEN IF
  3015. MICROSOFT CORPORATION OR ITS SUPPLIERS HAVE BEEN ADVISED OF THE
  3016. POSSIBILITY OF SUCH DAMAGES. SOME STATES DO NOT ALLOW THE EXCLUSION OR
  3017. LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES SO THE
  3018. FOREGOING LIMITATION MAY NOT APPLY.
  3019. *************************************************************************/