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.
 
 
 
 
 
 

2284 lines
72 KiB

//
// correctionimx.cpp
//
#include "private.h"
#ifdef SUPPORT_INTERNAL_WIDGET
#include "globals.h"
#include "timsink.h"
#include "immxutil.h"
#include "sapilayr.h"
#include "spdebug.h"
#include "sphelper.h"
#include "mscandui.h"
#include "computil.h"
#include "ids.h"
#include "cicspres.h"
#include <conio.h>
#define WM_USERLBUTTONDOWN WM_USER+10000 // Replacement for LBUTTONDOWN to fix raid 8828.
LPCTSTR g_lpszClassName = TEXT("CorrectionWidget");
// BUGBUG - Pixel values currently. Need to take account of screen DPI.
const ULONG g_uWidgetWidth = 11;
const ULONG g_uWidgetHeight = 11;
const ULONG g_uActualWidgetHeight = 7;
const ULONG g_uExpandedWidgetWidth = 20;
const ULONG g_uExpandedWidgetHeight = 16;
const ULONG g_uExpandedWidgetXOffset = g_uExpandedWidgetWidth - (g_uExpandedWidgetWidth - g_uWidgetWidth)/2;
const ULONG g_uWidgetYOffset = 0; // Position expanded widget just touching actual edge of correction list when it's above widget.
const ULONG g_uExpandedWidgetYOffset = 0; // Position expanded widget just touching actual edge of correction list when it's above widget.
// ** These NEED to match the icons and each other to avoid misbehavors. **
// Small widget MUST be entirely enclosed by large widget.
const ULONG g_cSloppySelection = 3;
const ULONG g_uTimerLength = 2500; // Time before the widget starts fading out.
const ULONG g_uTimerFade = 10; // Time between alpha decrements for fadeout.
const ULONG g_uAlphaFade = 4; // Alpha fade decrement every 10ms for fadeout effect.
const ULONG g_uAlpha = 216; // Should be multiples of g_uAlphaFade
const ULONG g_uAlphaLarge = 255; // Can be anything.
const ULONG g_uAlphaInvisible = 5; // Needs to be sufficiently non zero that when combined with above, still receives mouse events.
// Can be 4 minimum when combined with alpha 255 for 24/32 bit color mode.
// Needs to be at least 5 when in 16 bit color mode.
const ULONG g_uTimerSloppyMouseLeave = 500; // Time after mouse leave correction window that it resizes small.
/****************************************************************************
* CCorrectionIMX::CCorrectionIMX *
*--------------------------------*
* Description:
* Constructor for Correction 1Tip.
*
* Returns:
* None.
*
* Args:
* None
*
**************************************************************** agarside ***/
CCorrectionIMX::CCorrectionIMX() : m_dwEditCookie(0),
m_dwLayoutCookie(0),
m_dwThreadFocusCookie(0),
m_dwKeyTraceCookie(0),
m_fExpanded(FALSE),
m_hWnd(NULL),
m_hIconInvoke(NULL),
m_hIconInvokeLarge(NULL),
m_hIconInvokeClose(NULL),
m_eWindowState(WINDOW_HIDE),
m_fDisplayAlternatesMyself(FALSE),
m_fCandidateOpen(FALSE),
m_fKeyDown(FALSE),
m_hAtom(0)
{
SPDBG_FUNC("CCorrectionIMX::CCorrectionIMX");
memset(&m_rcSelection, 0, sizeof(m_rcSelection));
}
/****************************************************************************
* CCorrectionIMX::~CCorrectionIMX *
*---------------------------------*
* Description:
* Destructor for Correction Tip
*
* Returns:
* None.
*
**************************************************************** agarside ***/
CCorrectionIMX::~CCorrectionIMX()
{
SPDBG_FUNC("CCorrectionIMX::~CCorrectionIMX");
if (m_hWnd)
{
DestroyWindow(m_hWnd);
}
if (m_hIconInvoke)
{
DestroyIcon(m_hIconInvoke);
m_hIconInvoke = NULL;
}
if (m_hIconInvokeLarge)
{
DestroyIcon(m_hIconInvokeLarge);
m_hIconInvokeLarge = NULL;
}
if (m_hIconInvokeClose)
{
DestroyIcon(m_hIconInvokeClose);
m_hIconInvokeClose = NULL;
}
}
/****************************************************************************
* CCorrectionIMX::FinalConstruct *
*--------------------------------*
* Description:
* Preliminary initialization of the object.
* Creates window class and hidden window for message pump.
* Loads icon resources.
*
* Returns: HRESULT
* S_OK - Everything succeeded.
* Otherwise, appropriate error code.
*
**************************************************************** agarside ***/
HRESULT CCorrectionIMX::FinalConstruct()
{
SPDBG_FUNC("CCorrectionIMX::FinalConstruct");
HRESULT hr = S_OK;
if (SUCCEEDED(hr))
{
m_hIconInvoke = (HICON)LoadImage(_Module.GetModuleInstance(), MAKEINTRESOURCE(IDI_INVOKE), IMAGE_ICON, g_uWidgetWidth, g_uWidgetHeight, 0);
ASSERT("Failed to create small invocation icon." && m_hIconInvoke);
if (!m_hIconInvoke)
{
hr = SpHrFromLastWin32Error();
}
}
if (SUCCEEDED(hr))
{
m_hIconInvokeLarge = (HICON)LoadImage(_Module.GetModuleInstance(), MAKEINTRESOURCE(IDI_INVOKE), IMAGE_ICON, g_uExpandedWidgetWidth, g_uExpandedWidgetHeight, 0);
ASSERT("Failed to create large invocation icon." && m_hIconInvokeLarge);
if (!m_hIconInvokeLarge)
{
hr = SpHrFromLastWin32Error();
}
}
if (SUCCEEDED(hr))
{
m_hIconInvokeClose = (HICON)LoadImage(_Module.GetModuleInstance(), MAKEINTRESOURCE(IDI_INVOKECLOSE), IMAGE_ICON, g_uExpandedWidgetWidth, g_uExpandedWidgetHeight, 0);
ASSERT("Failed to create large invocation icon." && m_hIconInvokeClose);
if (!m_hIconInvokeClose)
{
hr = SpHrFromLastWin32Error();
}
}
if (SUCCEEDED(hr))
{
if (!CicLoadStringWrapW(g_hInst, IDS_DELETESELECTION, m_wszDelete, ARRAYSIZE(m_wszDelete))
{
hr = E_OUTOFMEMORY;
}
if (!CicLoadStringWrapW(g_hInst, IDS_ADDTODICTIONARYPREFIX, m_wszAddPrefix, ARRAYSIZE(m_wszAddPrefix))
{
hr = E_OUTOFMEMORY;
}
if (!CicLoadStringWrapW(g_hInst, IDS_ADDTODICTIONARYPOSTFIX, m_wszAddPostfix, ARRAYSIZE(m_wszAddPostfix))
{
hr = E_OUTOFMEMORY;
}
}
SPDBG_REPORT_ON_FAIL(hr);
return hr;
}
/****************************************************************************
* CCorrectionIMX::LazyInitializeWindow *
*--------------------------------------*
* Description:
*
* Returns: HRESULT
*
**************************************************************** agarside ***/
HRESULT CCorrectionIMX::LazyInitializeWindow()
{
SPDBG_FUNC("CCorrectionIMX::LazyInitializeWindow");
HRESULT hr = S_OK;
if (m_hWnd)
{
return S_OK;
}
WNDCLASSEX wndClass;
memset(&wndClass, 0, sizeof(wndClass));
wndClass.cbSize = sizeof(wndClass);
wndClass.style = CS_HREDRAW | CS_VREDRAW;
wndClass.lpszClassName = g_lpszClassName;
wndClass.hInstance = _Module.GetModuleInstance();
wndClass.lpfnWndProc = WndProc;
wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
ATOM hAtom;
if ((hAtom = RegisterClassEx(&wndClass)) == 0)
{
ASSERT("Failed to register window class." && FALSE);
}
if (SUCCEEDED(hr))
{
m_hAtom = hAtom;
m_hWnd = CreateWindowEx(WS_EX_TOPMOST | WS_EX_NOACTIVATE | WS_EX_LAYERED, g_lpszClassName, g_lpszClassName, WS_POPUP | WS_DISABLED, 0, 0, g_uWidgetWidth, g_uWidgetHeight, NULL, NULL, _Module.GetModuleInstance(), this);
ASSERT("Failed to create hidden window." && m_hWnd);
if (!m_hWnd)
{
hr = SpHrFromLastWin32Error();
}
}
SPDBG_REPORT_ON_FAIL(hr);
return hr;
}
/****************************************************************************
* CCorrectionIMX::DrawWidget *
*----------------------------*
* Description:
*
* Returns: HRESULT
*
* Args:
* BYTE uAlpha
*
**************************************************************** agarside ***/
HRESULT CCorrectionIMX::DrawWidget(BYTE uAlpha)
{
SPDBG_FUNC("CCorrectionIMX::DrawWidget");
HRESULT hr = S_OK;
typedef struct _RGBALPHA {
BYTE rgbBlue;
BYTE rgbGreen;
BYTE rgbRed;
BYTE rgbAlpha;
} RGBALPHA;
HDC hdcScreen = NULL;
HDC hdcLayered = NULL;
RECT rcWindow;
SIZE size;
BITMAPINFO BitmapInfo;
HBITMAP hBitmapMem = NULL;
HBITMAP hBitmapOld = NULL;
void *pDIBits;
int i;
int j;
POINT ptSrc;
POINT ptDst;
BLENDFUNCTION Blend;
BOOL bRet;
RGBALPHA *ppxl;
GetWindowRect( m_hWnd, &rcWindow );
size.cx = rcWindow.right - rcWindow.left;
size.cy = rcWindow.bottom - rcWindow.top;
hdcScreen = GetDC( NULL );
if (hdcScreen == NULL)
{
return E_FAIL;
}
hdcLayered = CreateCompatibleDC( hdcScreen );
if (hdcLayered == NULL)
{
ReleaseDC( NULL, hdcScreen );
return E_FAIL;
}
// create bitmap
BitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
BitmapInfo.bmiHeader.biWidth = size.cx;
BitmapInfo.bmiHeader.biHeight = size.cy;
BitmapInfo.bmiHeader.biPlanes = 1;
BitmapInfo.bmiHeader.biBitCount = 8 * sizeof(_RGBALPHA);
BitmapInfo.bmiHeader.biCompression = BI_RGB;
BitmapInfo.bmiHeader.biSizeImage = 0;
BitmapInfo.bmiHeader.biXPelsPerMeter = 100;
BitmapInfo.bmiHeader.biYPelsPerMeter = 100;
BitmapInfo.bmiHeader.biClrUsed = 0;
BitmapInfo.bmiHeader.biClrImportant = 0;
hBitmapMem = CreateDIBSection( hdcScreen, &BitmapInfo, DIB_RGB_COLORS, &pDIBits, NULL, 0 );
if (pDIBits == NULL)
{
ReleaseDC( NULL, hdcScreen );
DeleteDC( hdcLayered );
return E_FAIL;
}
ICONINFO iconInfo;
if (m_fExpanded)
{
if (m_eWindowState == WINDOW_LARGE)
{
bRet = GetIconInfo(m_hIconInvokeLarge, &iconInfo);
}
else
{
bRet = GetIconInfo(m_hIconInvokeClose, &iconInfo);
}
}
else
{
bRet = GetIconInfo(m_hIconInvoke, &iconInfo);
}
if (bRet)
{
BITMAP bm, bmMask;
GetObject(iconInfo.hbmColor, sizeof(bm), &bm);
GetObject(iconInfo.hbmMask, sizeof(bmMask), &bmMask);
if (bm.bmPlanes==1 && bmMask.bmBitsPixel==1 && bmMask.bmPlanes==1)
{
ASSERT(bm.bmWidth == size.cx);
ASSERT(bm.bmHeight == size.cy);
// Copy icon into layered window.
GetDIBits(hdcScreen, iconInfo.hbmColor, 0, g_uExpandedWidgetHeight, pDIBits, &BitmapInfo, DIB_RGB_COLORS);
UINT uiNumberBytesMask = bmMask.bmHeight * bmMask.bmWidthBytes;
BYTE *bitmapBytesMask = new BYTE[uiNumberBytesMask];
if (bitmapBytesMask == NULL)
{
hr = E_OUTOFMEMORY;
}
else
{
memset(bitmapBytesMask, 0, uiNumberBytesMask);
int cBytes = GetBitmapBits(iconInfo.hbmMask,uiNumberBytesMask,bitmapBytesMask);
ASSERT(cBytes == uiNumberBytesMask);
if (bRet)
{
for (i = 0; i < size.cy; i++)
{
ppxl = (RGBALPHA *)pDIBits + ((size.cy - i - 1) * size.cx);
for (j = 0; j < size.cx; j++)
{
if ( (bitmapBytesMask[i * bmMask.bmWidthBytes + j/8] >> (7 - (j % 8)))&1)
{
ppxl->rgbRed = 0;
ppxl->rgbBlue = 0;
ppxl->rgbGreen = 0;
ppxl->rgbAlpha = g_uAlphaInvisible;
}
else
{
ppxl->rgbAlpha = 255;
}
ppxl++;
}
}
}
delete [] bitmapBytesMask;
}
}
else
{
ASSERT("Correction icon is an invalid bitmap format." && FALSE);
}
bRet = DeleteObject (iconInfo.hbmColor);
iconInfo.hbmColor=NULL;
ASSERT(bRet);
bRet = DeleteObject (iconInfo.hbmMask);
iconInfo.hbmMask=NULL;
ASSERT(bRet);
}
if (SUCCEEDED(hr))
{
ptSrc.x = 0;
ptSrc.y = 0;
ptDst.x = rcWindow.left;
ptDst.y = rcWindow.top;
Blend.BlendOp = AC_SRC_OVER;
Blend.BlendFlags = 0;
Blend.SourceConstantAlpha = uAlpha;
Blend.AlphaFormat = AC_SRC_ALPHA;
hBitmapOld = (HBITMAP)SelectObject( hdcLayered, hBitmapMem );
bRet = UpdateLayeredWindow(m_hWnd, hdcScreen, &ptDst, &size, hdcLayered, &ptSrc, 0, &Blend, ULW_ALPHA );
if (!bRet)
{
DWORD dw = GetLastError();
}
SelectObject( hdcLayered, hBitmapOld );
}
// done
ReleaseDC( NULL, hdcScreen );
DeleteDC( hdcLayered );
DeleteObject( hBitmapMem );
return hr;
}
/****************************************************************************
* CCorrectionIMX::Activate *
*--------------------------*
* Description:
* Called when Cicero is initialized on a thread.
* Allows us to initialize any Cicero related objects at this point.
*
* Returns: STDAPI
* S_OK - Everything successfully initialized.
* Otherwise, appropriate error code.
*
* Args:
* ITfThreadMgr *ptim
* Pointer to the thread input manager object for the thread.
* TfClientId tid
* Text frameworks client ID for thread.
*
**************************************************************** agarside ***/
STDAPI CCorrectionIMX::Activate(ITfThreadMgr *ptim, TfClientId tid)
{
HRESULT hr = S_OK;
SPDBG_FUNC("CCorrectionIMX::Activate");
ASSERT(m_cptim == NULL);
#if 0
hr = m_cptim.CoCreateInstance(CLSID_TF_ThreadMgr);
// Activate the thread manager
if (S_OK == hr)
{
hr = m_cptim->Activate(&m_tid);
}
#else
m_cptim = ptim;
#endif
m_ptimEventSink = new CThreadMgrEventSink(DIMCallback, ICCallback, this);
if (m_ptimEventSink == NULL)
{
hr = E_OUTOFMEMORY;
}
if (SUCCEEDED(hr))
{
hr = m_ptimEventSink->_Advise(m_cptim);
}
CComPtr<ITfSource> cpSource;
if (SUCCEEDED(hr))
{
hr = m_cptim->QueryInterface(IID_ITfSource, (void **)&cpSource);
}
if (SUCCEEDED(hr))
{
hr = cpSource->AdviseSink(IID_ITfThreadFocusSink, (ITfThreadFocusSink *)this, &m_dwThreadFocusCookie);
}
if (SUCCEEDED(hr))
{
hr = cpSource->AdviseSink(IID_ITfKeyTraceEventSink, (ITfKeyTraceEventSink *)this, &m_dwKeyTraceCookie);
}
if (SUCCEEDED(hr))
{
m_ptimEventSink->_InitDIMs(TRUE);
}
CComPtr<ITfFunctionProvider> cpSysFuncPrv;
if (SUCCEEDED(hr))
{
hr = m_cptim->GetFunctionProvider(GUID_SYSTEM_FUNCTIONPROVIDER, &cpSysFuncPrv);
}
if (SUCCEEDED(hr))
{
hr = cpSysFuncPrv->GetFunction(GUID_NULL, IID_ITfFnReconversion, (IUnknown **)&m_cpSysReconv);
}
SPDBG_REPORT_ON_FAIL(hr);
return hr;
}
/****************************************************************************
* CCorrectionIMX::Deactivate *
*----------------------------*
* Description:
* Called when the Cicero thread manager is closing down on a thread.
* Tip is required to deactivate and release all objects.
* Guaranteed to be called if we were ever activated except if a
* catastrophic failure has already occurred.
*
* Returns: STDAPI
* S_OK - Everything succeeded.
* Otherwise, appropriate error code.
*
**************************************************************** agarside ***/
STDAPI CCorrectionIMX::Deactivate()
{
SPDBG_FUNC("CCorrectionIMX::Deactivate");
HRESULT hr = S_OK;
if (m_ptimEventSink)
{
hr = m_ptimEventSink->_InitDIMs(FALSE);
if (SUCCEEDED(hr))
{
hr = m_ptimEventSink->_Unadvise();
}
delete m_ptimEventSink;
m_ptimEventSink = NULL;
}
CComPtr<ITfSource> cpSource;
if (m_cptim && m_cptim->QueryInterface(IID_ITfSource, (void **)&cpSource) == S_OK)
{
cpSource->UnadviseSink(m_dwThreadFocusCookie);
cpSource->UnadviseSink(m_dwKeyTraceCookie);
}
m_cpRangeReconv = NULL;
m_cpRangeUser = NULL;
m_cpRangeWord = NULL;
m_cpSysReconv = NULL;
// m_cptim->Deactivate();
m_cptim.Release();
if (m_hAtom)
{
UnregisterClass((LPCTSTR)m_hAtom, _Module.GetModuleInstance());
}
m_hAtom = NULL;
SPDBG_REPORT_ON_FAIL(hr);
return hr;
}
/****************************************************************************
* CCorrectionIMX::OnSetThreadFocus *
*----------------------------------*
* Description:
* Called by Cicero when our thread gets focus.
* We do nothing here.
*
* Returns: STDAPI
*
* Args:
* void
*
**************************************************************** agarside ***/
STDAPI CCorrectionIMX::OnSetThreadFocus(void)
{
SPDBG_FUNC("CCorrectionIMX::OnSetThreadFocus");
HRESULT hr = S_OK;
// We do nothing here.
SPDBG_REPORT_ON_FAIL(hr);
return hr;
}
/****************************************************************************
* CCorrectionIMX::OnKillThreadFocus *
*-----------------------------------*
* Description:
* Called by Cicero when our thread gets focus.
* We use this to intelligently hide the widget when the app loses focus.
*
* Returns: STDAPI
*
* Args:
* void
*
**************************************************************** agarside ***/
STDAPI CCorrectionIMX::OnKillThreadFocus(void)
{
SPDBG_FUNC("CCorrectionIMX::OnKillThreadFocus");
HRESULT hr = S_OK;
// When we lose focus, we must hide the widget.
hr = Show(WINDOW_HIDE);
SPDBG_REPORT_ON_FAIL(hr);
return hr;
}
/****************************************************************************
* CCorrectionIMX::OnKeyTraceDown *
*--------------------------------*
* Description:
*
* Returns: STDAPI
*
* Args:
* WPARAM wParam
* LPARAM lParam
*
**************************************************************** agarside ***/
STDAPI CCorrectionIMX::OnKeyTraceDown(WPARAM wParam,LPARAM lParam)
{
SPDBG_FUNC("CCorrectionIMX::OnKeyTraceDown");
HRESULT hr = S_OK;
m_fKeyDown = TRUE;
SPDBG_REPORT_ON_FAIL(hr);
return hr;
}
/****************************************************************************
* CCorrectionIMX::OnKeyTraceUp *
*------------------------------*
* Description:
*
* Returns: STDAPI
*
* Args:
* WPARAM wParam
* LPARAM lParam
*
**************************************************************** agarside ***/
STDAPI CCorrectionIMX::OnKeyTraceUp(WPARAM wParam,LPARAM lParam)
{
SPDBG_FUNC("CCorrectionIMX::OnKeyTraceUp");
HRESULT hr = S_OK;
m_fKeyDown = FALSE;
SPDBG_REPORT_ON_FAIL(hr);
return hr;
}
/****************************************************************************
* CCorrectionIMX::OnLayoutChange *
*--------------------------------*
* Description:
* Called by Cicero when the document is resized and/or moved provided Cicero
* application correctly handles this. This allows us to update the location
* of the widget to match the new location of the associated selection.
*
* Returns: STDAPI
* S_OK - Everything succeeded.
* Otherwise, appropriate error code.
*
* Args:
* ITfContext *pic
* Pointer to the input context which was affected.
* TfLayoutCode lcode
* Flag - one of CREATE, CHANGE, DESTROY. We do not currently use this.
* ITfContextView *pView
* Pointer to the context view affected.
*
**************************************************************** agarside ***/
STDAPI CCorrectionIMX::OnLayoutChange(ITfContext *pic, TfLayoutCode lcode, ITfContextView *pView)
{
SPDBG_FUNC("CCorrectionIMX::OnLayoutChange");
HRESULT hr = S_OK;
BOOL fInWriteSession = FALSE;
CEditSession *pes = NULL;
if (m_cpRangeReconv == NULL)
{
return S_OK;
}
// ignore events made by client tip
pic->InWriteSession( m_tid, &fInWriteSession );
if (fInWriteSession)
{
return S_OK;
}
// we only care about the active view
if (!IsActiveView( pic, (ITfContextView *)pView ))
{
return S_OK;
}
pes = new CEditSession( EditSessionCallback );
// move candidate window
if (pes)
{
pes->_state.u = ESCB_RESETTARGETPOS;
pes->_state.pv = this;
pes->_state.wParam = 0;
pes->_state.pRange = NULL;
pes->_state.pic = pic;
pic->RequestEditSession( m_tid, pes, TF_ES_READ | TF_ES_SYNC, &hr );
pes->Release();
}
else
{
hr = E_OUTOFMEMORY;
}
SPDBG_REPORT_ON_FAIL(hr);
return hr;
}
/****************************************************************************
* CCorrectionIMX::IsCandidateObjectOpen *
*---------------------------------------*
* Description:
*
* Returns: HRESULT
* Appropriate error code.
*
* Args:
* BOOL *fOpen
* TRUE if candidate object is open.
*
**************************************************************** agarside ***/
HRESULT CCorrectionIMX::IsCandidateObjectOpen(ITfContext *pic, BOOL *fOpen)
{
SPDBG_FUNC("CCorrectionIMX::IsCandidateObjectOpen");
HRESULT hr = S_OK;
CComPtr<ITfCompartmentMgr> cpCompMgr;
CComPtr<ITfCompartment> cpComp;
CComPtr<ITfContext> cpICTop;
CComPtr<ITfDocumentMgr> cpDim;
CComVariant cpVarCandOpen;
// Default to candidate UI not open in case of failure.
*fOpen = FALSE;
cpVarCandOpen.lVal = 0;
hr = pic->GetDocumentMgr(&cpDim);
if (SUCCEEDED(hr) && cpDim)
{
// Could shortcut check here if cpICTop and pic are the same object (check IUnknowns).
hr = cpDim->GetTop(&cpICTop);
}
if (SUCCEEDED(hr) && cpICTop)
{
hr = cpICTop->QueryInterface(&cpCompMgr);
}
if (SUCCEEDED(hr) && cpCompMgr)
{
hr = cpCompMgr->GetCompartment(GUID_COMPARTMENT_MSCANDIDATEUI_CONTEXT, &cpComp);
}
if (SUCCEEDED(hr) && cpComp)
{
hr = cpComp->GetValue(&cpVarCandOpen);
// If the Top IC has this set to one, then this IC was created by the candidate UI object and hence we
// do *not* want to display the widget.
}
if (SUCCEEDED(hr))
{
*fOpen = (cpVarCandOpen.lVal == 1);
}
SPDBG_REPORT_ON_FAIL(hr);
return hr;
}
/****************************************************************************
* CCorrectionIMX::OnEndEdit *
*---------------------------*
* Description:
* Called when something causes submission of an edit to the input context.
*
* Returns: STDAPI
* S_OK - Everything succeeded.
* Otherwise, appropriate error code.
*
* Args:
* ITfContext *pic
* Input context affected.
* TfEditCookie ecReadOnly
* Read only cookie for immediate use.
* ITfEditRecord *pEditRecord
* Pointer to object allowing the details of the edit to be investigated.
* We use this solely to find out if the selection changed since we do not need
* to take action based on anything else.
*
**************************************************************** agarside ***/
STDAPI CCorrectionIMX::OnEndEdit(ITfContext *pic, TfEditCookie ecReadOnly, ITfEditRecord *pEditRecord)
{
SPDBG_FUNC("CCorrectionIMX::OnEndEdit");
HRESULT hr = S_OK;
CComPtr<ITfRange> cpRangeUser;
CComPtr<ITfRange> cpRangeWord;
CComPtr<ITfRange> cpRangeReconv;
CComPtr<ITfContextView> cpView;
BOOL fHideWidget = TRUE;
BOOL fSelectionChanged = FALSE;
BOOL fCandOpen = FALSE;
BOOL fHasFocus = TRUE; // If we fail focus check in any way, assume does have focus.
m_fDisplayAlternatesMyself = FALSE;
hr = pic->GetActiveView(&cpView);
if (SUCCEEDED(hr))
{
HWND hWnd;
hr = cpView->GetWnd(&hWnd);
if (hWnd == GetFocus())
{
fHasFocus = TRUE;
}
}
if (fHasFocus && !m_fCandidateOpen)
{
// Candidate list is open. We do not want to display correction widget.
pEditRecord->GetSelectionStatus(&fSelectionChanged);
// if (fSelectionChanged) // This is FALSE when IP first put into RichEdit stage! RE also claims IP is at end of previous utterance :-(.
{
// Get user selection.
BOOL fEmpty = FALSE;
hr = GetSelectionSimple(ecReadOnly, pic, &cpRangeUser);
if (SUCCEEDED(hr))
{
// Check it isn't empty. If it is empty we want to hide the widget.
hr = cpRangeUser->IsEmpty(ecReadOnly, &fEmpty);
}
if (SUCCEEDED(hr) && fEmpty && !m_fKeyDown)
{
BOOL fMatch = FALSE;
if (m_cpRangeUser)
{
cpRangeUser->IsEqualStart(ecReadOnly, m_cpRangeUser, TF_ANCHOR_START, &fMatch);
if (!fMatch)
{
// Check end point.
cpRangeUser->IsEqualStart(ecReadOnly, m_cpRangeUser, TF_ANCHOR_END, &fMatch);
}
}
if (!fMatch)
{
// Find word range (using white space delimiters upto 20 characters either side).
FindWordRange(ecReadOnly, cpRangeUser, &cpRangeWord);
}
}
if (!fEmpty)
{
cpRangeWord = cpRangeUser;
}
if (SUCCEEDED(hr) && cpRangeWord && (!fEmpty || !m_fKeyDown))
{
// Get reconversion range.
BOOL fConvertable = FALSE;
hr = m_cpSysReconv->QueryRange(cpRangeWord, &cpRangeReconv, &fConvertable);
// Will validly fail if there is no alternates - e.g. partial words or typed text.
hr = S_OK;
BOOL fMatch = FALSE;
if (SUCCEEDED(hr) && fConvertable)
{
hr = DoesUserSelectionMatchReconversion(ecReadOnly, cpRangeWord, cpRangeReconv, &fMatch);
// May not be convertable or ranges may not match.
}
if (SUCCEEDED(hr) && fMatch)
{
fHideWidget = FALSE;
// Convertable and ranges do match.
}
else
{
if (fEmpty)
{
cpRangeReconv = NULL;
cpRangeReconv = cpRangeWord;
fHideWidget = FALSE;
m_fDisplayAlternatesMyself = TRUE;
}
else
{
// Find word range (using white space delimiters upto 20 characters either side).
CComPtr<ITfRange> cpRangeWordTmp;
CComPtr<ITfRange> cpRangeClone;
hr = cpRangeWord->Clone(&cpRangeClone);
if (SUCCEEDED(hr))
{
hr = cpRangeClone->Collapse(ecReadOnly, TF_ANCHOR_START);
}
if (SUCCEEDED(hr))
{
LONG pcch = 0;
hr = cpRangeClone->ShiftStart(ecReadOnly, 1, &pcch, NULL);
}
if (SUCCEEDED(hr))
{
hr = FindWordRange(ecReadOnly, cpRangeClone, &cpRangeWordTmp);
}
if (cpRangeWordTmp)
{
fMatch = FALSE;
hr = DoesUserSelectionMatchReconversion(ecReadOnly, cpRangeWord, cpRangeWordTmp, &fMatch);
}
if (SUCCEEDED(hr) && cpRangeWordTmp && fMatch)
{
cpRangeReconv = NULL;
cpRangeReconv = cpRangeWord;
fHideWidget = FALSE;
m_fDisplayAlternatesMyself = TRUE;
}
}
}
}
}
}
if (m_fCandidateOpen || !fHasFocus)
{
SPDBG_REPORT_ON_FAIL(hr);
return hr;
}
if (fHideWidget)
{
m_cpRangeReconv = NULL;
if (cpRangeUser)
{
m_cpRangeUser = NULL;
m_cpRangeUser = cpRangeUser;
}
if (cpRangeWord)
{
m_cpRangeWord = NULL;
m_cpRangeWord = cpRangeWord;
}
Show(WINDOW_HIDE);
}
else
{
m_cpRangeUser = NULL;
m_cpRangeUser = cpRangeUser;
m_cpRangeWord = NULL;
m_cpRangeWord = cpRangeWord;
m_cpRangeReconv = NULL;
m_cpRangeReconv = cpRangeReconv;
m_cpic = pic;
// Update selection screen coordinates to match user selection (not reconversion range).
hr = UpdateWidgetLocation(ecReadOnly);
if (SUCCEEDED(hr))
{
Show(WINDOW_SMALLSHOW);
}
}
SPDBG_REPORT_ON_FAIL(hr);
return hr;
}
// PRIVATE FUNCTIONS
/****************************************************************************
* CCorrectionIMX::CompareRange *
*------------------------------*
* Description:
* Compare two ranges and set a boolean value TRUE if they match.
*
* Returns: HRESULT
* S_OK - Everything succeeded.
* Otherwise, appropriate error code.
*
* Args:
* TfEditCookie ecReadOnly
* Edit cookie.
* ITfRange *pRange1
* First range.
* ITfRange *pRange2
* Second range.
* BOOL *fIdentical
* Boolean return value. TRUE = ranges match.
*
**************************************************************** agarside ***/
HRESULT CCorrectionIMX::CompareRange(TfEditCookie ecReadOnly, ITfRange *pRange1, ITfRange *pRange2, BOOL *fIdentical)
{
SPDBG_FUNC("CCorrectionIMX::CompareRange");
HRESULT hr = S_OK;
LONG lStartResult = -1, lEndResult = -1;
*fIdentical = FALSE;
hr = pRange1->CompareStart(ecReadOnly, pRange2, TF_ANCHOR_START, &lStartResult);
if (SUCCEEDED(hr))
{
hr = pRange1->CompareEnd(ecReadOnly, pRange2, TF_ANCHOR_END, &lEndResult);
}
if (SUCCEEDED(hr) && lStartResult == 0 && lEndResult == 0)
{
*fIdentical = TRUE;
}
SPDBG_REPORT_ON_FAIL(hr);
return hr;
}
/****************************************************************************
* CCorrectionIMX::FindWordRange *
*-------------------------------*
* Description:
*
* Returns: HRESULT
* S_OK - Everything succeeded.
* Otherwise, appropriate error code.
*
* Args:
* TfEditCookie ecReadOnly
* Edit cookie.
* ITfRange *pRangeIP
* Range of the IP (zero length)
* ITfRange *ppRangeWord
* Returned range of the word found by simple word breaking algorithm.
*
**************************************************************** agarside ***/
HRESULT CCorrectionIMX::FindWordRange(TfEditCookie ecReadOnly, ITfRange *pRangeIP, ITfRange **ppRangeWord)
{
SPDBG_FUNC("CCorectionIMX::FindWordRange");
HRESULT hr = S_OK;
CComPtr<ITfRangeACP> cpRangeIPACP;
LONG cchStart = 0, cchEnd = 0, iStart = 0, iEnd = 0;
ULONG cchTotal = 0;
WCHAR wzText[41];
*ppRangeWord = NULL;
if (SUCCEEDED(hr))
{
hr = pRangeIP->QueryInterface(IID_ITfRangeACP, (void **)&cpRangeIPACP);
}
if (SUCCEEDED(hr))
{
hr = cpRangeIPACP->ShiftStart(ecReadOnly, -20, &cchStart, NULL);
}
if (SUCCEEDED(hr))
{
hr = cpRangeIPACP->ShiftEnd(ecReadOnly, 20, &cchEnd, NULL);
}
if (SUCCEEDED(hr))
{
hr = cpRangeIPACP->GetText(ecReadOnly, 0, wzText, 40, &cchTotal);
}
wzText[cchTotal] = 0;
if (SUCCEEDED(hr))
{
iStart = abs(cchStart);
while (iStart >= 0)
{
iStart --;
if (wzText[iStart] < 'A' ||
wzText[iStart] > 'z' ||
(wzText[iStart] > 'Z' && wzText[iStart] < 'a') )
{
break;
}
}
iStart ++;
if (iStart == abs(cchStart))
{
// Special case - do not show widget on IP when IP at start of the word.
return S_OK;
}
iEnd = abs(cchStart);
while (iEnd < (LONG)cchTotal)
{
if (wzText[iEnd] < 'A' ||
wzText[iEnd] > 'z' ||
(wzText[iEnd] > 'Z' && wzText[iEnd] < 'a') )
{
break;
}
iEnd ++;
}
if (iEnd == abs(cchStart))
{
return S_OK;
}
LONG cchTemp;
if (iStart > 0)
{
hr = cpRangeIPACP->ShiftStart(ecReadOnly, iStart, &cchTemp, NULL);
}
if (SUCCEEDED(hr))
{
if (iEnd < (LONG)cchTotal)
{
hr = cpRangeIPACP->ShiftEnd(ecReadOnly, iEnd - cchTotal, &cchTemp, NULL);
}
}
if (SUCCEEDED(hr))
{
hr = pRangeIP->Clone(ppRangeWord);
}
}
SPDBG_REPORT_ON_FAIL(hr);
return hr;
}
/****************************************************************************
* CCorrectionIMX::DoesUserSelectionMatchReconversion *
*----------------------------------------------------*
* Description:
* Compares the user selection to the reconversion range returned by the tip.
* Need to match up exactly barring a small amount of white space at start and end.
*
* Returns: HRESULT
* S_OK - Everything succeeded.
* Otherwise, appropriate error code.
*
* Args:
* TfEditCookie ecReadOnly
* Edit cookie.
* ITfRange *pRangeUser
* User selection range.
* BOOL *fMatch
* Boolean return value for whether they match or not.
*
**************************************************************** agarside ***/
HRESULT CCorrectionIMX::DoesUserSelectionMatchReconversion(TfEditCookie ecReadOnly, ITfRange *pRangeUser, ITfRange *pRangeReconv, BOOL *fMatch)
{
SPDBG_FUNC("CCorrectionIMX::DoesUserSelectionMatchReconversion");
HRESULT hr = S_OK;
CComPtr<ITfRangeACP> cpRangeUserACP, cpReconvACP;
CComPtr<ITfRange> cpRangeClone;
*fMatch = FALSE;
hr = pRangeUser->QueryInterface(IID_ITfRangeACP, (void **)&cpRangeUserACP);
if (SUCCEEDED(hr))
{
hr = pRangeReconv->QueryInterface(IID_ITfRangeACP, (void **)&cpReconvACP);
}
if (cpRangeUserACP && cpReconvACP)
{
while (TRUE)
{
LONG iStartSelection, iStartReconv, iEndSelection, iEndReconv;
ULONG startchars, endchars;
LONG cch;
WCHAR starttext[g_cSloppySelection+1], endtext[g_cSloppySelection+1];
// Get start indexs and end offsets.
cpRangeUserACP->GetExtent(&iStartSelection, &iEndSelection);
cpReconvACP->GetExtent(&iStartReconv, &iEndReconv);
// Convert end character positions to absolute values.
iEndSelection += iStartSelection;
iEndReconv += iStartReconv;
if (abs(iStartSelection-iStartReconv) > g_cSloppySelection ||
abs(iEndSelection-iEndReconv) > g_cSloppySelection)
{
// Two much of a mismatch between selection and reconversion range.
// Do not display widget.
break;
}
if (abs(iStartSelection-iStartReconv) == 0 &&
abs(iEndSelection - iEndReconv) == 0)
{
// Shortcut check.
*fMatch = TRUE;
break;
}
if (iStartSelection<iStartReconv)
{
hr = cpRangeUserACP->GetText(ecReadOnly, 0, starttext, abs(iStartSelection-iStartReconv), &startchars);
}
else
{
hr = cpReconvACP->GetText(ecReadOnly, 0, starttext, abs(iStartSelection-iStartReconv), &startchars);
}
starttext[startchars] = 0;
if (SUCCEEDED(hr))
{
if (iEndSelection<iEndReconv)
{
hr = pRangeReconv->Clone(&cpRangeClone);
if (SUCCEEDED(hr))
{
hr = cpRangeClone->ShiftStart(ecReadOnly, iEndSelection-iStartReconv, &cch, NULL);
}
}
else
{
hr = pRangeUser->Clone(&cpRangeClone);
if (SUCCEEDED(hr))
{
hr = cpRangeClone->ShiftStart(ecReadOnly, iEndReconv-iStartSelection, &cch, NULL);
}
}
}
if (SUCCEEDED(hr))
{
hr = cpRangeClone->GetText(ecReadOnly, 0, endtext, abs(iEndSelection-iEndReconv), &endchars);
endtext[endchars] = 0;
UINT i;
if (iStartSelection != iStartReconv)
{
for (i = 0; i < (UINT)abs(iStartReconv-iStartSelection); i++)
{
if (starttext[i] != L' ')
{
break;
}
}
if (i != (UINT)abs(iStartReconv-iStartSelection))
{
break;
}
}
if (iEndSelection != iEndReconv)
{
for (i = 0; i < (UINT)abs(iEndReconv-iEndSelection); i++)
{
if (endtext[i] != L' ' && endtext[i] != 13)
{
// 13 is found when selection hits end of richedit contents.
// Range is 1 longer than it should be and contains a carriage return.
break;
}
}
if (i != (UINT)abs(iEndReconv-iEndSelection))
{
break;
}
}
*fMatch = TRUE;
break;
}
// We failed. We need to exit the loop now.
break;
}
}
SPDBG_REPORT_ON_FAIL(hr);
return hr;
}
/****************************************************************************
* CCorrectionIMX::GetReconversion *
*---------------------------------*
* Description:
*
* Returns: HRESULT
*
* Args:
* TfEditCookie ec
* ITfCandidateList** ppCandList
*
**************************************************************** agarside ***/
HRESULT CCorrectionIMX::GetReconversion(TfEditCookie ec, ITfCandidateList** ppCandList)
{
SPDBG_FUNC("CCorrectionIMX::GetReconversion");
HRESULT hr = E_NOTIMPL;
SPDBG_REPORT_ON_FAIL(hr);
return hr;
}
/****************************************************************************
* CCorrectionIMX::Show *
*----------------------*
* Description:
* Shows / hides / and resizes / repositions correction widget window as
* requested.
*
* Returns: HRESULT
* S_OK - Everything succeeded.
* Otherwise, appropriate error code.
*
* Args:
* WINDOWSTATE eWindowState
* One of 5 enumerations stating requested action.
*
**************************************************************** agarside ***/
HRESULT CCorrectionIMX::Show(WINDOWSTATE eWindowState)
{
SPDBG_FUNC("CCorrectionIMX::Show");
HRESULT hr = S_OK;
m_eWindowState = eWindowState;
// First update the expanded state flag.
switch (m_eWindowState)
{
case WINDOW_HIDE:
case WINDOW_SMALL:
case WINDOW_SMALLSHOW:
{
m_fExpanded = FALSE;
break;
}
case WINDOW_LARGE:
case WINDOW_LARGECLOSE:
{
m_fExpanded = TRUE;
break;
}
case WINDOW_REFRESH:
{
break;
}
default:
{
ASSERT("Reached default in CCorrectionIMX::Show" && FALSE);
}
}
// Need to ensure we delete hRgn3 in all cases below where we do not pass it to SetWindowRgn.
UINT uTop = m_rcSelection.top + g_uWidgetYOffset; //( m_rcSelection.top + m_rcSelection.bottom - g_uWidgetHeight ) / 2;
UINT uTopExpanded = m_rcSelection.top + g_uExpandedWidgetYOffset; //( m_rcSelection.top + m_rcSelection.bottom - g_uExpandedWidgetHeight ) / 2;
if (SUCCEEDED(hr))
{
// Now show/hide the window as requested.
switch (m_eWindowState)
{
case WINDOW_HIDE:
{
KillTimer(m_hWnd, ID_HIDETIMER);
KillTimer(m_hWnd, ID_FADETIMER);
ShowWindow(m_hWnd, SW_HIDE);
break;
}
case WINDOW_SMALLSHOW:
{
if (!m_hWnd)
{
LazyInitializeWindow();
}
KillTimer(m_hWnd, ID_FADETIMER);
SetTimer(m_hWnd, ID_HIDETIMER, g_uTimerLength, NULL);
// Should be same as WINDOW_SMALL
SetWindowPos(m_hWnd, HWND_TOPMOST, m_rcSelection.left - g_uWidgetWidth, uTop, g_uWidgetWidth, g_uWidgetHeight, SWP_NOACTIVATE);
// Now show the window in new location.
ShowWindow(m_hWnd, SW_SHOWNA);
DrawWidget(g_uAlpha);
break;
}
case WINDOW_SMALL:
{
KillTimer(m_hWnd, ID_FADETIMER);
SetTimer(m_hWnd, ID_HIDETIMER, g_uTimerLength, NULL);
// Resize and reposition window.
SetWindowPos(m_hWnd, HWND_TOPMOST, m_rcSelection.left - g_uWidgetWidth, uTop, g_uWidgetWidth, g_uWidgetHeight, SWP_NOACTIVATE);
// Now we can switch to TRUE layered window drawing.
DrawWidget(g_uAlpha);
break;
}
case WINDOW_LARGE:
case WINDOW_LARGECLOSE:
{
KillTimer(m_hWnd, ID_FADETIMER);
KillTimer(m_hWnd, ID_HIDETIMER);
// Resize and reposition window.
SetWindowPos(m_hWnd, HWND_TOPMOST, m_rcSelection.left - g_uExpandedWidgetWidth, uTopExpanded, g_uExpandedWidgetWidth, g_uExpandedWidgetHeight, SWP_NOACTIVATE);
DrawWidget(g_uAlphaLarge);
break;
}
case WINDOW_REFRESH:
{
KillTimer(m_hWnd, ID_FADETIMER);
// Do not kill hide timer here - the window has moved is all that is happening.
if (m_fExpanded)
{
SetWindowPos(m_hWnd, HWND_TOPMOST, m_rcSelection.left - g_uExpandedWidgetWidth, uTopExpanded, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
}
else
{
SetWindowPos(m_hWnd, HWND_TOPMOST, m_rcSelection.left - g_uWidgetWidth, uTop, 0, 0, SWP_NOSIZE | SWP_NOACTIVATE);
}
break;
}
default:
{
ASSERT("Reached default in CCorrectionIMX::Show" && FALSE);
break;
}
}
}
SPDBG_REPORT_ON_FAIL(hr);
return hr;
}
/****************************************************************************
* CCorrectionIMX::WndProc *
*-------------------------*
* Description:
* Windows message handling procedure for hidden window.
*
* Returns: LRESULT CALLBACK
* Appropriate return values based on Windows message received.
*
* Args:
* HWND hWnd
* Handle to hidden window.
* UINT uMsg
* Message number.
* WPARAM wParam
* Message data.
* LPARAM lParam
* Message data.
*
**************************************************************** agarside ***/
/* static */
LRESULT CALLBACK CCorrectionIMX::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
SPDBG_FUNC("CCorrectionIMX::WndProc");
static CCorrectionIMX *_this = NULL;
switch (uMsg)
{
case WM_TIMER:
{
if (wParam == ID_HIDETIMER)
{
KillTimer(_this->m_hWnd, ID_HIDETIMER);
_this->m_uAlpha = g_uAlpha;
SetTimer(_this->m_hWnd, ID_FADETIMER, g_uTimerFade, NULL);
}
if (wParam == ID_FADETIMER)
{
_this->m_uAlpha -= g_uAlphaFade;
if (_this->m_uAlpha <= 0)
{
KillTimer(_this->m_hWnd, ID_FADETIMER);
_this->Show(WINDOW_HIDE);
}
else
{
_this->DrawWidget((BYTE)_this->m_uAlpha);
}
}
if (wParam == ID_MOUSELEAVETIMER)
{
POINT pt;
GetCursorPos(&pt);
ScreenToClient(hWnd, &pt);
LPARAM lP = MAKELPARAM(pt.x, pt.y);
PostMessage(hWnd, WM_MOUSEMOVE, 0, lP);
}
break;
}
case WM_SETCURSOR:
{
if (HIWORD(lParam) == WM_LBUTTONDOWN)
{
PostMessage(hWnd, WM_USERLBUTTONDOWN, 0, 0);
return 1;
}
if (HIWORD(lParam) == WM_MOUSEMOVE)
{
POINT pt;
GetCursorPos(&pt);
ScreenToClient(hWnd, &pt);
LPARAM lP = MAKELPARAM(pt.x, pt.y);
PostMessage(hWnd, WM_MOUSEMOVE, 0, lP);
}
SetCursor(LoadCursor(NULL, IDC_ARROW));
return 1;
}
case WM_NCCREATE:
{
_this = (CCorrectionIMX *)(((CREATESTRUCT *)lParam)->lpCreateParams);
return 1;
}
case WM_MOUSEACTIVATE:
{
// We are interested in the mouse click but don't want to activate.
return MA_NOACTIVATE;
}
case WM_MOUSEMOVE:
{
::KillTimer(hWnd, ID_MOUSELEAVETIMER);
int xPos = LOWORD(lParam);
int yPos = HIWORD(lParam);
if (!_this->m_fExpanded)
{
if (xPos > 0 && yPos > 0 && xPos < g_uWidgetWidth && yPos < g_uWidgetHeight)
{
_this->Show(WINDOW_LARGE);
::SetTimer(hWnd, ID_MOUSELEAVETIMER, g_uTimerSloppyMouseLeave, NULL);
}
}
else
{
if (xPos < 0 || yPos < 0 || xPos >= g_uExpandedWidgetWidth || yPos >= g_uExpandedWidgetHeight)
{
if (_this->m_eWindowState != WINDOW_LARGECLOSE)
{
_this->Show(WINDOW_SMALL);
}
}
else
{
::SetTimer(hWnd, ID_MOUSELEAVETIMER, g_uTimerSloppyMouseLeave, NULL);
}
}
return 1;
}
case WM_USERLBUTTONDOWN:
{
HRESULT hr = S_OK;
CEditSession *pes;
// Is it within our window if we are the small widget?
if (_this->m_fExpanded)
{
// Need to call this first, but it will reset the m_fExpanded flag.
if (_this->m_eWindowState == WINDOW_LARGE)
{
_this->Show(WINDOW_LARGECLOSE);
if (_this->m_cpRangeReconv)
{
if (!_this->m_fDisplayAlternatesMyself)
{
hr = _this->m_cpSysReconv->Reconvert(_this->m_cpRangeReconv);
}
else
{
pes = new CEditSession( EditSessionCallback );
if (pes)
{
pes->_state.u = ESCB_RECONVERTMYSELF;
pes->_state.pv = _this;
pes->_state.wParam = 0;
pes->_state.pRange = _this->m_cpRangeReconv;
pes->_state.pic = _this->GetIC();
pes->_state.pic->RequestEditSession( _this->GetId(), pes, TF_ES_ASYNC | TF_ES_READWRITE, &hr);
pes->Release();
}
}
}
}
else
{
// Send escape character which by design, causes candidate UI to close :-).
INPUT escape[2];
escape[0].type = INPUT_KEYBOARD;
escape[0].ki.wVk = 0;
escape[0].ki.wScan = 27;
escape[0].ki.dwFlags = KEYEVENTF_UNICODE;
escape[1] = escape[0];
escape[1].ki.dwFlags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP;
SendInput(2, escape, sizeof(escape[0]));
}
}
else
{
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
return 1;
}
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
/****************************************************************************
* CCorrectionIMX::UpdateWidgetLocation *
*--------------------------------------*
* Description:
* Queries Cicero for the location of the selected text in order to
* position the widget appropriately. The selection is adjusted when the
* last the right edge of the document and the selection ends just to the
* left of the next word on the next line of the document. In this case,
* we want to display the widget at the right side of the document on the
* first line so we remove all trailing spaces to find this position.
*
* Returns: HRESULT
* S_OK - Everything succeeded.
* Otherwise, appropriate error code.
*
* Args:
* void
*
**************************************************************** agarside ***/
HRESULT CCorrectionIMX::UpdateWidgetLocation(TfEditCookie ec)
{
SPDBG_FUNC("CCorrectionIMX::UpdateWidgetLocation");
HRESULT hr = S_OK;
if (m_cpRangeWord)
{
CComPtr<ITfRange> cpCollapsedRange;
hr = m_cpRangeWord->Clone(&cpCollapsedRange);
if (SUCCEEDED(hr))
{
hr = cpCollapsedRange->Collapse(ec, TF_ANCHOR_START);
}
BOOL fClipped = FALSE;
if (SUCCEEDED(hr))
{
hr = GetTextExtInActiveView(ec, cpCollapsedRange, &m_rcSelection, &fClipped);
}
}
SPDBG_REPORT_ON_FAIL(hr);
return hr;
}
/****************************************************************************
* CCorrectionIMX::EditSessionCallback *
*-------------------------------------*
* Description:
* Called by Cicero when a request for an edit session has been granted.
*
* Returns: HRESULT
* S_OK - Everything succeeded.
* Otherwise, appropriate error code.
*
* Args:
* TfEditCookie ec
* Appropriate edit cookie as requested.
* CEditSession *pes
* Object containing data and pointers supplied when edit session was requested.
*
**************************************************************** agarside ***/
/* static */
HRESULT CCorrectionIMX::EditSessionCallback(TfEditCookie ec, CEditSession *pes)
{
SPDBG_FUNC("CCorrectionIMX::EditSessionCallback");
HRESULT hr = S_OK;
CCorrectionIMX *_this = (CCorrectionIMX *)pes->_state.pv;
switch (pes->_state.u)
{
case ESCB_RESETTARGETPOS:
{
if (_this->m_cpRangeWord)
{
// Find new location of selection (range)
hr = _this->UpdateWidgetLocation(ec);
if (SUCCEEDED(hr))
{
// Reposition correction widget without resizing.
hr = _this->Show(WINDOW_REFRESH);
}
}
break;
}
case ESCB_RECONVERTMYSELF:
{
CComPtr<ITfCandidateList> cpCandList;
WCHAR wzText[MAX_PATH];
WCHAR wzWord[MAX_PATH];
WCHAR *wzTmp;
ULONG cchTotal;
LANGID langid = GetUserDefaultLangID(); // BUGBUG - How should we get this value?
CCandidateList *pCandList = new CCandidateList(CCorrectionIMX::SetResult, pes->_state.pic, pes->_state.pRange, CCorrectionIMX::SetOptionResult);
if (NULL == pCandList)
{
hr = E_OUTOFMEMORY;
}
if (SUCCEEDED(hr))
{
hr = SetSelectionSimple(ec, pes->_state.pic, pes->_state.pRange);
}
if (SUCCEEDED(hr))
{
cchTotal = 0;
hr = pes->_state.pRange->GetText(ec, 0, wzText, ARRAYSIZE(wzText)-1, &cchTotal);
wzText[cchTotal] = 0;
// Space strip word without altering contents of wzText as we use this later on.
wzTmp = wzText;
while (*wzTmp == ' ')
{
wzTmp++;
cchTotal--;
}
wcscpy(wzWord, wzTmp);
while (cchTotal > 1 && wzWord[cchTotal-1] == ' ')
{
cchTotal--;
wzWord[cchTotal] = 0;
}
}
if (SUCCEEDED(hr))
{
if (cchTotal > 0)
{
// Toggle case of first letter.
WCHAR wzTmp[2];
wzTmp[0] = wzText[0];
wzTmp[1] = 0;
_wcslwr(wzTmp);
if (wzTmp[0] == wzText[0])
{
_wcsupr(wzTmp);
}
if (SUCCEEDED(hr) && wzText[0] != wzTmp[0])
{
wzText[0] = wzTmp[0];
hr = pCandList->AddString(wzText, langid, (void **)_this, NULL, NULL, 0);
}
}
}
if (SUCCEEDED(hr))
{
HRESULT tmpHr = _this->IsWordInDictionary(wzWord);
if ( tmpHr == S_FALSE )
{
CComBSTR cpbstr;
cpbstr.Append(_this->m_wszAddPrefix);
cpbstr.Append(wzWord);
cpbstr.Append(_this->m_wszAddPostfix);
hr = pCandList->AddOption(cpbstr, langid, (void **)_this, NULL, NULL, 0, NULL, wzWord);
}
}
if (SUCCEEDED(hr))
{
HICON hDeleteIcon = LoadIcon(GetSpgrmrInstance(), MAKEINTRESOURCE(IDI_SPTIP_DELETEICON));
hr = pCandList->AddOption(_this->m_wszDelete, langid, (void **)_this, NULL, NULL, 2, hDeleteIcon, NULL);
}
if (SUCCEEDED(hr))
{
hr = pCandList->QueryInterface(IID_ITfCandidateList, (void **)&cpCandList);
}
if (SUCCEEDED(hr))
{
hr = _this->ShowCandidateList(ec, pes->_state.pic, pes->_state.pRange, cpCandList);
}
if (pCandList)
{
pCandList->Release();
}
break;
}
case ESCB_INJECTALTERNATETEXT:
{
CComPtr<ITfRange> cpInsertionPoint;
hr = GetSelectionSimple(ec, pes->_state.pic, &cpInsertionPoint);
cpInsertionPoint->SetText(ec, 0, (BSTR)pes->_state.wParam, -1);
cpInsertionPoint->Collapse(ec, TF_ANCHOR_END);
SetSelectionSimple(ec, pes->_state.pic, cpInsertionPoint);
SysFreeString((BSTR)pes->_state.wParam);
break;
}
}
SPDBG_REPORT_ON_FAIL(hr);
return hr;
}
/****************************************************************************
* CCorrectionIMX::IsWordInDictionary *
*------------------------------------*
* Description:
*
* Returns: HRESULT
*
* Args:
* WCHAR *pwzWord
*
**************************************************************** agarside ***/
HRESULT CCorrectionIMX::IsWordInDictionary(WCHAR *pwzWord)
{
SPDBG_FUNC("CCorrectionIMX::IsWordInDictionary");
HRESULT hr = S_OK;
// here we should query the speech user dictionary.
if (!m_cpLexicon)
{
hr = m_cpLexicon.CoCreateInstance(CLSID_SpLexicon);
}
if (SUCCEEDED(hr) && m_cpLexicon)
{
SPWORDPRONUNCIATIONLIST spwordpronlist;
memset(&spwordpronlist, 0, sizeof(spwordpronlist));
// Find out status of word in lexicon.
hr = m_cpLexicon->GetPronunciations(pwzWord, 0x409, eLEXTYPE_USER | eLEXTYPE_APP, &spwordpronlist);
if (hr == SPERR_NOT_IN_LEX)
{
hr = S_FALSE;
}
else if (hr == SP_WORD_EXISTS_WITHOUT_PRONUNCIATION)
{
hr = S_OK;
}
//free all the buffers
CoTaskMemFree(spwordpronlist.pvBuffer);
}
SPDBG_REPORT_ON_FAIL(hr);
return hr;
}
/****************************************************************************
* CCorrectionIMX::AddWordToDictionary *
*-------------------------------------*
* Description:
*
* Returns: HRESULT
*
* Args:
* WCHAR *pwzWord
*
**************************************************************** agarside ***/
HRESULT CCorrectionIMX::AddWordToDictionary(WCHAR *pwzWord)
{
SPDBG_FUNC("CCorrectionIMX::AddWordToDictionary");
HRESULT hr = S_OK;
if (m_cpLexicon)
{
// Add in unknown pronciation.
hr = m_cpLexicon->AddPronunciation(pwzWord, 0x409, SPPS_Unknown, NULL);
}
SPDBG_REPORT_ON_FAIL(hr);
return hr;
}
/****************************************************************************
* CCorrectionIMX::RemoveWordFromDictionary *
*------------------------------------------*
* Description:
*
* Returns: HRESULT
*
* Args:
* WCHAR *pwzWord
*
**************************************************************** agarside ***/
HRESULT CCorrectionIMX::RemoveWordFromDictionary(WCHAR *pwzWord)
{
SPDBG_FUNC("CCorrectionIMX::RemoveWordFromDictionary");
HRESULT hr = S_OK;
if (m_cpLexicon)
{
// Since the last paremeter is NULL here, all instances of the word are removed.
hr = m_cpLexicon->RemovePronunciation(pwzWord, 0x409, SPPS_Unknown, NULL);
// IGNORE ERRORS DELIBERATELY HERE.
ASSERT("Unexpected error trying to clear word in user lexicon." && (SUCCEEDED(hr) || hr == SPERR_NOT_IN_LEX));
hr = S_OK;
}
SPDBG_REPORT_ON_FAIL(hr);
return hr;
}
/****************************************************************************
* CCorrectionIMX::ShowCandidateList *
*-----------------------------------*
* Description:
*
* Returns: HRESULT
*
* Args:
* TfEditCookie ec
* ITfContext *pic
* ITfRange *pRange
* ITfCandidateList *pCandList
*
**************************************************************** agarside ***/
HRESULT CCorrectionIMX::ShowCandidateList(TfEditCookie ec, ITfContext *pic, ITfRange *pRange, ITfCandidateList *pCandList)
{
SPDBG_FUNC("CCorrectionIMX::ShowCandidateList");
HRESULT hr = S_OK;
CComPtr<ITfDocumentMgr> cpdim;
// Create candidate object if necessary. Use previously created object if we have it.
if (m_cpCandUIEx == NULL)
{
hr = m_cpCandUIEx.CoCreateInstance(CLSID_TFCandidateUI);
}
if (SUCCEEDED(hr))
{
hr = pic->GetDocumentMgr(&cpdim);
}
if (SUCCEEDED(hr))
{
m_cpCandUIEx->SetClientId(GetId());
}
if (SUCCEEDED(hr))
{
hr = m_cpCandUIEx->SetCandidateList(pCandList);
}
if (SUCCEEDED(hr))
{
hr = m_cpCandUIEx->OpenCandidateUI(NULL, cpdim, ec, pRange);
}
return S_OK;
}
/****************************************************************************
* CCorrectionIMX::SetResult *
*---------------------------*
* Description:
*
* Returns: HRESULT
*
* Args:
* ITfContext *pic
* ITfRange *pRange
* CCandidateString *pCand
* TfCandidateResult imcr
*
**************************************************************** agarside ***/
HRESULT CCorrectionIMX::SetResult(ITfContext *pic, ITfRange *pRange, CCandidateString *pCand, TfCandidateResult imcr)
{
SPDBG_FUNC("CCorrectionIMX::SetResult");
BSTR bstr;
CCorrectionIMX *_this = (CCorrectionIMX *)pCand->_pv;
HRESULT hr = S_OK;
CEditSession *pes = NULL;
if (imcr == CAND_FINALIZED)
{
ULONG ulID = 0;
pCand->GetID(&ulID);
switch (ulID)
{
case 0: // Reverse capitalized choice.
{
pCand->GetString(&bstr);
pes = new CEditSession( EditSessionCallback );
if (pes)
{
pes->_state.u = ESCB_INJECTALTERNATETEXT;
pes->_state.pv = _this;
pes->_state.wParam = (WPARAM)bstr;
pes->_state.pRange = pRange;
pes->_state.pic = pic;
pic->RequestEditSession( _this->GetId(), pes, TF_ES_ASYNC | TF_ES_READWRITE, &hr);
pes->Release();
hr = S_OK;
// Do not return errors from this function or Cicero will disable the tip.
}
// Do not call SysFreeString - called inside edit session.
break;
}
// BUGBUG - Need to handle words starting with non-alphabetic characters better.
}
}
// close candidate UI if it's still there
if (imcr == CAND_FINALIZED || imcr == CAND_CANCELED)
{
if (_this->m_cpCandUIEx)
{
_this->m_cpCandUIEx->CloseCandidateUI();
}
}
SPDBG_REPORT_ON_FAIL(hr);
return hr;
}
/****************************************************************************
* CCorrectionIMX::SetOptionResult *
*---------------------------------*
* Description:
*
* Returns: HRESULT
*
* Args:
* ITfContext *pic
* ITfRange *pRange
* CCandidateString *pCand
* TfCandidateResult imcr
*
**************************************************************** agarside ***/
HRESULT CCorrectionIMX::SetOptionResult(ITfContext *pic, ITfRange *pRange, CCandidateString *pCand, TfCandidateResult imcr)
{
SPDBG_FUNC("CCorrectionIMX::SetOptionResult");
BSTR bstr;
CCorrectionIMX *_this = (CCorrectionIMX *)pCand->_pv;
HRESULT hr = S_OK;
CEditSession *pes = NULL;
if (imcr == CAND_FINALIZED)
{
ULONG ulID = 0;
pCand->GetID(&ulID);
switch (ulID)
{
case 0: // Add to dictinary...
{
if (SUCCEEDED(pCand->GetWord(&bstr)))
{
_this->AddWordToDictionary(bstr);
SysFreeString(bstr);
}
break;
}
#ifdef ENABLEDELETE
case 1: // Delete from dictinary...
{
if (SUCCEEDED(pCand->GetWord(&bstr)))
{
_this->RemoveWordFromDictionary(bstr);
SysFreeString(bstr);
}
break;
#endif
case 2: // Delete
{
bstr = SysAllocString(L"");
pes = new CEditSession( EditSessionCallback );
if (pes)
{
pes->_state.u = ESCB_INJECTALTERNATETEXT;
pes->_state.pv = _this;
pes->_state.wParam = (WPARAM)bstr;
pes->_state.pRange = pRange;
pes->_state.pic = pic;
pic->RequestEditSession( _this->GetId(), pes, TF_ES_ASYNC | TF_ES_READWRITE, &hr);
pes->Release();
hr = S_OK;
}
break;
}
}
}
// close candidate UI if it's still there
if (imcr == CAND_FINALIZED || imcr == CAND_CANCELED)
{
if (_this->m_cpCandUIEx)
{
_this->m_cpCandUIEx->CloseCandidateUI();
}
}
SPDBG_REPORT_ON_FAIL(hr);
return hr;
}
/****************************************************************************
* CCorrectionIMX::ICCallback *
*----------------------------*
* Description:
* Called by Cicero when changes happen to the DIM with focus.
* We need to listen to this so we can hide the widget on a focus change within an app.
* (e.g. two instances of Microsoft Word windows)
*
* Returns: HRESULT
* S_OK - Everything succeeded.
* Otherwise, appropriate error code.
*
* Args:
* UINT uCode
* Code specifying change to IC.
* ITfDicumentMgr *pdim
* ITfDicumentMgr *pdimPrevFocus
* void *pv
*
**************************************************************** agarside ***/
/* static */
HRESULT CCorrectionIMX::DIMCallback(UINT uCode, ITfDocumentMgr *pdim, ITfDocumentMgr *pdimPrevFocus, void *pv)
{
SPDBG_FUNC("CCorrectionIMX::DIMCallback");
HRESULT hr = S_OK;
CCorrectionIMX *_this = (CCorrectionIMX *)pv;
CComPtr<ITfSource> cpSource;
switch (uCode)
{
case TIM_CODE_SETFOCUS:
{
hr = _this->Show(WINDOW_HIDE);
break;
}
}
SPDBG_REPORT_ON_FAIL(hr);
return hr;
}
/****************************************************************************
* CCorrectionIMX::ICCallback *
*----------------------------*
* Description:
* Called by Cicero when changes happen to an input context.
* We need to listen to this so we can hook up to edit and layout changes.
*
* Returns: HRESULT
* S_OK - Everything succeeded.
* Otherwise, appropriate error code.
*
* Args:
* UINT uCode
* Code specifying change to IC.
* ITfContext *pic
* Interface for the affected IC.
* void *pv
* Pointer for change - specific data.
* We don't use this.
*
**************************************************************** agarside ***/
/* static */
HRESULT CCorrectionIMX::ICCallback(UINT uCode, ITfContext *pic, void *pv)
{
SPDBG_FUNC("CCorrectionIMX::ICCallback");
HRESULT hr = S_OK;
CCorrectionIMX *_this = (CCorrectionIMX *)pv;
CComPtr<ITfSource> cpSource;
CICPriv *priv = NULL;
switch (uCode)
{
case TIM_CODE_INITIC:
{
if ((priv = GetInputContextPriv(_this->GetId(), pic)) == NULL)
{
break;
}
_this->InitICPriv(_this->GetId(), priv, pic);
_this->IsCandidateObjectOpen(pic, &_this->m_fCandidateOpen);
if (_this->m_fCandidateOpen)
{
_this->Show(WINDOW_LARGECLOSE);
}
break;
}
case TIM_CODE_UNINITIC:
{
if ((priv = GetInputContextPriv(_this->GetId(), pic)) == NULL)
{
break;
}
_this->DeleteICPriv(priv, pic);
BOOL fOpen;
_this->IsCandidateObjectOpen(pic, &fOpen);
if (fOpen)
{
_this->m_fCandidateOpen = FALSE;
// If widget is in LARGECLOSE state, must close it here.
_this->Show(WINDOW_HIDE);
}
break;
}
}
SPDBG_REPORT_ON_FAIL(hr);
return hr;
}
/****************************************************************************
* CCorrectionIMX::InitICPriv *
*----------------------------*
* Description:
*
* Returns: HRESULT
*
* Args:
* TfClientId tid
* CICPriv *priv
* ITfContext *pic
*
**************************************************************** agarside ***/
HRESULT CCorrectionIMX::InitICPriv(TfClientId tid, CICPriv *priv, ITfContext *pic)
{
CComPtr<ITfSource> cpSource;
HRESULT hr = S_OK;
priv->_tid = tid;
priv->_pic = pic; // not AddRef'd, this struct is contained in life of the pic
hr = pic->QueryInterface(IID_ITfSource, (void **)&cpSource);
if (cpSource)
{
hr = cpSource->AdviseSink(IID_ITfTextEditSink, (ITfTextEditSink *)this, &priv->m_dwEditCookie);
if (SUCCEEDED(hr))
{
hr = cpSource->AdviseSink(IID_ITfTextLayoutSink, (ITfTextLayoutSink *)this, &priv->m_dwLayoutCookie);
}
}
SPDBG_REPORT_ON_FAIL(hr);
return hr;
}
/****************************************************************************
* CCorrectionIMX::DeleteICPriv *
*------------------------------*
* Description:
*
* Returns: HRESULT
*
* Args:
* CICPriv *picp
* ITfContext *pic
*
**************************************************************** agarside ***/
HRESULT CCorrectionIMX::DeleteICPriv(CICPriv *picp, ITfContext *pic)
{
CComPtr<ITfSource> cpSource;
HRESULT hr = S_OK;
if (!picp)
{
return E_FAIL;
}
hr = pic->QueryInterface(IID_ITfSource, (void **)&cpSource);
if (SUCCEEDED(hr))
{
cpSource->UnadviseSink(picp->m_dwEditCookie);
cpSource->UnadviseSink(picp->m_dwLayoutCookie);
}
// we MUST clear out the private data before cicero is free
// to release the ic
ClearCompartment(GetId(), pic, GUID_IC_PRIVATE, FALSE);
SPDBG_REPORT_ON_FAIL(hr);
return hr;
}
//+---------------------------------------------------------------------------
//
// CreateInstance
//
// This is our internal object creator. We only call this method
// when creating a wrapper for a specific tip, or when an app
// uses TF_CreateThreadMgr. Never from CoCreateInstance.
//----------------------------------------------------------------------------
/* static */
HRESULT
CCorrectionIMX::CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppvObj)
{
return _CreatorClass::CreateInstance(pUnkOuter, riid, ppvObj);
}
#endif // SUPPORT_INTERNAL_WIDGET