diff --git a/config.cc b/config.cc index c8537a19..addca5cd 100644 --- a/config.cc +++ b/config.cc @@ -102,6 +102,9 @@ Preferences::Preferences(): scanPopupModifiers( 0 ), scanPopupAltMode( false ), scanPopupAltModeSecs( 3 ), + scanPopupUseUIAutomation( false ), + scanPopupUseIAccessibleEx( false ), + scanPopupUseGDMessage( false ), pronounceOnLoadMain( false ), pronounceOnLoadPopup( false ), #ifdef Q_WS_WIN @@ -597,6 +600,9 @@ Class load() throw( exError ) c.preferences.scanPopupAltMode = ( preferences.namedItem( "scanPopupAltMode" ).toElement().text() == "1" ); if ( !preferences.namedItem( "scanPopupAltModeSecs" ).isNull() ) c.preferences.scanPopupAltModeSecs = preferences.namedItem( "scanPopupAltModeSecs" ).toElement().text().toUInt(); + c.preferences.scanPopupUseUIAutomation = ( preferences.namedItem( "scanPopupUseUIAutomation" ).toElement().text() == "1" ); + c.preferences.scanPopupUseIAccessibleEx = ( preferences.namedItem( "scanPopupUseIAccessibleEx" ).toElement().text() == "1" ); + c.preferences.scanPopupUseGDMessage = ( preferences.namedItem( "scanPopupUseGDMessage" ).toElement().text() == "1" ); c.preferences.pronounceOnLoadMain = ( preferences.namedItem( "pronounceOnLoadMain" ).toElement().text() == "1" ); c.preferences.pronounceOnLoadPopup = ( preferences.namedItem( "pronounceOnLoadPopup" ).toElement().text() == "1" ); @@ -1106,6 +1112,18 @@ void save( Class const & c ) throw( exError ) opt.appendChild( dd.createTextNode( QString::number( c.preferences.scanPopupAltModeSecs ) ) ); preferences.appendChild( opt ); + opt = dd.createElement( "scanPopupUseUIAutomation" ); + opt.appendChild( dd.createTextNode( c.preferences.scanPopupUseUIAutomation ? "1":"0" ) ); + preferences.appendChild( opt ); + + opt = dd.createElement( "scanPopupUseIAccessibleEx" ); + opt.appendChild( dd.createTextNode( c.preferences.scanPopupUseIAccessibleEx ? "1":"0" ) ); + preferences.appendChild( opt ); + + opt = dd.createElement( "scanPopupUseGDMessage" ); + opt.appendChild( dd.createTextNode( c.preferences.scanPopupUseGDMessage ? "1":"0" ) ); + preferences.appendChild( opt ); + opt = dd.createElement( "pronounceOnLoadMain" ); opt.appendChild( dd.createTextNode( c.preferences.pronounceOnLoadMain ? "1" : "0" ) ); preferences.appendChild( opt ); diff --git a/config.hh b/config.hh index 5089f33c..ff4dc0dc 100644 --- a/config.hh +++ b/config.hh @@ -159,6 +159,9 @@ struct Preferences unsigned long scanPopupModifiers; // Combination of KeyboardState::Modifier bool scanPopupAltMode; // When you press modifier shortly after the selection unsigned scanPopupAltModeSecs; + bool scanPopupUseUIAutomation; + bool scanPopupUseIAccessibleEx; + bool scanPopupUseGDMessage; // Whether the word should be pronounced on page load, in main window/popup bool pronounceOnLoadMain, pronounceOnLoadPopup; diff --git a/goldendict.pro b/goldendict.pro index ec58f41e..ec8ce8af 100644 --- a/goldendict.pro +++ b/goldendict.pro @@ -37,7 +37,10 @@ win32 { LIBS += -liconv \ -lwsock32 \ -lwinmm \ - -lpsapi + -lpsapi \ + -lole32 \ + -loleaut32 \ + -ladvapi32 LIBS += -lvorbisfile \ -lvorbis \ -logg \ @@ -255,8 +258,12 @@ SOURCES += folding.cc \ maintabwidget.cc \ mainstatusbar.cc win32 { - SOURCES += mouseover_win32/ThTypes.c - HEADERS += mouseover_win32/ThTypes.h + SOURCES += mouseover_win32/ThTypes.c \ + wordbyauto.cc \ + guids.c + HEADERS += mouseover_win32/ThTypes.h \ + wordbyauto.hh \ + uiauto.hh } RESOURCES += resources.qrc \ flags.qrc diff --git a/guids.c b/guids.c new file mode 100644 index 00000000..4cb2fdc5 --- /dev/null +++ b/guids.c @@ -0,0 +1,9 @@ +#define INITGUID +#include + +DEFINE_GUID(IID_IUIAutomation, 0x30cbe57d, 0xd9d0, 0x452a, 0xab, 0x13, 0x7a, 0xc5, 0xac, 0x48, 0x25, 0xee); +DEFINE_GUID(CLSID_CUIAutomation, 0xff48dba4, 0x60ef, 0x4201, 0xaa, 0x87, 0x54, 0x10, 0x3e, 0xef, 0x59, 0x4e); +DEFINE_GUID(IID_IUIAutomationElement, 0xd22108aa, 0x8ac5, 0x49a5, 0x83, 0x7b, 0x37, 0xbb, 0xb3, 0xd7, 0x59, 0x1e); +DEFINE_GUID(IID_IUIAutomationTextPattern, 0x32eba289, 0x3583, 0x42c9, 0x9c, 0x59, 0x3b, 0x6d, 0x9a, 0x1e, 0x9b, 0x6a); +DEFINE_GUID(IID_IUIAutomationTextRange, 0xa543cc6a, 0xf4ae, 0x494b, 0x82, 0x39, 0xc8, 0x14, 0x48, 0x11, 0x87, 0xa8); +DEFINE_GUID(IID_IUIAutomationTreeWalker, 0x4042c624, 0x389c, 0x4afc, 0xa6, 0x30, 0x9d, 0xf8, 0x54, 0xa5, 0x41, 0xfc); diff --git a/mouseover.cc b/mouseover.cc index 90ec3c6d..545a6f00 100644 --- a/mouseover.cc +++ b/mouseover.cc @@ -5,7 +5,13 @@ #include #ifdef Q_OS_WIN32 +#undef WINVER +#define WINVER 0x0500 +#include +#include +#include #include "mouseover_win32/ThTypes.h" +#include "wordbyauto.hh" #endif MouseOver & MouseOver::instance() @@ -18,12 +24,71 @@ MouseOver & MouseOver::instance() #ifdef Q_OS_WIN32 const UINT WM_MY_SHOW_TRANSLATION = WM_USER + 301; static wchar_t className[] = L"GoldenDictMouseover"; +typedef BOOL WINAPI ( *ChangeWindowMessageFilterFunc )( UINT, DWORD ); + +#ifndef CHANGEFILTERSTRUCT +typedef struct tagCHANGEFILTERSTRUCT { + DWORD cbSize; + DWORD ExtStatus; +} CHANGEFILTERSTRUCT, *PCHANGEFILTERSTRUCT; #endif +typedef BOOL WINAPI ( *ChangeWindowMessageFilterExFunc )( HWND, UINT, DWORD, PCHANGEFILTERSTRUCT ); + +#endif // Q_OS_WIN32 + +#ifdef Q_OS_WIN32 + +extern "C" BOOL WINAPI ConvertStringSecurityDescriptorToSecurityDescriptorW( + LPCWSTR StringSecurityDescriptor, + DWORD StringSDRevision, + PSECURITY_DESCRIPTOR *SecurityDescriptor, + PULONG SecurityDescriptorSize ); + + +static void SetLowLabelToGDSynchroObjects() +{ +// The LABEL_SECURITY_INFORMATION SDDL SACL to be set for low integrity +#define LOW_INTEGRITY_SDDL_SACL_W L"S:(ML;;NW;;;LW)" + DWORD dwErr = ERROR_SUCCESS; + PSECURITY_DESCRIPTOR pSD = NULL; + + PACL pSacl = NULL; // not allocated + BOOL fSaclPresent = FALSE; + BOOL fSaclDefaulted = FALSE; + LPCWSTR pwszMapFileName = L"GoldenDictTextOutHookSharedMem"; + LPCWSTR pwszSpyMutexName = L"GoldenDictTextOutSpyMutex"; + LPCWSTR pwszHookMutexName = L"GoldenDictTextOutHookMutex"; + + if( ConvertStringSecurityDescriptorToSecurityDescriptorW( LOW_INTEGRITY_SDDL_SACL_W, 1 /* SDDL_REVISION_1 */, &pSD, NULL ) ) + { + if( GetSecurityDescriptorSacl(pSD, &fSaclPresent, &pSacl, &fSaclDefaulted)) + { +// Note that psidOwner, psidGroup, and pDacl are +// all NULL and set the new LABEL_SECURITY_INFORMATION + + dwErr = SetNamedSecurityInfoW( (LPWSTR)pwszMapFileName, + SE_KERNEL_OBJECT, LABEL_SECURITY_INFORMATION, NULL, NULL, NULL, pSacl); + + dwErr = SetNamedSecurityInfoW( (LPWSTR)pwszSpyMutexName, + SE_KERNEL_OBJECT, LABEL_SECURITY_INFORMATION, NULL, NULL, NULL, pSacl); + + dwErr = SetNamedSecurityInfoW( (LPWSTR)pwszHookMutexName, + SE_KERNEL_OBJECT, LABEL_SECURITY_INFORMATION, NULL, NULL, NULL, pSacl); + + } + LocalFree(pSD); + } +} + +#endif // Q_OS_WIN32 + MouseOver::MouseOver() { #ifdef Q_OS_WIN32 - +HMODULE hm; +ChangeWindowMessageFilterFunc changeWindowMessageFilterFunc = NULL; +ChangeWindowMessageFilterExFunc changeWindowMessageFilterExFunc = NULL; mouseOverEnabled = false; ThTypes_Init(); @@ -37,17 +102,17 @@ MouseOver::MouseOver() wcex.cbSize = sizeof( WNDCLASSEX ); - wcex.style = 0; - wcex.lpfnWndProc = ( WNDPROC ) eventHandler; - wcex.cbClsExtra = 0; - wcex.cbWndExtra = 0; - wcex.hInstance = GetModuleHandle( 0 ); - wcex.hIcon = NULL; - wcex.hCursor = NULL, - wcex.hbrBackground = NULL; - wcex.lpszMenuName = NULL; - wcex.lpszClassName = className; - wcex.hIconSm = NULL; + wcex.style = 0; + wcex.lpfnWndProc = ( WNDPROC ) eventHandler; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = GetModuleHandle( 0 ); + wcex.hIcon = NULL; + wcex.hCursor = NULL, + wcex.hbrBackground = NULL; + wcex.lpszMenuName = NULL; + wcex.lpszClassName = className; + wcex.hIconSm = NULL; RegisterClassEx( &wcex ); @@ -58,6 +123,23 @@ MouseOver::MouseOver() if ( spyDll ) activateSpyFn = ( ActivateSpyFn ) GetProcAddress( spyDll, "ActivateTextOutSpying" ); +// Allow messages from low intehrity process - for Vista and Win7 + hm = GetModuleHandle( __TEXT("user32.dll")); + if ( hm != NULL ) { + changeWindowMessageFilterExFunc = (ChangeWindowMessageFilterExFunc)GetProcAddress( hm, "ChangeWindowMessageFilterEx" ); + if( changeWindowMessageFilterExFunc ) { + CHANGEFILTERSTRUCT cfs = { sizeof( CHANGEFILTERSTRUCT ), 0 }; + changeWindowMessageFilterExFunc( GlobalData->ServerWND, WM_MY_SHOW_TRANSLATION, 1 /* MSGFLT_ALLOW */, &cfs ); + } else { + changeWindowMessageFilterFunc = (ChangeWindowMessageFilterFunc)GetProcAddress( hm, "ChangeWindowMessageFilter" ); + if( changeWindowMessageFilterFunc ) + changeWindowMessageFilterFunc( WM_MY_SHOW_TRANSLATION, 1 /* MSGFLT_ADD */ ); + } + } + +//Allow object access from low intehrity process - for Vista and Win7 + SetLowLabelToGDSynchroObjects(); + #endif } @@ -90,46 +172,72 @@ LRESULT CALLBACK MouseOver::eventHandler( HWND hwnd, UINT msg, { if ( msg == WM_MY_SHOW_TRANSLATION ) { - // Don't handle word without necessity + /// Don't handle word without necessity + bool bNeed = false; + emit instance().askNeedWord( &bNeed ); + if( !bNeed ) return 0; - bool bNeedHandle = false; - emit instance().askNeedWord( &bNeedHandle ); - if( !bNeedHandle ) return 0; + if( wparam != 0) { //Ask for methods of word retrieving + LRESULT ret = GD_FLAG_METHOD_STANDARD; + emit instance().askUseGDMessage( &bNeed ); + if( bNeed ) + ret |= GD_FLAG_METHOD_GD_MESSAGE; + emit instance().askUseIAccessibleEx( &bNeed ); + if( bNeed ) + ret |= GD_FLAG_METHOD_IACCESSIBLEEX; + emit instance().askUseUIAutomation( &bNeed ); + if( bNeed ) + ret |= GD_FLAG_METHOD_UI_AUTOMATION; + return ret; + } - int wordSeqPos; + int wordSeqPos = 0; QString wordSeq; - // Is the string in utf8 or in locale encoding? - - gd::wchar testBuf[ 256 ]; - - long result = Utf8::decode( GlobalData->CurMod.MatchedWord, - strlen( GlobalData->CurMod.MatchedWord ), - testBuf ); - - if ( result >= 0 ) + if( GlobalData->CurMod.WordLen == 0) { - // It seems to be - QString begin = QString::fromUtf8( GlobalData->CurMod.MatchedWord, - GlobalData->CurMod.BeginPos ).normalized( QString::NormalizationForm_C ); - - QString end = QString::fromUtf8( GlobalData->CurMod.MatchedWord + - GlobalData->CurMod.BeginPos ).normalized( QString::NormalizationForm_C ); - - wordSeq = begin + end; - wordSeqPos = begin.size(); + emit instance().askUseUIAutomation( &bNeed ); + if( !bNeed ) return 0; + POINT pt = GlobalData->CurMod.Pt; + WCHAR *pwstr = gdGetWordAtPointByAutomation( pt ); + if( pwstr == NULL ) return 0; + wordSeq = QString::fromWCharArray( pwstr ); } else { - // It's not, so interpret it as in local encoding - QString begin = QString::fromLocal8Bit( GlobalData->CurMod.MatchedWord, - GlobalData->CurMod.BeginPos ).normalized( QString::NormalizationForm_C ); - QString end = QString::fromLocal8Bit( GlobalData->CurMod.MatchedWord + - GlobalData->CurMod.BeginPos ).normalized( QString::NormalizationForm_C ); + // Is the string in utf8 or in locale encoding? - wordSeq = begin + end; - wordSeqPos = begin.size(); + gd::wchar testBuf[ 256 ]; + + long result = Utf8::decode( GlobalData->CurMod.MatchedWord, + strlen( GlobalData->CurMod.MatchedWord ), + testBuf ); + + if ( result >= 0 ) + { + // It seems to be + QString begin = QString::fromUtf8( GlobalData->CurMod.MatchedWord, + GlobalData->CurMod.BeginPos ).normalized( QString::NormalizationForm_C ); + + QString end = QString::fromUtf8( GlobalData->CurMod.MatchedWord + + GlobalData->CurMod.BeginPos ).normalized( QString::NormalizationForm_C ); + + wordSeq = begin + end; + wordSeqPos = begin.size(); + } + else + { + // It's not, so interpret it as in local encoding + QString begin = QString::fromLocal8Bit( GlobalData->CurMod.MatchedWord, + GlobalData->CurMod.BeginPos ).normalized( QString::NormalizationForm_C ); + + QString end = QString::fromLocal8Bit( GlobalData->CurMod.MatchedWord + + GlobalData->CurMod.BeginPos ).normalized( QString::NormalizationForm_C ); + + wordSeq = begin + end; + wordSeqPos = begin.size(); + } } // Now locate the word inside the sequence @@ -139,7 +247,7 @@ LRESULT CALLBACK MouseOver::eventHandler( HWND hwnd, UINT msg, if ( wordSeq[ wordSeqPos ].isSpace() ) { // Currently we ignore such cases - return DefWindowProc( hwnd, msg, wparam, lparam ); + return 0; } else if ( !wordSeq[ wordSeqPos ].isLetterOrNumber() ) @@ -163,7 +271,7 @@ LRESULT CALLBACK MouseOver::eventHandler( HWND hwnd, UINT msg, if ( end - begin == 1 ) { // Well, turns out it was just a single non-letter char, discard it - return DefWindowProc( hwnd, msg, wparam, lparam ); + return 0; } word = wordSeq.mid( begin, end - begin ); @@ -203,6 +311,7 @@ LRESULT CALLBACK MouseOver::eventHandler( HWND hwnd, UINT msg, } emit instance().hovered( word ); + return 0; } return DefWindowProc( hwnd, msg, wparam, lparam ); diff --git a/mouseover.hh b/mouseover.hh index 9e38d3c9..48011fb0 100644 --- a/mouseover.hh +++ b/mouseover.hh @@ -33,8 +33,19 @@ signals: /// Emitted when there was some text under cursor which was hovered over. void hovered( QString const & ); + +#ifdef Q_OS_WIN32 + /// Ask for need to handle word under cursor void askNeedWord( bool * ); + /// Ask for use UI Automation interfaces to get word under cursor + void askUseUIAutomation( bool * ); + /// Ask for use IAccessibleEx interface to get word under cursor + void askUseIAccessibleEx( bool * ); + /// Ask for use GD message to get word under cursor + void askUseGDMessage( bool * ); + +#endif private: diff --git a/mouseover_win32/GetWordByIAccEx.cc b/mouseover_win32/GetWordByIAccEx.cc new file mode 100644 index 00000000..16c0a0e6 --- /dev/null +++ b/mouseover_win32/GetWordByIAccEx.cc @@ -0,0 +1,108 @@ +#include +#include +#include "ThTypes.h" +#include "IAccExInt.h" +#include "GetWordByIAccEx.h" + +GetPhysicalCursorPosFunc getPhysicalCursorPosFunc; + +BOOL FindGetPhysicalCursorPos() +{ +HMODULE hm; + getPhysicalCursorPosFunc = NULL; + hm = GetModuleHandle( "user32.dll" ); + if( hm != NULL ) { + getPhysicalCursorPosFunc = (GetPhysicalCursorPosFunc)GetProcAddress( hm, "GetPhysicalCursorPos" ); + } + return( getPhysicalCursorPosFunc != NULL ); +} + + +HRESULT GetParentAccessibleObject( IAccessible* pAcc, IAccessible** ppAccParent ) +{ +IDispatch* pDispatch = NULL; + + *ppAccParent = NULL; + if ( pAcc == NULL ) + return E_INVALIDARG; + HRESULT hr = pAcc->get_accParent( &pDispatch ); + if ( ( hr == S_OK ) && ( pDispatch != NULL ) ) { + hr = pDispatch->QueryInterface( IID_IAccessible, (void**)ppAccParent ); + pDispatch->Release(); + } + return hr; +} + + +BOOL getWordByAccEx( POINT pt ) +{ +HRESULT hr; +IAccessible *pAcc, *pAccParent; +ITextProvider *pText; +ITextRangeProvider *pTextRange; +VARIANT var; +long idChild; +BSTR bstr = NULL; +int n; +UiaPoint upt; +POINT ppt = { 0, 0 }; + + if( getPhysicalCursorPosFunc != NULL ) { + getPhysicalCursorPosFunc( &ppt ); + } else { + ppt = pt; + } + + upt.x = ppt.x; + upt.y = ppt.y; + + pAcc = NULL; + hr = AccessibleObjectFromPoint( ppt, &pAcc, &var ); + idChild = var.lVal; + + if( hr != S_OK || pAcc == NULL) { + VariantClear( &var ); + return FALSE; + } + + pText = NULL; + while( pAcc != NULL) { + hr = GetPatternFromIAccessible( pAcc, 0, UIA_TextPatternId, IID_ITextProvider, (void **)&pText ); + if( hr == S_OK && pText != NULL ) + break; + pAccParent = NULL; + hr = GetParentAccessibleObject( pAcc, &pAccParent ); + pAcc->Release(); + pAcc = pAccParent; + } + if( pAcc == NULL ) + return FALSE; + + pAcc->Release(); + + pTextRange = NULL; + hr = pText->RangeFromPoint( upt, &pTextRange ); + if( hr != S_OK || pTextRange == NULL ) { + pText->Release(); + return FALSE; + } + + hr = pTextRange->ExpandToEnclosingUnit( TextUnit_Word ); + if( hr == S_OK) { + bstr = NULL; + hr = pTextRange->GetText( 255, &bstr ); + if (hr == S_OK) { + n = SysStringLen( bstr ); + if( n != 0 ) { + n = WideCharToMultiByte( CP_UTF8, 0, (LPCWSTR)bstr, n, GlobalData->CurMod.MatchedWord, sizeof( GlobalData->CurMod.MatchedWord ) - 1, NULL, NULL ); + GlobalData->CurMod.WordLen = n; + GlobalData->CurMod.MatchedWord[n] = 0; + } + SysFreeString( bstr ); + } + } + pTextRange->Release(); + pText->Release(); + + return TRUE; +} diff --git a/mouseover_win32/GetWordByIAccEx.h b/mouseover_win32/GetWordByIAccEx.h new file mode 100644 index 00000000..ace0c8b7 --- /dev/null +++ b/mouseover_win32/GetWordByIAccEx.h @@ -0,0 +1,19 @@ +#ifndef __GetWordByIAccEx_H_DEFINED_ +#define __GetWordByIAccEx_H_DEFINED_ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef BOOL (*GetPhysicalCursorPosFunc)(LPPOINT); +extern GetPhysicalCursorPosFunc getPhysicalCursorPosFunc; + +BOOL FindGetPhysicalCursorPos(); + +BOOL getWordByAccEx( POINT pt ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/mouseover_win32/HookImportFunction.c b/mouseover_win32/HookImportFunction.c index 06fec359..d4b4287c 100644 --- a/mouseover_win32/HookImportFunction.c +++ b/mouseover_win32/HookImportFunction.c @@ -87,8 +87,10 @@ static BOOL HookImportFunction(HMODULE hModule, LPCSTR szImportModule, LPCSTR sz MEMORY_BASIC_INFORMATION mbi_thunk; DWORD dwOldProtect; - VirtualQuery(pRealThunk, &mbi_thunk, sizeof(MEMORY_BASIC_INFORMATION)); - VirtualProtect(mbi_thunk.BaseAddress, mbi_thunk.RegionSize, PAGE_READWRITE, &mbi_thunk.Protect); + if( !VirtualQuery(pRealThunk, &mbi_thunk, sizeof(MEMORY_BASIC_INFORMATION)) ) + return FALSE; + if( !VirtualProtect(mbi_thunk.BaseAddress, mbi_thunk.RegionSize, PAGE_READWRITE, &mbi_thunk.Protect) ) + return FALSE; if (paOrigFuncs) *paOrigFuncs = (PROC)pRealThunk->u1.Function; pRealThunk->u1.Function = (DWORD)paHookFuncs; diff --git a/mouseover_win32/IAccEx.cc b/mouseover_win32/IAccEx.cc new file mode 100644 index 00000000..70dd8415 --- /dev/null +++ b/mouseover_win32/IAccEx.cc @@ -0,0 +1,90 @@ +#include +#include +#include "IAccExInt.h" + +HRESULT GetIAccessibleExFromIAccessible( IAccessible *pAcc, long idChild, IAccessibleEx **ppaex ) +{ +*ppaex = NULL; +IAccessibleEx *paex; +IServiceProvider *pSp = NULL; +//char s[500]; + HRESULT hr = pAcc->QueryInterface( IID_IServiceProvider, (void **)&pSp ); +/* + wsprintf(s,"GD:QueryInterface (IServiceProvider) return hr=%08X, ptr=%p\n", hr, pSp); + OutputDebugString(s); +*/ + if( hr != S_OK ) return hr; + if( pSp == NULL ) return E_NOINTERFACE; + + paex = NULL; + hr = pSp->QueryService( IID_IAccessibleEx, IID_IAccessibleEx, (void **)&paex ); + pSp->Release(); +/* + wsprintf(s,"GD:QueryService (IAccessibleEx) return hr=%08X, ptr=%p\n", hr, paex); + OutputDebugString(s); +*/ + if( hr != S_OK ) return hr; + if( paex == NULL ) return E_NOINTERFACE; + + if(idChild == CHILDID_SELF) { + *ppaex = paex; + } else { + IAccessibleEx *paexChild = NULL; + hr = paex->GetObjectForChild( idChild, &paexChild ); +/* + wsprintf(s,"GD: GetObjectForChild return hr=%08X, ptr=%p (ChildID=%i)\n", hr, paexChild, idChild); + OutputDebugString(s); +*/ + paex->Release(); + if( hr != S_OK ) return hr; + if(paexChild == NULL) return E_NOINTERFACE; + *ppaex = paexChild; + } + return S_OK; +} + +HRESULT GetIRawElementProviderFromIAccessible( IAccessible * pAcc, long idChild, IRawElementProviderSimple **ppEl ) +{ +*ppEl = NULL; +IAccessibleEx *paex; +//char s[500]; + HRESULT hr = GetIAccessibleExFromIAccessible( pAcc, idChild, &paex ); + if( hr != S_OK ) return hr; + + hr = paex->QueryInterface( IID_IRawElementProviderSimple, (void **)ppEl ); +/* + wsprintf(s,"GD:QueryInterface (IRawElementProviderSimple) return hr=%08X, ptr=%p\n", hr, ppEl); + OutputDebugString(s); +*/ + paex->Release(); + return hr; +} + +HRESULT GetPatternFromIAccessible( IAccessible * pAcc, long idChild, PATTERNID patternId, REFIID iid, void **ppv ) +{ +IRawElementProviderSimple * pel; +//char s[500]; + HRESULT hr = GetIRawElementProviderFromIAccessible( pAcc, idChild, &pel ); + if( hr != S_OK ) return hr; + if( pel == NULL ) return E_NOINTERFACE; + + IUnknown * pPatternObject = NULL; + hr = pel->GetPatternProvider( patternId, &pPatternObject ); +/* + wsprintf(s,"GD:GetPatternProvider return hr=%08X, ptr=%p\n", hr, pPatternObject); + OutputDebugString(s); +*/ + pel->Release(); + if( hr != S_OK ) return hr; + if( pPatternObject == NULL ) return E_NOINTERFACE; + + *ppv = NULL; + hr = pPatternObject->QueryInterface( iid, ppv ); +/* + wsprintf(s,"GD:QueryInterface (TextPattern) return hr=%08X, ptr=%p\n", hr, ppv); + OutputDebugString(s); +*/ + pPatternObject->Release(); + if( *ppv == NULL ) return E_NOINTERFACE; + return hr; +} diff --git a/mouseover_win32/IAccExInt.h b/mouseover_win32/IAccExInt.h new file mode 100644 index 00000000..1477483d --- /dev/null +++ b/mouseover_win32/IAccExInt.h @@ -0,0 +1,157 @@ +#ifndef __UIAUTO_HH_INCLUDED__ +#define __UIAUTO_HH_INCLUDED__ + +//#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +EXTERN_C const IID IID_IAccessibleEx; +EXTERN_C const IID IID_IRawElementProviderSimple; +EXTERN_C const IID IID_ITextProvider; +EXTERN_C const IID IID_ITextRangeProvider; + +typedef interface IAccessibleEx IAccessibleEx; +typedef interface IRawElementProviderSimple IRawElementProviderSimple; +typedef interface ITextProvider ITextProvider; +typedef interface ITextRangeProvider ITextRangeProvider; + +typedef int PROPERTYID; +typedef int PATTERNID; +typedef int TEXTATTRIBUTEID; + +struct UiaPoint +{ + double x; + double y; +}; + +enum ProviderOptions +{ + ProviderOptions_ClientSideProvider = 0x1, + ProviderOptions_ServerSideProvider = 0x2, + ProviderOptions_NonClientAreaProvider = 0x4, + ProviderOptions_OverrideProvider = 0x8, + ProviderOptions_ProviderOwnsSetFocus = 0x10, + ProviderOptions_UseComThreading = 0x20 +}; + +enum SupportedTextSelection +{ + SupportedTextSelection_None = 0, + SupportedTextSelection_Single = 1, + SupportedTextSelection_Multiple = 2 +}; + +enum TextUnit +{ + TextUnit_Character = 0, + TextUnit_Format = 1, + TextUnit_Word = 2, + TextUnit_Line = 3, + TextUnit_Paragraph = 4, + TextUnit_Page = 5, + TextUnit_Document = 6 +}; + +enum TextPatternRangeEndpoint +{ + TextPatternRangeEndpoint_Start = 0, + TextPatternRangeEndpoint_End = 1 +}; + +/* UIA_PatternIds */ +const long UIA_InvokePatternId = 10000; +const long UIA_SelectionPatternId = 10001; +const long UIA_ValuePatternId = 10002; +const long UIA_RangeValuePatternId = 10003; +const long UIA_ScrollPatternId = 10004; +const long UIA_ExpandCollapsePatternId = 10005; +const long UIA_GridPatternId = 10006; +const long UIA_GridItemPatternId = 10007; +const long UIA_MultipleViewPatternId = 10008; +const long UIA_WindowPatternId = 10009; +const long UIA_SelectionItemPatternId = 10010; +const long UIA_DockPatternId = 10011; +const long UIA_TablePatternId = 10012; +const long UIA_TableItemPatternId = 10013; +const long UIA_TextPatternId = 10014; +const long UIA_TogglePatternId = 10015; +const long UIA_TransformPatternId = 10016; +const long UIA_ScrollItemPatternId = 10017; +const long UIA_LegacyIAccessiblePatternId = 10018; +const long UIA_ItemContainerPatternId = 10019; +const long UIA_VirtualizedItemPatternId = 10020; +const long UIA_SynchronizedInputPatternId = 10021; + +#define INTERFACE ITextProvider +DECLARE_INTERFACE_(ITextProvider, IUnknown) +{ + STDMETHOD(GetSelection)(THIS_ SAFEARRAY **) PURE; + STDMETHOD(GetVisibleRanges)(THIS_ SAFEARRAY **) PURE; + STDMETHOD(RangeFromChild)(THIS_ IRawElementProviderSimple *, ITextRangeProvider **) PURE; + STDMETHOD(RangeFromPoint)(THIS_ struct UiaPoint, ITextRangeProvider **pRetVal) PURE; + STDMETHOD(get_DocumentRange)(THIS_ ITextRangeProvider **) PURE; + STDMETHOD(get_SupportedTextSelection)(THIS_ enum SupportedTextSelection *) PURE; +}; +#undef INTERFACE + +#define INTERFACE ITextRangeProvider +DECLARE_INTERFACE_(ITextRangeProvider, IUnknown) +{ + STDMETHOD(Clone)(THIS_ ITextRangeProvider **) PURE; + STDMETHOD(Compare)(THIS_ ITextRangeProvider *, BOOL *) PURE; + STDMETHOD(CompareEndpoints)(THIS_ enum TextPatternRangeEndpoint, ITextRangeProvider *, enum TextPatternRangeEndpoint, int *) PURE; + STDMETHOD(ExpandToEnclosingUnit)(THIS_ enum TextUnit) PURE; + STDMETHOD(FindAttribute)(THIS_ TEXTATTRIBUTEID, VARIANT, BOOL, ITextRangeProvider **) PURE; + STDMETHOD(FindText)(THIS_ BSTR, BOOL, BOOL, ITextRangeProvider **) PURE; + STDMETHOD(GetAttributeValue)(THIS_ TEXTATTRIBUTEID, VARIANT *) PURE; + STDMETHOD(GetBoundingRectangles)(THIS_ SAFEARRAY **) PURE; + STDMETHOD(GetEnclosingElement)(THIS_ IRawElementProviderSimple **) PURE; + STDMETHOD(GetText)(THIS_ int, BSTR *) PURE; + STDMETHOD(Move)(THIS_ enum TextUnit, int, int *) PURE; + STDMETHOD(MoveEndpointByUnit)(THIS_ enum TextPatternRangeEndpoint, enum TextUnit, int *) PURE; + STDMETHOD(MoveEndpointByRange)(THIS_ enum TextPatternRangeEndpoint, ITextRangeProvider *, enum TextPatternRangeEndpoint) PURE; + STDMETHOD(Select)(THIS) PURE; + STDMETHOD(AddToSelection)(THIS) PURE; + STDMETHOD(RemoveFromSelection)(THIS) PURE; + STDMETHOD(ScrollIntoView)(THIS_ BOOL) PURE; + STDMETHOD(GetChildren)(THIS_ SAFEARRAY **) PURE; +}; +#undef INTERFACE + +#define INTERFACE IRawElementProviderSimple +DECLARE_INTERFACE_(IRawElementProviderSimple, IUnknown) +{ + STDMETHOD(get_ProviderOptions)(THIS_ enum ProviderOptions *) PURE; + STDMETHOD(GetPatternProvider)(THIS_ PATTERNID, IUnknown **) PURE; + STDMETHOD(GetPropertyValue)(THIS_ PROPERTYID, VARIANT *) PURE; + STDMETHOD(get_HostRawElementProvider)(THIS_ IRawElementProviderSimple **) PURE; +}; +#undef INTERFACE + + +#define INTERFACE IAccessibleEx +DECLARE_INTERFACE_(IAccessibleEx, IUnknown) +{ + STDMETHOD(GetObjectForChild)(THIS_ long, IAccessibleEx **) PURE; + STDMETHOD(GetIAccessiblePair)(THIS_ IAccessible **, long *) PURE; + STDMETHOD(GetRuntimeId)(THIS_ SAFEARRAY **) PURE; + STDMETHOD(ConvertReturnedElement)(THIS_ IRawElementProviderSimple *, IAccessibleEx **) PURE; +}; +#undef INTERFACE + +HRESULT GetIAccessibleExFromIAccessible( IAccessible *pAcc, long idChild, IAccessibleEx **ppaex ); +HRESULT GetIRawElementProviderFromIAccessible( IAccessible * pAcc, long idChild, IRawElementProviderSimple **ppEl ); +HRESULT GetPatternFromIAccessible( IAccessible * pAcc, long idChild, PATTERNID patternId, REFIID iid, void **ppv ); + +#ifdef __cplusplus +} +#endif + +#endif // UIAUTO_HH diff --git a/mouseover_win32/Makefile b/mouseover_win32/Makefile index e5ddfc9f..467e5f13 100644 --- a/mouseover_win32/Makefile +++ b/mouseover_win32/Makefile @@ -1,4 +1,5 @@ -GCC:=i686-pc-mingw32-gcc.exe -W -Wall -s -O2 +GCC:=gcc.exe -W -Wall -s -O2 +GPP:=g++.exe -W -Wall -s -O2 .PHONY: all clean @@ -16,6 +17,15 @@ HookImportFunction.o: HookImportFunction.c GetWord.o: GetWord.c $(GCC) -c $< +IAccEx.o: IAccEx.cc + $(GPP) -c $< + +GetWordByIAccEx.o: GetWordByIAccEx.cc + $(GPP) -c $< + +guids.o: guids.c + $(GCC) -c $< + GdTextOutHook.dll libGdTextOutHook.a: TextOutHook.o HookImportFunction.o GetWord.o $(GCC) -shared -o GdTextOutHook.dll $^ -lgdi32 -Wl,--out-implib,libGdTextOutHook.a @@ -25,5 +35,5 @@ TextOutSpy.o: TextOutSpy.c ThTypes.o: ThTypes.c $(GCC) -c $< -GdTextOutSpy.dll libGdTextOutSpy.a: TextOutSpy.o ThTypes.o - $(GCC) -shared -o GdTextOutSpy.dll $^ -lgdi32 -Wl,--out-implib,libGdTextOutSpy.a +GdTextOutSpy.dll libGdTextOutSpy.a: TextOutSpy.o ThTypes.o IAccEx.o guids.o GetWordByIAccEx.o + $(GPP) -shared -o GdTextOutSpy.dll $^ -lgdi32 -luuid -loleacc -loleaut32 -Wl,--out-implib,libGdTextOutSpy.a diff --git a/mouseover_win32/TextOutHook.c b/mouseover_win32/TextOutHook.c index a8e92fb8..cdeceae6 100644 --- a/mouseover_win32/TextOutHook.c +++ b/mouseover_win32/TextOutHook.c @@ -525,9 +525,9 @@ DWORD wso; case DLL_PROCESS_ATTACH: if(hHookMutex==0) { hHookMutex = CreateMutex(NULL, FALSE, "GoldenDictTextOutHookMutex"); - if(hHookMutex==0) return(FALSE); + if(hHookMutex==0) + return(FALSE); } - //ThTypes_Init(); InstallTextOutHooks(); break; @@ -541,7 +541,6 @@ DWORD wso; hHookMutex=0; } } - //Thtypes_End(); break; case DLL_THREAD_ATTACH: diff --git a/mouseover_win32/TextOutSpy.c b/mouseover_win32/TextOutSpy.c index 416b3f63..caa365e7 100644 --- a/mouseover_win32/TextOutSpy.c +++ b/mouseover_win32/TextOutSpy.c @@ -1,13 +1,14 @@ #include "TextOutSpy.h" #include "ThTypes.h" #include "GDDataTranfer.h" +#include "GetWordByIAccEx.h" const int MOUSEOVER_INTERVAL = 300; const int REQUEST_MESSAGE_INTERVAL = 500; const int WM_MY_SHOW_TRANSLATION = WM_USER + 301; HINSTANCE g_hInstance = NULL; -HANDLE hSynhroMutex = 0; +HANDLE hSynhroMutex = 0, hHookMutex = 0; HINSTANCE hGetWordLib = 0; UINT_PTR TimerID = 0; typedef void (*GetWordProc_t)(TCurrentMode *); @@ -29,9 +30,29 @@ HWND WndParent,WndChild; static void SendWordToServer() { -DWORD SendMsgAnswer; - if(uGdAskMessage) { - LRESULT lr; +DWORD SendMsgAnswer, flags; +LRESULT lr; + if (hGetWordLib == 0) { + hGetWordLib = LoadLibrary(GlobalData->LibName); + if (hGetWordLib) { + GetWordProc = (GetWordProc_t)GetProcAddress(hGetWordLib, "__gdGetWord"); + } + else { + hGetWordLib = (HINSTANCE)-1; + } + } + + // Ask for needing to retrieve word - WPARAM = 1 + lr = SendMessageTimeout(GlobalData->ServerWND, WM_MY_SHOW_TRANSLATION, 1, 0, SMTO_ABORTIFHUNG, MOUSEOVER_INTERVAL, &SendMsgAnswer); + if( lr == 0 || SendMsgAnswer == 0) //No answer or no needing + return; + + GlobalData->CurMod.MatchedWord[0] = 0; + GlobalData->CurMod.WordLen = 0; + GlobalData->CurMod.BeginPos = 0; + flags = SendMsgAnswer; + + if( ( flags & GD_FLAG_METHOD_GD_MESSAGE ) != 0 && uGdAskMessage != 0 ) { int n; gds.dwSize = sizeof(gds); gds.cwData = Buffer; @@ -51,23 +72,28 @@ DWORD SendMsgAnswer; return; } } - if (hGetWordLib == 0) { - hGetWordLib = LoadLibrary(GlobalData->LibName); - if (hGetWordLib) { - GetWordProc = (GetWordProc_t)GetProcAddress(hGetWordLib, "__gdGetWord"); - } - else { - hGetWordLib = (HINSTANCE)-1; - } - } - if (GetWordProc) { + + if( ( flags & GD_FLAG_METHOD_STANDARD ) != 0 && GetWordProc != 0 ) { GlobalData->CurMod.WND = GlobalData->LastWND; GlobalData->CurMod.Pt = GlobalData->LastPt; GetWordProc(&(GlobalData->CurMod)); if (GlobalData->CurMod.WordLen > 0) { SendMessageTimeout(GlobalData->ServerWND, WM_MY_SHOW_TRANSLATION, 0, 0, SMTO_ABORTIFHUNG, MOUSEOVER_INTERVAL, &SendMsgAnswer); + return; } } + + if( ( flags & GD_FLAG_METHOD_IACCESSIBLEEX ) != 0 ) { + getWordByAccEx( GlobalData->LastPt ); + if (GlobalData->CurMod.WordLen > 0) { + SendMessageTimeout(GlobalData->ServerWND, WM_MY_SHOW_TRANSLATION, 0, 0, SMTO_ABORTIFHUNG, MOUSEOVER_INTERVAL, &SendMsgAnswer); + return; + } + } + + if( ( flags & GD_FLAG_METHOD_UI_AUTOMATION ) != 0 ) { + PostMessage( GlobalData->ServerWND, WM_MY_SHOW_TRANSLATION, 0, 0 ); + } } void CALLBACK TimerFunc(HWND hWnd,UINT nMsg,UINT nTimerid,DWORD dwTime) @@ -86,6 +112,7 @@ DWORD wso; LRESULT CALLBACK MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam) { DWORD wso; + if ((nCode == HC_ACTION) && ((wParam == WM_MOUSEMOVE) || (wParam == WM_NCMOUSEMOVE)) && (GlobalData != NULL)) { wso = WaitForSingleObject(hSynhroMutex, 0); if (wso == WAIT_OBJECT_0 || wso == WAIT_ABANDONED) { @@ -163,12 +190,18 @@ BOOL APIENTRY DllMain (HINSTANCE hInst /* Library instance handle. */ , { case DLL_PROCESS_ATTACH: g_hInstance = hInst; + if(hHookMutex==0) { + hHookMutex = CreateMutex(NULL, FALSE, "GoldenDictTextOutHookMutex"); + } if(hSynhroMutex==0) { hSynhroMutex = CreateMutex(NULL, FALSE, "GoldenDictTextOutSpyMutex"); - if(hSynhroMutex==0) return(FALSE); + if(hSynhroMutex==0) { + return(FALSE); + } } ThTypes_Init(); uGdAskMessage = RegisterWindowMessage(GD_MESSAGE_NAME); + FindGetPhysicalCursorPos(); break; case DLL_PROCESS_DETACH: @@ -190,6 +223,14 @@ BOOL APIENTRY DllMain (HINSTANCE hInst /* Library instance handle. */ , if ((hGetWordLib != 0)&&(hGetWordLib != (HINSTANCE)(-1))) { FreeLibrary(hGetWordLib); } + if(hHookMutex) { + DWORD wso = WaitForSingleObject(hHookMutex, 5000); + if (wso == WAIT_OBJECT_0 || wso == WAIT_ABANDONED) { + ReleaseMutex(hHookMutex); + CloseHandle(hHookMutex); + hHookMutex=0; + } + } Thtypes_End(); break; diff --git a/mouseover_win32/ThTypes.c b/mouseover_win32/ThTypes.c index 577fc780..9ba40230 100644 --- a/mouseover_win32/ThTypes.c +++ b/mouseover_win32/ThTypes.c @@ -5,8 +5,12 @@ TGlobalDLLData *GlobalData = NULL; void ThTypes_Init() { - if (!MMFHandle) + if (!MMFHandle) { MMFHandle = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(TGlobalDLLData), "GoldenDictTextOutHookSharedMem"); + } + if (!MMFHandle) { + MMFHandle = OpenFileMappingA(FILE_MAP_READ | FILE_MAP_WRITE, 0, "GoldenDictTextOutHookSharedMem"); + } if (!GlobalData && MMFHandle != NULL) GlobalData = MapViewOfFile(MMFHandle, FILE_MAP_ALL_ACCESS, 0, 0, 0); } diff --git a/mouseover_win32/ThTypes.h b/mouseover_win32/ThTypes.h index 5f9a3f84..efdaa57f 100644 --- a/mouseover_win32/ThTypes.h +++ b/mouseover_win32/ThTypes.h @@ -3,6 +3,11 @@ #include +#define GD_FLAG_METHOD_STANDARD 0x00000001 +#define GD_FLAG_METHOD_GD_MESSAGE 0x00000002 +#define GD_FLAG_METHOD_IACCESSIBLEEX 0x00000004 +#define GD_FLAG_METHOD_UI_AUTOMATION 0x00000008 + #ifdef __cplusplus extern "C" { diff --git a/mouseover_win32/guids.c b/mouseover_win32/guids.c new file mode 100644 index 00000000..0f613c2e --- /dev/null +++ b/mouseover_win32/guids.c @@ -0,0 +1,8 @@ +#define INITGUID +#include + +DEFINE_GUID(IID_IAccessibleEx, 0xf8b80ada, 0x2c44, 0x48d0, 0x89, 0xbe, 0x5f, 0xf2, 0x3c, 0x9c, 0xd8, 0x75); +DEFINE_GUID(IID_IRawElementProviderSimple, 0xd6dd68d1, 0x86fd, 0x4332, 0x86, 0x66, 0x9a, 0xbe, 0xde, 0xa2, 0xd2, 0x4c); +DEFINE_GUID(IID_ITextProvider, 0x3589c92c, 0x63f3, 0x4367, 0x99, 0xbb, 0xad, 0xa6, 0x53, 0xb7, 0x7c, 0xf2); +DEFINE_GUID(IID_ITextRangeProvider, 0x5347ad7b, 0xc355, 0x46f8, 0xaf, 0xf5, 0x90, 0x90, 0x33, 0x58, 0x2f, 0x63); + diff --git a/preferences.cc b/preferences.cc index 6c8e8f25..07f6901f 100644 --- a/preferences.cc +++ b/preferences.cc @@ -118,6 +118,9 @@ Preferences::Preferences( QWidget * parent, Config::Preferences const & p ): ui.scanPopupAltMode->setChecked( p.scanPopupAltMode ); ui.scanPopupAltModeSecs->setValue( p.scanPopupAltModeSecs ); + ui.scanPopupUseUIAutomation->setChecked( p.scanPopupUseUIAutomation ); + ui.scanPopupUseIAccessibleEx->setChecked( p.scanPopupUseIAccessibleEx ); + ui.scanPopupUseGDMessage->setChecked( p.scanPopupUseGDMessage ); // Different platforms have different keys available @@ -130,6 +133,11 @@ Preferences::Preferences( QWidget * parent, Config::Preferences const & p ): ui.rightCtrl->hide(); ui.leftShift->hide(); ui.rightShift->hide(); + + // This settings is Windows-specific + ui.scanPopupUseUIAutomation->hide(); + ui.scanPopupUseIAccessibleEx->hide(); + ui.scanPopupUseGDMessage->hide(); #endif // Sound @@ -224,6 +232,9 @@ Config::Preferences Preferences::getPreferences() p.scanPopupAltMode = ui.scanPopupAltMode->isChecked(); p.scanPopupAltModeSecs = ui.scanPopupAltModeSecs->value(); + p.scanPopupUseUIAutomation = ui.scanPopupUseUIAutomation->isChecked(); + p.scanPopupUseIAccessibleEx = ui.scanPopupUseIAccessibleEx->isChecked(); + p.scanPopupUseGDMessage = ui.scanPopupUseGDMessage->isChecked(); p.pronounceOnLoadMain = ui.pronounceOnLoadMain->isChecked(); p.pronounceOnLoadPopup = ui.pronounceOnLoadPopup->isChecked(); diff --git a/preferences.ui b/preferences.ui index dbb0a7c9..7d0e0cd0 100644 --- a/preferences.ui +++ b/preferences.ui @@ -7,7 +7,7 @@ 0 0 572 - 347 + 357 @@ -276,6 +276,19 @@ With this on however, it will hide the main window. + + Qt::Vertical + + + + 20 + 20 + + + + + + Qt::Vertical @@ -545,9 +558,88 @@ seconds, which is specified here. + + + + + + Try to use IAccessibleEx interface to retrieve word under cursor. +This technology works only with some programs that support it. +Don't select this option if you don't use such programs. + + + Use IAccessibleE&x + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Use special GoldenDict message to ask other programs about a word under cursor. +This technology works only with some programs that support it. +Don't select this option if you don't use such programs. + + + Use GD message + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Try to use UI Automation technology to retrieve word under cursor. +This technology works only with some programs that support it. +Don't select this option if you don't use such programs. + + + Use UI Automation + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + @@ -1069,7 +1161,6 @@ download page. proxyPort proxyUser proxyPassword - enableScanPopup diff --git a/scanpopup.cc b/scanpopup.cc index e7f6fecc..d9b022d1 100644 --- a/scanpopup.cc +++ b/scanpopup.cc @@ -153,8 +153,18 @@ ScanPopup::ScanPopup( QWidget * parent, connect( &mouseGrabPollTimer, SIGNAL( timeout() ), this, SLOT(mouseGrabPoll()) ); +#ifdef Q_OS_WIN32 + connect( &MouseOver::instance(), SIGNAL( askNeedWord( bool * ) ), this, SLOT( needHandleWord( bool * ) ), Qt::DirectConnection ); + connect( &MouseOver::instance(), SIGNAL( askUseUIAutomation( bool * ) ), + this, SLOT( useUIAutomation( bool * ) ), Qt::DirectConnection ); + connect( &MouseOver::instance(), SIGNAL( askUseIAccessibleEx( bool * ) ), + this, SLOT( useIAccessibleEx( bool * ) ), Qt::DirectConnection ); + connect( &MouseOver::instance(), SIGNAL( askUseGDMessage( bool * ) ), + this, SLOT( useGDMessage( bool * ) ), Qt::DirectConnection ); + +#endif } ScanPopup::~ScanPopup() @@ -768,7 +778,26 @@ void ScanPopup::mutedDictionariesChanged() definition->updateMutedContents(); } +#ifdef Q_OS_WIN32 + void ScanPopup::needHandleWord( bool *pNeed ) { *pNeed = !cfg.preferences.enableScanPopupModifiers || checkModifiersPressed( cfg.preferences.scanPopupModifiers ); } + +void ScanPopup::useUIAutomation( bool *pUse ) +{ + *pUse = cfg.preferences.scanPopupUseUIAutomation != 0; +} + +void ScanPopup::useIAccessibleEx( bool *pUse ) +{ + *pUse = cfg.preferences.scanPopupUseIAccessibleEx != 0; +} + +void ScanPopup::useGDMessage( bool *pUse ) +{ + *pUse = cfg.preferences.scanPopupUseGDMessage != 0; +} + +#endif // Q_OS_WIN32 diff --git a/scanpopup.hh b/scanpopup.hh index eca737e5..79ece28f 100644 --- a/scanpopup.hh +++ b/scanpopup.hh @@ -134,7 +134,12 @@ private slots: void altModeExpired(); void altModePoll(); +#ifdef Q_OS_WIN32 void needHandleWord(bool *pNeed); + void useUIAutomation(bool *pUse); + void useIAccessibleEx(bool *pUse); + void useGDMessage(bool *pUse); +#endif /// Called repeatedly once the popup is initially engaged and we monitor the /// mouse as it may move away from the window. This simulates mouse grab, in diff --git a/uiauto.hh b/uiauto.hh new file mode 100644 index 00000000..5d7205bb --- /dev/null +++ b/uiauto.hh @@ -0,0 +1,330 @@ +#ifndef __UIAUTO_HH_INCLUDED__ +#define __UIAUTO_HH_INCLUDED__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +EXTERN_C const IID IID_IUIAutomation; +EXTERN_C const IID CLSID_CUIAutomation; +EXTERN_C const IID IID_IUIAutomationElement; +EXTERN_C const IID IID_IUIAutomationTextPattern; +EXTERN_C const IID IID_IUIAutomationTextRange; +EXTERN_C const IID IID_IUIAutomationTreeWalker; + +typedef interface IUIAutomationElement IUIAutomationElement; +typedef interface IUIAutomationElementArray IUIAutomationElementArray; +typedef interface IUIAutomationTextPattern IUIAutomationTextPattern; +typedef interface IUIAutomationTextRange IUIAutomationTextRange; +typedef interface IUIAutomationTextRangeArray IUIAutomationTextRangeArray; +typedef interface IUIAutomationCacheRequest IUIAutomationCacheRequest; +typedef interface IUIAutomationTreeWalker IUIAutomationTreeWalker; +typedef interface IUIAutomationCondition IUIAutomationCondition; +typedef interface IUIAutomationEventHandler IUIAutomationEventHandler; +typedef interface IUIAutomationPropertyChangedEventHandler IUIAutomationPropertyChangedEventHandler; +typedef interface IUIAutomationStructureChangedEventHandler IUIAutomationStructureChangedEventHandler; +typedef interface IUIAutomationFocusChangedEventHandler IUIAutomationFocusChangedEventHandler; +typedef interface IUIAutomationProxyFactory IUIAutomationProxyFactory; +typedef interface IUIAutomationProxyFactoryEntry IUIAutomationProxyFactoryEntry; +typedef interface IUIAutomationProxyFactoryMapping IUIAutomationProxyFactoryMapping; + +typedef void *UIA_HWND; +typedef int PROPERTYID; +typedef int EVENTID; +typedef int PATTERNID; +typedef int CONTROLTYPEID; +typedef int TEXTATTRIBUTEID; + +enum TreeScope +{ + TreeScope_Element = 0x1, + TreeScope_Children = 0x2, + TreeScope_Descendants = 0x4, + TreeScope_Parent = 0x8, + TreeScope_Ancestors = 0x10, + TreeScope_Subtree = ( ( TreeScope_Element | TreeScope_Children ) | TreeScope_Descendants ) +}; + +enum PropertyConditionFlags +{ + PropertyConditionFlags_None = 0, + PropertyConditionFlags_IgnoreCase = 0x1 +}; + +enum OrientationType +{ + OrientationType_None = 0, + OrientationType_Horizontal = 1, + OrientationType_Vertical = 2 +}; + +enum SupportedTextSelection +{ + SupportedTextSelection_None = 0, + SupportedTextSelection_Single = 1, + SupportedTextSelection_Multiple = 2 +}; + +enum TextPatternRangeEndpoint +{ + TextPatternRangeEndpoint_Start = 0, + TextPatternRangeEndpoint_End = 1 +}; + +enum TextUnit +{ + TextUnit_Character = 0, + TextUnit_Format = 1, + TextUnit_Word = 2, + TextUnit_Line = 3, + TextUnit_Paragraph = 4, + TextUnit_Page = 5, + TextUnit_Document = 6 +}; + +enum ProviderOptions +{ + ProviderOptions_ClientSideProvider = 0x1, + ProviderOptions_ServerSideProvider = 0x2, + ProviderOptions_NonClientAreaProvider = 0x4, + ProviderOptions_OverrideProvider = 0x8, + ProviderOptions_ProviderOwnsSetFocus = 0x10, + ProviderOptions_UseComThreading = 0x20 +} ; + +/* UIA_PatternIds */ +const long UIA_InvokePatternId = 10000; +const long UIA_SelectionPatternId = 10001; +const long UIA_ValuePatternId = 10002; +const long UIA_RangeValuePatternId = 10003; +const long UIA_ScrollPatternId = 10004; +const long UIA_ExpandCollapsePatternId = 10005; +const long UIA_GridPatternId = 10006; +const long UIA_GridItemPatternId = 10007; +const long UIA_MultipleViewPatternId = 10008; +const long UIA_WindowPatternId = 10009; +const long UIA_SelectionItemPatternId = 10010; +const long UIA_DockPatternId = 10011; +const long UIA_TablePatternId = 10012; +const long UIA_TableItemPatternId = 10013; +const long UIA_TextPatternId = 10014; +const long UIA_TogglePatternId = 10015; +const long UIA_TransformPatternId = 10016; +const long UIA_ScrollItemPatternId = 10017; +const long UIA_LegacyIAccessiblePatternId = 10018; +const long UIA_ItemContainerPatternId = 10019; +const long UIA_VirtualizedItemPatternId = 10020; +const long UIA_SynchronizedInputPatternId = 10021; + +#define INTERFACE IUIAutomation +DECLARE_INTERFACE_(IUIAutomation, IUnknown) +{ + STDMETHOD(CompareElements)(THIS_ IUIAutomationElement *, IUIAutomationElement *, BOOL *) PURE; + STDMETHOD(CompareRuntimeIds)(THIS_ SAFEARRAY *, SAFEARRAY *, BOOL *) PURE; + STDMETHOD(GetRootElement)(THIS_ IUIAutomationElement **) PURE; + STDMETHOD(ElementFromHandle)(THIS_ UIA_HWND, IUIAutomationElement **) PURE; + STDMETHOD(ElementFromPoint)(THIS_ POINT, IUIAutomationElement **) PURE; + STDMETHOD(GetFocusedElement)(THIS_ IUIAutomationElement **) PURE; + STDMETHOD(GetRootElementBuildCache)(THIS_ IUIAutomationCacheRequest *, IUIAutomationElement **) PURE; + STDMETHOD(ElementFromHandleBuildCache)(THIS_ UIA_HWND, IUIAutomationCacheRequest *, IUIAutomationElement **) PURE; + STDMETHOD(ElementFromPointBuildCache)(THIS_ POINT, IUIAutomationCacheRequest *, IUIAutomationElement **) PURE; + STDMETHOD(GetFocusedElementBuildCache)(THIS_ IUIAutomationCacheRequest *, IUIAutomationElement **) PURE; + STDMETHOD(CreateTreeWalker)(THIS_ IUIAutomationCondition *, IUIAutomationTreeWalker **) PURE; + STDMETHOD(get_ControlViewWalker)(THIS_ IUIAutomationTreeWalker **) PURE; + STDMETHOD(get_ContentViewWalker)(THIS_ IUIAutomationTreeWalker **) PURE; + STDMETHOD(get_RawViewWalker)(THIS_ IUIAutomationTreeWalker **) PURE; + STDMETHOD(get_RawViewCondition)(THIS_ IUIAutomationCondition **) PURE; + STDMETHOD(get_ControlViewCondition)(THIS_ IUIAutomationCondition **) PURE; + STDMETHOD(get_ContentViewCondition)(THIS_ IUIAutomationCondition **) PURE; + STDMETHOD(CreateCacheRequest)(THIS_ IUIAutomationCacheRequest **) PURE; + STDMETHOD(CreateTrueCondition)(THIS_ IUIAutomationCondition **) PURE; + STDMETHOD(CreateFalseCondition)(THIS_ IUIAutomationCondition **) PURE; + STDMETHOD(CreatePropertyCondition)(THIS_ PROPERTYID, VARIANT, IUIAutomationCondition **) PURE; + STDMETHOD(CreatePropertyConditionEx)(THIS_ PROPERTYID, VARIANT, enum PropertyConditionFlags, IUIAutomationCondition **) PURE; + STDMETHOD(CreateAndCondition)(THIS_ IUIAutomationCondition *, IUIAutomationCondition *, IUIAutomationCondition **) PURE; + STDMETHOD(CreateAndConditionFromArray)(THIS_ SAFEARRAY *, IUIAutomationCondition **) PURE; + STDMETHOD(CreateAndConditionFromNativeArray)(THIS_ IUIAutomationCondition **, int , IUIAutomationCondition **) PURE; + STDMETHOD(CreateOrCondition)(THIS_ IUIAutomationCondition *, IUIAutomationCondition *, IUIAutomationCondition **) PURE; + STDMETHOD(CreateOrConditionFromArray)(THIS_ SAFEARRAY *, IUIAutomationCondition **) PURE; + STDMETHOD(CreateOrConditionFromNativeArray)(THIS_ IUIAutomationCondition **, int , IUIAutomationCondition **) PURE; + STDMETHOD(CreateNotCondition)(THIS_ IUIAutomationCondition *, IUIAutomationCondition **) PURE; + STDMETHOD(AddAutomationEventHandler)(THIS_ EVENTID, IUIAutomationElement *, enum TreeScope, IUIAutomationCacheRequest *, IUIAutomationEventHandler *) PURE; + STDMETHOD(RemoveAutomationEventHandler)(THIS_ EVENTID, IUIAutomationElement *, IUIAutomationEventHandler *) PURE; + STDMETHOD(AddPropertyChangedEventHandlerNativeArray)(THIS_ IUIAutomationElement *, enum TreeScope, IUIAutomationCacheRequest *, + IUIAutomationPropertyChangedEventHandler *, PROPERTYID *, int) PURE; + STDMETHOD(AddPropertyChangedEventHandler)(THIS_ IUIAutomationElement *, enum TreeScope, EVENTID, IUIAutomationCacheRequest *, + IUIAutomationPropertyChangedEventHandler *, SAFEARRAY *) PURE; + STDMETHOD(RemovePropertyChangedEventHandler)(THIS_ IUIAutomationElement *, IUIAutomationPropertyChangedEventHandler *) PURE; + STDMETHOD(AddStructureChangedEventHandler)(THIS_ IUIAutomationElement *, enum TreeScope, IUIAutomationCacheRequest *, IUIAutomationStructureChangedEventHandler *) PURE; + STDMETHOD(RemoveStructureChangedEventHandler)(THIS_ IUIAutomationElement *, IUIAutomationStructureChangedEventHandler *) PURE; + STDMETHOD(AddFocusChangedEventHandler)(THIS_ IUIAutomationCacheRequest *, IUIAutomationFocusChangedEventHandler *) PURE; + STDMETHOD(RemoveFocusChangedEventHandler)(THIS_ IUIAutomationFocusChangedEventHandler *) PURE; + STDMETHOD(RemoveAllEventHandlers)(THIS) PURE; + STDMETHOD(IntNativeArrayToSafeArray)(THIS_ int *, int, SAFEARRAY **) PURE; + STDMETHOD(IntSafeArrayToNativeArray)(THIS_ SAFEARRAY *, int **, int *) PURE; + STDMETHOD(RectToVariant)(THIS_ RECT, VARIANT *) PURE; + STDMETHOD(VariantToRect)(THIS_ VARIANT, RECT *) PURE; + STDMETHOD(SafeArrayToRectNativeArray)(THIS_ SAFEARRAY *, RECT **, int *) PURE; + STDMETHOD(CreateProxyFactoryEntry)(THIS_ IUIAutomationProxyFactory *, IUIAutomationProxyFactoryEntry **) PURE; + STDMETHOD(get_ProxyFactoryMapping)(THIS_ IUIAutomationProxyFactoryMapping **) PURE; + STDMETHOD(GetPropertyProgrammaticName)(THIS_ PROPERTYID, BSTR *) PURE; + STDMETHOD(GetPatternProgrammaticName)(THIS_ PATTERNID, BSTR *) PURE; + STDMETHOD(PollForPotentialSupportedPatterns)(THIS_ IUIAutomationElement *, SAFEARRAY **, SAFEARRAY **) PURE; + STDMETHOD(PollForPotentialSupportedProperties)(THIS_ IUIAutomationElement *, SAFEARRAY **, SAFEARRAY **) PURE; + STDMETHOD(CheckNotSupported)(THIS_ VARIANT, BOOL *) PURE; + STDMETHOD(get_ReservedNotSupportedValue)(THIS_ IUnknown **) PURE; + STDMETHOD(get_ReservedMixedAttributeValue)(THIS_ IUnknown **) PURE; + STDMETHOD(ElementFromIAccessible)(THIS_ IAccessible *, int, IUIAutomationElement **) PURE; + STDMETHOD(ElementFromIAccessibleBuildCache)(THIS_ IAccessible *, int, IUIAutomationCacheRequest *, IUIAutomationElement **) PURE; +}; +#undef INTERFACE + +#define INTERFACE IUIAutomationElement +DECLARE_INTERFACE_(IUIAutomationElement, IUnknown) +{ + STDMETHOD(SetFocus)(THIS) PURE; + STDMETHOD(GetRuntimeId)(THIS_ SAFEARRAY **) PURE; + STDMETHOD(FindFirst)(THIS_ enum TreeScope, IUIAutomationCondition *, IUIAutomationElement **) PURE; + STDMETHOD(FindAll)(THIS_ enum TreeScope, IUIAutomationCondition *, IUIAutomationElementArray **) PURE; + STDMETHOD(FindFirstBuildCache)(THIS_ enum TreeScope, IUIAutomationCondition *, IUIAutomationCacheRequest *, IUIAutomationElement **) PURE; + STDMETHOD(FindAllBuildCache)(THIS_ enum TreeScope, IUIAutomationCondition *, IUIAutomationCacheRequest *, IUIAutomationElementArray **) PURE; + STDMETHOD(BuildUpdatedCache)(THIS_ IUIAutomationCacheRequest *, IUIAutomationElement **) PURE; + STDMETHOD(GetCurrentPropertyValue)(THIS_ PROPERTYID, VARIANT *) PURE; + STDMETHOD(GetCurrentPropertyValueEx)(THIS_ PROPERTYID, BOOL, VARIANT *) PURE; + STDMETHOD(GetCachedPropertyValue)(THIS_ PROPERTYID, VARIANT *) PURE; + STDMETHOD(GetCachedPropertyValueEx)(THIS_ PROPERTYID, BOOL, VARIANT *) PURE; + STDMETHOD(GetCurrentPatternAs)(THIS_ PATTERNID, REFIID, void **) PURE; + STDMETHOD(GetCachedPatternAs)(THIS_ PATTERNID, REFIID, void **) PURE; + STDMETHOD(GetCurrentPattern)(THIS_ PATTERNID, IUnknown **) PURE; + STDMETHOD(GetCachedPattern)(THIS_ PATTERNID, IUnknown **) PURE; + STDMETHOD(GetCachedParent)(THIS_ IUIAutomationElement **) PURE; + STDMETHOD(GetCachedChildren)(THIS_ IUIAutomationElement **) PURE; + STDMETHOD(get_CurrentProcessId)(THIS_ int *) PURE; + STDMETHOD(get_CurrentControlType)(THIS_ CONTROLTYPEID *) PURE; + STDMETHOD(get_CurrentLocalizedControlType)(THIS_ BSTR *) PURE; + STDMETHOD(get_CurrentName)(THIS_ BSTR *) PURE; + STDMETHOD(get_CurrentAcceleratorKey)(THIS_ BSTR *) PURE; + STDMETHOD(get_CurrentAccessKey)(THIS_ BSTR *) PURE; + STDMETHOD(get_CurrentHasKeyboardFocus)(THIS_ BOOL *) PURE; + STDMETHOD(get_CurrentIsKeyboardFocusable)(THIS_ BOOL *) PURE; + STDMETHOD(get_CurrentIsEnabled)(THIS_ BOOL *) PURE; + STDMETHOD(get_CurrentAutomationId)(THIS_ BSTR *) PURE; + STDMETHOD(get_CurrentClassName)(THIS_ BSTR *) PURE; + STDMETHOD(get_CurrentHelpText)(THIS_ BSTR *) PURE; + STDMETHOD(get_CurrentCulture)(THIS_ int *) PURE; + STDMETHOD(get_CurrentIsControlElement)(THIS_ BOOL *) PURE; + STDMETHOD(get_CurrentIsContentElement)(THIS_ BOOL *) PURE; + STDMETHOD(get_CurrentIsPassword)(THIS_ BOOL *) PURE; + STDMETHOD(get_CurrentNativeWindowHandle)(THIS_ UIA_HWND *) PURE; + STDMETHOD(get_CurrentItemType)(THIS_ BSTR *) PURE; + STDMETHOD(get_CurrentIsOffscreen)(THIS_ BOOL *) PURE; + STDMETHOD(get_CurrentOrientation)(THIS_ enum OrientationType *) PURE; + STDMETHOD(get_CurrentFrameworkId)(THIS_ BSTR *) PURE; + STDMETHOD(get_CurrentIsRequiredForForm)(THIS_ BOOL *) PURE; + STDMETHOD(get_CurrentItemStatus)(THIS_ BSTR *) PURE; + STDMETHOD(get_CurrentBoundingRectangle)(THIS_ RECT *) PURE; + STDMETHOD(get_CurrentLabeledBy)(THIS_ IUIAutomationElement **) PURE; + STDMETHOD(get_CurrentAriaRole)(THIS_ BSTR *) PURE; + STDMETHOD(get_CurrentAriaProperties)(THIS_ BSTR *) PURE; + STDMETHOD(get_CurrentIsDataValidForForm)(THIS_ BOOL *) PURE; + STDMETHOD(get_CurrentControllerFor)(THIS_ IUIAutomationElementArray **) PURE; + STDMETHOD(get_CurrentDescribedBy)(THIS_ IUIAutomationElementArray **) PURE; + STDMETHOD(get_CurrentFlowsTo)(THIS_ IUIAutomationElementArray **) PURE; + STDMETHOD(get_CurrentProviderDescription)(THIS_ BSTR *) PURE; + STDMETHOD(get_CachedProcessId)(THIS_ int *) PURE; + STDMETHOD(get_CachedControlType)(THIS_ CONTROLTYPEID *) PURE; + STDMETHOD(get_CachedLocalizedControlType)(THIS_ BSTR *) PURE; + STDMETHOD(get_CachedName)(THIS_ BSTR *) PURE; + STDMETHOD(get_CachedAcceleratorKey)(THIS_ BSTR *) PURE; + STDMETHOD(get_CachedAccessKey)(THIS_ BSTR *) PURE; + STDMETHOD(get_CachedHasKeyboardFocus)(THIS_ BOOL *) PURE; + STDMETHOD(get_CachedIsKeyboardFocusable)(THIS_ BOOL *) PURE; + STDMETHOD(get_CachedIsEnabled)(THIS_ BOOL *) PURE; + STDMETHOD(get_CachedAutomationId)(THIS_ BSTR *) PURE; + STDMETHOD(get_CachedClassName)(THIS_ BSTR *) PURE; + STDMETHOD(get_CachedHelpText)(THIS_ BSTR *) PURE; + STDMETHOD(get_CachedCulture)(THIS_ int *) PURE; + STDMETHOD(get_CachedIsControlElement)(THIS_ BOOL *) PURE; + STDMETHOD(get_CachedIsContentElement)(THIS_ BOOL *) PURE; + STDMETHOD(get_CachedIsPassword)(THIS_ BOOL *) PURE; + STDMETHOD(get_CachedNativeWindowHandle)(THIS_ UIA_HWND *) PURE; + STDMETHOD(get_CachedItemType)(THIS_ BSTR *) PURE; + STDMETHOD(get_CachedIsOffscreen)(THIS_ BOOL *) PURE; + STDMETHOD(get_CachedOrientation)(THIS_ enum OrientationType *) PURE; + STDMETHOD(get_CachedFrameworkId)(THIS_ BSTR *) PURE; + STDMETHOD(get_CachedIsRequiredForForm)(THIS_ BOOL *) PURE; + STDMETHOD(get_CachedItemStatus)(THIS_ BSTR *) PURE; + STDMETHOD(get_CachedBoundingRectangle)(THIS_ RECT *) PURE; + STDMETHOD(get_CachedLabeledBy)(THIS_ IUIAutomationElement **) PURE; + STDMETHOD(get_CachedAriaRole)(THIS_ BSTR *) PURE; + STDMETHOD(get_CachedAriaProperties)(THIS_ BSTR *) PURE; + STDMETHOD(get_CachedIsDataValidForForm)(THIS_ BOOL *) PURE; + STDMETHOD(get_CachedControllerFor)(THIS_ IUIAutomationElementArray **) PURE; + STDMETHOD(get_CachedDescribedBy)(THIS_ IUIAutomationElementArray **) PURE; + STDMETHOD(get_CachedFlowsTo)(THIS_ IUIAutomationElementArray **) PURE; + STDMETHOD(get_CachedProviderDescription)(THIS_ BSTR *) PURE; +}; +#undef INTERFACE + +#define INTERFACE IUIAutomationTextPattern +DECLARE_INTERFACE_(IUIAutomationTextPattern, IUnknown) +{ + STDMETHOD(RangeFromPoint)(THIS_ POINT, IUIAutomationTextRange **) PURE; + STDMETHOD(RangeFromChild)(THIS_ IUIAutomationElement *, IUIAutomationTextRange **) PURE; + STDMETHOD(GetSelection)(THIS_ IUIAutomationTextRangeArray **) PURE; + STDMETHOD(GetVisibleRanges)(THIS_ IUIAutomationTextRangeArray **) PURE; + STDMETHOD(get_DocumentRange)(THIS_ IUIAutomationTextRange **) PURE; + STDMETHOD(get_SupportedTextSelection)(THIS_ enum SupportedTextSelection *) PURE; +}; +#undef INTERFACE + +#define INTERFACE IUIAutomationTreeWalker +DECLARE_INTERFACE_(IUIAutomationTreeWalker, IUnknown) +{ + STDMETHOD(GetParentElement)(THIS_ IUIAutomationElement *, IUIAutomationElement **) PURE; + STDMETHOD(GetFirstChildElement)(THIS_ IUIAutomationElement *, IUIAutomationElement **) PURE; + STDMETHOD(GetLastChildElement)(THIS_ IUIAutomationElement *, IUIAutomationElement **) PURE; + STDMETHOD(GetNextSiblingElement)(THIS_ IUIAutomationElement *, IUIAutomationElement **) PURE; + STDMETHOD(GetPreviousSiblingElement)(THIS_ IUIAutomationElement *, IUIAutomationElement **) PURE; + STDMETHOD(NormalizeElement)(THIS_ IUIAutomationElement *, IUIAutomationElement **) PURE; + STDMETHOD(GetParentElementBuildCache)(THIS_ IUIAutomationElement *, IUIAutomationCacheRequest *, IUIAutomationElement **) PURE; + STDMETHOD(GetFirstChildElementBuildCache)(THIS_ IUIAutomationElement *, IUIAutomationCacheRequest *, IUIAutomationElement **) PURE; + STDMETHOD(GetLastChildElementBuildCache)(THIS_ IUIAutomationElement *, IUIAutomationCacheRequest *, IUIAutomationElement **) PURE; + STDMETHOD(GetNextSiblingElementBuildCache)(THIS_ IUIAutomationElement *, IUIAutomationCacheRequest *, IUIAutomationElement **) PURE; + STDMETHOD(GetPreviousSiblingElementBuildCache)(THIS_ IUIAutomationElement *, IUIAutomationCacheRequest *, IUIAutomationElement **) PURE; + STDMETHOD(NormalizeElementBuildCache)(THIS_ IUIAutomationElement *, IUIAutomationCacheRequest *, IUIAutomationElement **) PURE; + STDMETHOD(get_Condition)(THIS_ IUIAutomationCondition **) PURE; +}; +#undef INTERFACE + +#define INTERFACE IUIAutomationTextRange +DECLARE_INTERFACE_(IUIAutomationTextRange, IUnknown) +{ + STDMETHOD(Clone)(THIS_ IUIAutomationTextRange **) PURE; + STDMETHOD(Compare)(THIS_ IUIAutomationTextRange *, BOOL *) PURE; + STDMETHOD(CompareEndpoints)(THIS_ enum TextPatternRangeEndpoint, IUIAutomationTextRange *, enum TextPatternRangeEndpoint, int *) PURE; + STDMETHOD(ExpandToEnclosingUnit)(THIS_ enum TextUnit) PURE; + STDMETHOD(FindAttribute)(THIS_ TEXTATTRIBUTEID, VARIANT, BOOL, IUIAutomationTextRange **) PURE; + STDMETHOD(FindText)(THIS_ BSTR, BOOL, BOOL, IUIAutomationTextRange **) PURE; + STDMETHOD(GetAttributeValue)(THIS_ TEXTATTRIBUTEID, VARIANT *) PURE; + STDMETHOD(GetBoundingRectangles)(THIS_ SAFEARRAY **) PURE; + STDMETHOD(GetEnclosingElement)(THIS_ IUIAutomationElement **) PURE; + STDMETHOD(GetText)(THIS_ int, BSTR *) PURE; + STDMETHOD(Move)(THIS_ enum TextUnit, int, int *) PURE; + STDMETHOD(MoveEndpointByUnit)(THIS_ enum TextPatternRangeEndpoint, enum TextUnit, int *) PURE; + STDMETHOD(MoveEndpointByRange)(THIS_ enum TextPatternRangeEndpoint, IUIAutomationTextRange *, enum TextPatternRangeEndpoint) PURE; + STDMETHOD(Select)(THIS) PURE; + STDMETHOD(AddToSelection)(THIS) PURE; + STDMETHOD(RemoveFromSelection)(THIS) PURE; + STDMETHOD(ScrollIntoView)(THIS_ BOOL) PURE; + STDMETHOD(GetChildren)(THIS_ IUIAutomationElementArray **) PURE; +}; +#undef INTERFACE + +#ifdef __cplusplus +} +#endif + +#endif // UIAUTO_HH diff --git a/wordbyauto.cc b/wordbyauto.cc new file mode 100644 index 00000000..4d31d04e --- /dev/null +++ b/wordbyauto.cc @@ -0,0 +1,114 @@ +#include +#include +#include +#include "wordbyauto.hh" +#include "uiauto.hh" + +#include +#include "dprintf.hh" + +class GDAutomationClient { +public: + GDAutomationClient(); + ~GDAutomationClient(); + bool getWordAtPoint( POINT pt ); + WCHAR *getText() { return buffer; }; +private: + WCHAR buffer[256]; + IUIAutomation *pGDAutomation; + IUIAutomationTreeWalker *pTree; +}; + +GDAutomationClient gdAuto; + +GDAutomationClient::GDAutomationClient() +{ +HRESULT hr; + CoInitializeEx( NULL, COINIT_APARTMENTTHREADED ); + hr = CoCreateInstance( CLSID_CUIAutomation , NULL, CLSCTX_INPROC_SERVER, IID_IUIAutomation, (void**)&pGDAutomation ); + if( hr != S_OK ) pGDAutomation = NULL; + pTree = NULL; + hr = pGDAutomation->get_RawViewWalker( &pTree ); + memset( buffer, 0, sizeof(buffer) ); +} + +GDAutomationClient::~GDAutomationClient() +{ + if( pTree != NULL ) pTree->Release(); + if( pGDAutomation != NULL ) pGDAutomation->Release(); + CoUninitialize(); +} + +bool GDAutomationClient::getWordAtPoint( POINT pt ) +{ +HRESULT hr; +IUIAutomationTextPattern *pTextPattern; +IUIAutomationTextRange *pTextRange; +IUIAutomationElement *pElement, *pParent; +BSTR bstr; +RECT r = { 0, 0, 0, 0 }; +bool bGoUp; + + DPRINTF("\nEntering getWordAtPoint\n"); + + if( pGDAutomation == NULL ) return false; + + buffer[0] = 0; + pElement = NULL; + hr = pGDAutomation->ElementFromPoint( pt, &pElement ); + DPRINTF("ElementFromPoint return hr=%08X, ptr=%p\n", hr, pElement); + if( hr != S_OK || pElement == NULL ) + return false; + + pTextPattern = NULL; + bGoUp = false; + while( pElement != NULL ) { + hr = pElement->GetCurrentPatternAs( UIA_TextPatternId, IID_IUIAutomationTextPattern, (void**)&pTextPattern ); + if( hr == S_OK && pTextPattern != NULL ) + break; + if( pTree == NULL ) { + pElement->Release(); + return false; + } + pParent = NULL; + hr = pTree->GetParentElement( pElement, &pParent ); + pElement->Release(); + pElement = pParent; + bGoUp = TRUE; + } + if( pElement == NULL ) + return false; + + if( !bGoUp ) { + hr = pElement->get_CurrentBoundingRectangle( &r ); + if( hr == S_OK) { + pt.x -= r.left; + pt.y -= r.top; + } + } + pElement->Release(); + + pTextRange = NULL; + hr = pTextPattern->RangeFromPoint( pt, &pTextRange ); + pTextPattern->Release(); + if( hr != S_OK || pTextRange == NULL ) + return false; + + hr = pTextRange->ExpandToEnclosingUnit( TextUnit_Word ); + if( hr == S_OK) { + hr = pTextRange->GetText( 255, &bstr ); + if (hr == S_OK) { + wsprintfW( buffer, L"%s", (LPCWSTR)bstr ); + SysFreeString( bstr ); + } + } + pTextRange->Release(); + + return ( buffer[0] != 0 ); +} + +WCHAR *gdGetWordAtPointByAutomation( POINT pt ) +{ + if( gdAuto.getWordAtPoint( pt ) ) return gdAuto.getText(); + else return NULL; +} diff --git a/wordbyauto.hh b/wordbyauto.hh new file mode 100644 index 00000000..6a4cebbf --- /dev/null +++ b/wordbyauto.hh @@ -0,0 +1,6 @@ +#ifndef __WORD_BY_AUTO_HH_INCLUDED +#define __WORD_BY_AUTO_HH_INCLUDED + +WCHAR *gdGetWordAtPointByAutomation( POINT pt ); + +#endif