commit 7b83802bb264d5ee009bfd18d3283c35f4e87829 Author: fanlusky <1730976608@qq.com> Date: Thu Oct 12 23:53:17 2023 +0800 First commit. diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..c25c7b69 --- /dev/null +++ b/.clang-format @@ -0,0 +1,6 @@ +--- +BasedOnStyle: Microsoft +IndentWidth: 4 + +# 不要给头文件排序 +SortIncludes: false diff --git a/.clangd b/.clangd new file mode 100644 index 00000000..88c953e7 --- /dev/null +++ b/.clangd @@ -0,0 +1,2 @@ +CompileFlags: + Add: [-Wno-microsoft-goto, -Wno-unused-value] # 忽略某些警告 diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..3722ac63 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/ActiveLanguageProfileNotifySink.cpp b/ActiveLanguageProfileNotifySink.cpp new file mode 100644 index 00000000..97174bcf --- /dev/null +++ b/ActiveLanguageProfileNotifySink.cpp @@ -0,0 +1,118 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#include "Private.h" +#include "Globals.h" +#include "SampleIME.h" +#include "CompositionProcessorEngine.h" + +BOOL CSampleIME::VerifySampleIMECLSID(_In_ REFCLSID clsid) +{ + if (IsEqualCLSID(clsid, Global::SampleIMECLSID)) + { + return TRUE; + } + return FALSE; +} + +//+--------------------------------------------------------------------------- +// +// ITfActiveLanguageProfileNotifySink::OnActivated +// +// Sink called by the framework when changes activate language profile. +//---------------------------------------------------------------------------- + +STDAPI CSampleIME::OnActivated(_In_ REFCLSID clsid, _In_ REFGUID guidProfile, _In_ BOOL isActivated) +{ + guidProfile; + + if (FALSE == VerifySampleIMECLSID(clsid)) + { + return S_OK; + } + + if (isActivated) + { + _AddTextProcessorEngine(); + } + + if (nullptr == _pCompositionProcessorEngine) + { + return S_OK; + } + + if (isActivated) + { + _pCompositionProcessorEngine->ShowAllLanguageBarIcons(); + + _pCompositionProcessorEngine->ConversionModeCompartmentUpdated(_pThreadMgr); + } + else + { + _DeleteCandidateList(FALSE, nullptr); + + _pCompositionProcessorEngine->HideAllLanguageBarIcons(); + } + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// _InitActiveLanguageProfileNotifySink +// +// Advise a active language profile notify sink. +//---------------------------------------------------------------------------- + +BOOL CSampleIME::_InitActiveLanguageProfileNotifySink() +{ + ITfSource *pSource = nullptr; + BOOL ret = FALSE; + + if (_pThreadMgr->QueryInterface(IID_ITfSource, (void **)&pSource) != S_OK) + { + return ret; + } + + if (pSource->AdviseSink(IID_ITfActiveLanguageProfileNotifySink, (ITfActiveLanguageProfileNotifySink *)this, + &_activeLanguageProfileNotifySinkCookie) != S_OK) + { + _activeLanguageProfileNotifySinkCookie = TF_INVALID_COOKIE; + goto Exit; + } + + ret = TRUE; + +Exit: + pSource->Release(); + return ret; +} + +//+--------------------------------------------------------------------------- +// +// _UninitActiveLanguageProfileNotifySink +// +// Unadvise a active language profile notify sink. Assumes we have advised one already. +//---------------------------------------------------------------------------- + +void CSampleIME::_UninitActiveLanguageProfileNotifySink() +{ + ITfSource *pSource = nullptr; + + if (_activeLanguageProfileNotifySinkCookie == TF_INVALID_COOKIE) + { + return; // never Advised + } + + if (_pThreadMgr->QueryInterface(IID_ITfSource, (void **)&pSource) == S_OK) + { + pSource->UnadviseSink(_activeLanguageProfileNotifySinkCookie); + pSource->Release(); + } + + _activeLanguageProfileNotifySinkCookie = TF_INVALID_COOKIE; +} diff --git a/BaseDictionaryEngine.cpp b/BaseDictionaryEngine.cpp new file mode 100644 index 00000000..0b94732e --- /dev/null +++ b/BaseDictionaryEngine.cpp @@ -0,0 +1,118 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#include "Private.h" +#include "BaseDictionaryEngine.h" +#include "Globals.h" + +//+--------------------------------------------------------------------------- +// ctor +//---------------------------------------------------------------------------- + +CBaseDictionaryEngine::CBaseDictionaryEngine(LCID locale, _In_ CFile *pDictionaryFile) +{ + _locale = locale; + _pDictionaryFile = pDictionaryFile; +} + +//+--------------------------------------------------------------------------- +// dtor +//---------------------------------------------------------------------------- + +CBaseDictionaryEngine::~CBaseDictionaryEngine() +{ +} + +//+--------------------------------------------------------------------------- +// SortListItemByFindKeyCode +//---------------------------------------------------------------------------- + +VOID CBaseDictionaryEngine::SortListItemByFindKeyCode(_Inout_ CSampleImeArray *pItemList) +{ + MergeSortByFindKeyCode(pItemList, 0, pItemList->Count() - 1); +} + +//+--------------------------------------------------------------------------- +// MergeSortByFindKeyCode +// +// Mergesort the array of element in CCandidateListItem::_FindKeyCode +// +//---------------------------------------------------------------------------- + +VOID CBaseDictionaryEngine::MergeSortByFindKeyCode(_Inout_ CSampleImeArray *pItemList, int leftRange, int rightRange) +{ + int candidateCount = CalculateCandidateCount(leftRange, rightRange); + + if (candidateCount > 2) + { + int mid = leftRange + (candidateCount / 2); + + MergeSortByFindKeyCode(pItemList, leftRange, mid); + MergeSortByFindKeyCode(pItemList, mid, rightRange); + + CSampleImeArray ListItemTemp; + + int leftRangeTemp = 0; + int midTemp = 0; + for (leftRangeTemp = leftRange, midTemp = mid; leftRangeTemp != mid || midTemp != rightRange;) + { + CStringRange* psrgLeftTemp = nullptr; + CStringRange* psrgMidTemp = nullptr; + + psrgLeftTemp = &pItemList->GetAt(leftRangeTemp)->_FindKeyCode; + psrgMidTemp = &pItemList->GetAt(midTemp)->_FindKeyCode; + + CCandidateListItem* pLI = nullptr; + pLI = ListItemTemp.Append(); + if (pLI) + { + if (leftRangeTemp == mid) + { + *pLI = *pItemList->GetAt(midTemp++); + } + else if (midTemp == rightRange || CStringRange::Compare(_locale, psrgLeftTemp, psrgMidTemp) != CSTR_GREATER_THAN) + { + *pLI = *pItemList->GetAt(leftRangeTemp++); + } + else + { + *pLI = *pItemList->GetAt(midTemp++); + } + } + } + + leftRangeTemp = leftRange; + for (UINT count = 0; count < ListItemTemp.Count(); count++) + { + *pItemList->GetAt(leftRangeTemp++) = *ListItemTemp.GetAt(count); + } + } + else if (candidateCount == 2) + { + CStringRange *psrgLeft = nullptr; + CStringRange *psrgLeftNext = nullptr; + + psrgLeft = &pItemList->GetAt(leftRange )->_FindKeyCode; + psrgLeftNext = &pItemList->GetAt(leftRange+1)->_FindKeyCode; + + if (CStringRange::Compare(_locale, psrgLeft, psrgLeftNext) == CSTR_GREATER_THAN) + { + CCandidateListItem ListItem; + ListItem = *pItemList->GetAt(leftRange); + *pItemList->GetAt(leftRange ) = *pItemList->GetAt(leftRange+1); + *pItemList->GetAt(leftRange+1) = ListItem; + } + } +} + +int CBaseDictionaryEngine::CalculateCandidateCount(int leftRange, int rightRange) +{ + assert(leftRange >= 0); + assert(rightRange >= 0); + + return (rightRange - leftRange + 1); +} \ No newline at end of file diff --git a/BaseDictionaryEngine.h b/BaseDictionaryEngine.h new file mode 100644 index 00000000..b63d499a --- /dev/null +++ b/BaseDictionaryEngine.h @@ -0,0 +1,41 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + + +#pragma once + +#include "File.h" +#include "SampleIMEBaseStructure.h" + +class CBaseDictionaryEngine +{ +public: + CBaseDictionaryEngine(LCID locale, _In_ CFile *pDictionaryFile); + virtual ~CBaseDictionaryEngine(); + + virtual VOID CollectWord(_In_ CStringRange *psrgKeyCode, _Out_ CSampleImeArray *pasrgWordString) + { + psrgKeyCode; + pasrgWordString = nullptr; + } + + virtual VOID CollectWord(_In_ CStringRange *psrgKeyCode, _Out_ CSampleImeArray *pItemList) + { + psrgKeyCode; + pItemList = nullptr; + } + + virtual VOID SortListItemByFindKeyCode(_Inout_ CSampleImeArray *pItemList); + +protected: + CFile* _pDictionaryFile; + LCID _locale; + +private: + VOID MergeSortByFindKeyCode(_Inout_ CSampleImeArray *pItemList, int leftRange, int rightRange); + int CalculateCandidateCount(int leftRange, int rightRange); +}; diff --git a/BaseWindow.cpp b/BaseWindow.cpp new file mode 100644 index 00000000..4e7aa193 --- /dev/null +++ b/BaseWindow.cpp @@ -0,0 +1,662 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#include "Private.h" +#include "Globals.h" +#include "BaseWindow.h" + +#define idTimer_UIObject 39772 + +//+--------------------------------------------------------------------------- +// +// CBaseWindow::ctor +// +//---------------------------------------------------------------------------- + +CBaseWindow::CBaseWindow() +{ + _wndHandle = nullptr; + _pParentWnd = nullptr; + _pUIWnd = nullptr; + + _pTimerUIObj = nullptr; + _pUIObjCapture = nullptr; + + _enableVirtualWnd = TRUE; + _visibleVirtualWnd = TRUE; + _RectOfVirtualWnd.left = 0; + _RectOfVirtualWnd.top = 0; + _RectOfVirtualWnd.right = 0; + _RectOfVirtualWnd.bottom = 0; +} + +//+--------------------------------------------------------------------------- +// +// CBaseWindow::dtor +// +//---------------------------------------------------------------------------- + +CBaseWindow::~CBaseWindow() +{ + _SetThis(_wndHandle, nullptr); +} + +//+--------------------------------------------------------------------------- +// +// CBaseWindow::_InitWindowClass +// +//---------------------------------------------------------------------------- + +/* static */ +BOOL CBaseWindow::_InitWindowClass(_In_ LPCWSTR lpwszClassName, _Out_ ATOM *patom) +{ + WNDCLASS wc; + + wc.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_IME; + wc.lpfnWndProc = CBaseWindow::_WindowProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = Global::dllInstanceHandle; + wc.hIcon = nullptr; + wc.hCursor = LoadCursor(nullptr, IDC_ARROW); + wc.hbrBackground = (HBRUSH)GetStockObject(LTGRAY_BRUSH); + wc.lpszMenuName = nullptr; + wc.lpszClassName = lpwszClassName; + + *patom = RegisterClass(&wc); + + return (*patom != 0); +} + +//+--------------------------------------------------------------------------- +// +// CBaseWindow::_UninitClass +// +//---------------------------------------------------------------------------- + +/* static */ +void CBaseWindow::_UninitWindowClass(ATOM atom) +{ + if (atom != 0) + { + UnregisterClass((LPCTSTR)atom, Global::dllInstanceHandle); + } +} + +//+--------------------------------------------------------------------------- +// +// CBaseWindow::_Create +// +//---------------------------------------------------------------------------- + +BOOL CBaseWindow::_Create(ATOM atom, DWORD dwExStyle, DWORD dwStyle, _In_opt_ CBaseWindow *pParentWnd, int wndWidth, int wndHeight, _In_opt_ HWND parentWndHandle) +{ + _pParentWnd = pParentWnd; + + if (atom != 0) + { + // create real window + + _wndHandle = CreateWindowEx(dwExStyle, + (LPCTSTR)atom, + NULL, + dwStyle, + 0, 0, + wndWidth, wndHeight, + _pParentWnd ? _pParentWnd->_GetWnd() : parentWndHandle, // parentWndHandle + NULL, + Global::dllInstanceHandle, + this); // lpParam + + if (!_wndHandle) + { + return FALSE; + } + } + + return TRUE; +} + +//+--------------------------------------------------------------------------- +// +// CBaseWindow::_Destroy +// +//---------------------------------------------------------------------------- + +void CBaseWindow::_Destroy() +{ + if (_wndHandle != nullptr) + { + DestroyWindow(_wndHandle); + _wndHandle = nullptr; + } +} + +//+--------------------------------------------------------------------------- +// +// CBaseWindow::_Move +// +//---------------------------------------------------------------------------- + +void CBaseWindow::_Move(int x, int y) +{ + if (_wndHandle != nullptr) + { + SetWindowPos(_wndHandle, 0, x, y, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER); + } + else + { + int dx = x - _RectOfVirtualWnd.left; + int dy = y - _RectOfVirtualWnd.top; + + _RectOfVirtualWnd.left += dx; + _RectOfVirtualWnd.top += dy; + _RectOfVirtualWnd.right += dx; + _RectOfVirtualWnd.bottom += dy; + } +} + +//+--------------------------------------------------------------------------- +// +// CBaseWindow::_Resize +// +//---------------------------------------------------------------------------- + +void CBaseWindow::_Resize(int x, int y, int cx, int cy) +{ + if (_wndHandle != nullptr) + { + MoveWindow(_wndHandle, x, y, cx, cy, TRUE); + } + else + { + _RectOfVirtualWnd.left = x; + _RectOfVirtualWnd.top = y; + _RectOfVirtualWnd.right = x + cx; + _RectOfVirtualWnd.bottom = y + cy; + } +} + +//+--------------------------------------------------------------------------- +// +// CBaseWindow::_Show +// +//---------------------------------------------------------------------------- + +void CBaseWindow::_Show(BOOL isShowWnd) +{ + if (_wndHandle != nullptr) + { + if (isShowWnd) + { + ShowWindow(_wndHandle, SW_SHOWNA); + } + else + { + ShowWindow(_wndHandle, SW_HIDE); + } + } + else + { + _visibleVirtualWnd = isShowWnd; + } +} + +//+--------------------------------------------------------------------------- +// +// CBaseWindow::_IsWindowVisible +// +//---------------------------------------------------------------------------- + +BOOL CBaseWindow::_IsWindowVisible() +{ + if (_wndHandle != nullptr) + { + return IsWindowVisible(_wndHandle); + } + else + { + return _visibleVirtualWnd; + } +} + +//+--------------------------------------------------------------------------- +// +// CBaseWindow::_Enable +// +//---------------------------------------------------------------------------- + +void CBaseWindow::_Enable(BOOL enableWindowReceiveInput) +{ + if (_wndHandle != nullptr) + { + EnableWindow(_wndHandle, enableWindowReceiveInput); + } + else + { + _enableVirtualWnd = enableWindowReceiveInput; + } +} + +//+--------------------------------------------------------------------------- +// +// CBaseWindow::_IsEnabled +// +//---------------------------------------------------------------------------- + +BOOL CBaseWindow::_IsEnabled() +{ + if (_wndHandle != nullptr) + { + return IsWindowEnabled(_wndHandle); + } + else + { + return _enableVirtualWnd; + } +} + +//+--------------------------------------------------------------------------- +// +// CBaseWindow::_InvalidateRect +// +//---------------------------------------------------------------------------- + +void CBaseWindow::_InvalidateRect() +{ + if (_wndHandle != nullptr) + { + InvalidateRect(_wndHandle, NULL, TRUE); + } + else + { + CBaseWindow *pobj = _pParentWnd; + while (pobj != nullptr) + { + if (pobj->_wndHandle) + { + InvalidateRect(pobj->_wndHandle, &_RectOfVirtualWnd, TRUE); + break; + } + pobj = pobj->_pParentWnd; + } + } +} + +//+--------------------------------------------------------------------------- +// +// CBaseWindow::_GetWindowRect +// +//---------------------------------------------------------------------------- + +BOOL CBaseWindow::_GetWindowRect(_Inout_ LPRECT lpRect) +{ + if (_wndHandle != nullptr) + { + return GetWindowRect(_wndHandle, lpRect); + } + else + { + *lpRect = _RectOfVirtualWnd; + return TRUE; + } +} + +//+--------------------------------------------------------------------------- +// +// CBaseWindow::_GetClientRect +// +//---------------------------------------------------------------------------- + +BOOL CBaseWindow::_GetClientRect(_Inout_ LPRECT lpRect) +{ + if (_wndHandle != nullptr) + { + return GetClientRect(_wndHandle, lpRect); + } + else + { + *lpRect = _RectOfVirtualWnd; + return TRUE; + } +} + +//+--------------------------------------------------------------------------- +// +// _GetWindowExtent +// +//---------------------------------------------------------------------------- + +HRESULT CBaseWindow::_GetWindowExtent(_In_ const RECT *prcTextExtent, _In_opt_ RECT *prcCandidateExtent, _Inout_ POINT *pptCandidate) +{ + RECT rcWorkArea = {0, 0, 0, 0}; + + // Get work area + GetWorkAreaFromPoint(*(LPPOINT)&prcTextExtent->left, &rcWorkArea); + + // Calc candidate window extent + if (prcCandidateExtent) + { + CalcFitPointAroundTextExtent(prcTextExtent, &rcWorkArea, prcCandidateExtent, pptCandidate); + } + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// CalcFitPointAroundTextExtent +// +//---------------------------------------------------------------------------- + +void CBaseWindow::CalcFitPointAroundTextExtent(_In_ const RECT *prcTextExtent, _In_ const RECT *prcWorkArea, _In_ const RECT *prcWindow, _Out_ POINT *ppt) +{ + RECT rcTargetWindow[2]; + DWORD dwFlags[2]; + + // set rcTargetWindow[0] which rectangle attached on bottom side of text extent + rcTargetWindow[0] = *prcWindow; + OffsetRect(&rcTargetWindow[0], prcTextExtent->left, prcTextExtent->bottom); + + // set rcTargetWindow[1] which rectangle attached on top side of text extent + rcTargetWindow[1] = *prcWindow; + OffsetRect(&rcTargetWindow[1], prcTextExtent->left, prcTextExtent->top - (prcWindow->bottom - prcWindow->top)); + + // + // check target rectangle fit in workarea + // + for (DWORD i = 0; i < ARRAYSIZE(rcTargetWindow); i++) + { + dwFlags[i] = RectInRect(prcWorkArea, &rcTargetWindow[i]); + + // generally, rcTargetWindow[0] or rcTargetWindow[1] have neither of these flags + // but in the case where window is just complete way off the screen e.g. user + // starts input, then drag window way off screen, both of these will be true + if ((dwFlags[i] & RECT_OVER_TOP) || (dwFlags[i] & RECT_OVER_BOTTOM)) + { + continue; + } + + if (dwFlags[i] == RECT_INSIDE) + { + *ppt = *(POINT*)&rcTargetWindow[i].left; + return; + } + else if ((dwFlags[i] & RECT_OVER_LEFT) != 0) + { + ppt->x = 0; + ppt->y = rcTargetWindow[i].top; + return; + } + else if ((dwFlags[i] & RECT_OVER_RIGHT) != 0) + { + ppt->x = prcWorkArea->right - (prcWindow->right - prcWindow->left); + ppt->y = rcTargetWindow[i].top; + return; + } + } + + // if both rcTargetWindow have RECT_OVER_TOP or RECT_OVER_BOTTOM, + // then "dock" the window to top or bottom of working area. + if ((dwFlags[0] & RECT_OVER_TOP) != 0) + { + ppt->y = 0; + } + else + { + ppt->y = prcWorkArea->bottom - (prcWindow->bottom - prcWindow->top); + } + + // dock to left/right edge if RECT_OVER_LEFT or RECT_OVER_RIGHT. + // else just stay where we are + if ((dwFlags[0] & RECT_OVER_LEFT) != 0) + { + ppt->x = 0; + } + else if ((dwFlags[0] & RECT_OVER_RIGHT) != 0) + { + ppt->x = prcWorkArea->right - (prcWindow->right - prcWindow->left); + } + else + { + ppt->x = prcTextExtent->left; + } + + return; +} + +//+--------------------------------------------------------------------------- +// +// RectInRect +// +//---------------------------------------------------------------------------- + +DWORD CBaseWindow::RectInRect(_In_ const RECT *prcLimit, _In_ const RECT *prcTarget) +{ + DWORD dwFlags = 0; + // Check if prcTarget is entirely inside prcLimit + if (prcLimit->left <= prcTarget->left && prcTarget->right <= prcLimit->right && + prcLimit->top <= prcTarget->top && prcTarget->bottom <= prcLimit->bottom) + { + return RECT_INSIDE; + } + + // Check horizontal range. Target can be + // - wider than the limit (assert here since it should never happen) + // - entirely outside the limit (RECT_OVERLEFT or RECT_OVERRIGHT) + // - partially inside the limit (RECT_OVERLEFT or RECT_OVERRIGHT) + if (prcTarget->left < prcLimit->left && prcTarget->right > prcLimit->right) + { + assert(FALSE); + dwFlags |= RECT_TOO_WIDE; + } + else if (prcTarget->left < prcLimit->left) + { + dwFlags |= RECT_OVER_LEFT; + } + else if (prcTarget->right > prcLimit->right) + { + dwFlags |= RECT_OVER_RIGHT; + } + + // Check vertical range. Target can be + // - taller than the limit (assert here since it should never happen) + // - entirely outside the limit (RECT_OVERTOP or RECT_OVERBOTTOM) + // - partially inside the limit (RECT_OVERTOP or RECT_OVERBOTTOM) + if (prcTarget->top < prcLimit->top && prcTarget->bottom > prcLimit->bottom) + { + assert(FALSE); + dwFlags |= RECT_TOO_TALL; + } + else if (prcTarget->top < prcLimit->top) + { + dwFlags |= RECT_OVER_TOP; + } + else if (prcTarget->bottom > prcLimit->bottom) + { + dwFlags |= RECT_OVER_BOTTOM; + } + + return dwFlags; +} + +//+--------------------------------------------------------------------------- +// +// CBaseWindow::_NotifyCommand +// +//---------------------------------------------------------------------------- + +LRESULT CBaseWindow::_NotifyCommand(UINT uMsg, DWORD dwSB, int nPos) +{ + CBaseWindow* pUIWnd = _GetUIWnd(); + if (pUIWnd && pUIWnd->_GetWnd()) + { + WPARAM wParam = MAKEWPARAM(dwSB, nPos); + if (pUIWnd->_GetWnd()) + { + return SendMessage(pUIWnd->_GetWnd(), uMsg, wParam, (LPARAM)0); + } + } + return 0; +} + +//+--------------------------------------------------------------------------- +// +// _SetCaptureObject +// +//---------------------------------------------------------------------------- + +void CBaseWindow::_SetCaptureObject(_In_opt_ CBaseWindow *pUIObj) +{ + CBaseWindow* pUIWnd = _GetTopmostUIWnd(); + if (nullptr == pUIWnd) + { + return; + } + + pUIWnd->_pUIObjCapture = pUIObj; + if (pUIObj != nullptr) + { + SetCapture(pUIWnd->_GetWnd()); + } + else + { + ReleaseCapture(); + } +} + +//+--------------------------------------------------------------------------- +// +// _SetTimerObject +// +//---------------------------------------------------------------------------- + +void CBaseWindow::_SetTimerObject(_In_opt_ CBaseWindow *pUIObj, UINT uElapse) +{ + CBaseWindow* pUIWnd = _GetTopmostUIWnd(); + if (nullptr == pUIWnd) + { + return; + } + + pUIWnd->_pTimerUIObj = pUIObj; + if (pUIObj != nullptr) + { + SetTimer(pUIWnd->_GetWnd(), idTimer_UIObject, uElapse, NULL); + } + else + { + KillTimer(pUIWnd->_GetWnd(), idTimer_UIObject); + } +} + +//+--------------------------------------------------------------------------- +// +// CBaseWindow::_WindowProc +// +// CBaseWindow window proc. +//---------------------------------------------------------------------------- + +/* static */ +LRESULT CALLBACK CBaseWindow::_WindowProc(_In_ HWND wndHandle, UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam) +{ + if (uMsg == WM_CREATE) + { + _SetThis(wndHandle, ((CREATESTRUCT*)lParam)->lpCreateParams); + } + + CBaseWindow* pv = _GetThis(wndHandle); + if (!pv) + { + return DefWindowProc(wndHandle, uMsg, wParam, lParam); + } + + if (uMsg == WM_TIMER) + { + switch (wParam) + { + case idTimer_UIObject: + if (pv->_GetTimerObject() != nullptr) + { + pv->_GetTimerObject()->_OnTimer(); + } + break; + } + return 0; + } + else + { + return pv->_WindowProcCallback(wndHandle, uMsg, wParam, lParam); + } +} + +//+--------------------------------------------------------------------------- +// +// CBaseWindow::GetWorkAreaFromPoint +// +//---------------------------------------------------------------------------- + +void CBaseWindow::GetWorkAreaFromPoint(_In_ const POINT& ptPoint, _Out_ LPRECT lprcWorkArea) +{ + if (lprcWorkArea == nullptr) + { + return; + } + + lprcWorkArea->left = 0; + lprcWorkArea->top = 0; + lprcWorkArea->right = 0; + lprcWorkArea->bottom = 0; + + HMONITOR hMonitor = MonitorFromPoint(ptPoint, MONITOR_DEFAULTTONEAREST); + if (hMonitor) + { + MONITORINFO MonitorInfo = {0}; + + MonitorInfo.cbSize = sizeof(MONITORINFO); + if (GetMonitorInfo(hMonitor, &MonitorInfo)) + { + *lprcWorkArea = MonitorInfo.rcWork; + return; + } + } + + SystemParametersInfo(SPI_GETWORKAREA, 0, lprcWorkArea, 0); + return; +} + +BOOL CBaseWindow::_IsTimer() +{ + CBaseWindow* pobj = _GetTopmostUIWnd(); + if (pobj != nullptr) + { + return (pobj->_pTimerUIObj != nullptr); + } + return FALSE; +} + +BOOL CBaseWindow::_IsCapture() +{ + CBaseWindow* pobj = _GetTopmostUIWnd(); + if (pobj != nullptr) + { + return (pobj->_pUIObjCapture != nullptr); + } + return FALSE; +} + +CBaseWindow* CBaseWindow::_GetTopmostUIWnd() +{ + CBaseWindow* pobj = this; + + while (pobj->_pParentWnd != nullptr) + { + pobj = pobj->_pParentWnd; + } + + return pobj->_pUIWnd; +} \ No newline at end of file diff --git a/BaseWindow.h b/BaseWindow.h new file mode 100644 index 00000000..a93719d7 --- /dev/null +++ b/BaseWindow.h @@ -0,0 +1,160 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + + +#pragma once + +#define RECT_INSIDE (0x0001) +#define RECT_OVER_LEFT (0x0002) +#define RECT_OVER_TOP (0x0004) +#define RECT_OVER_RIGHT (0x0008) +#define RECT_OVER_BOTTOM (0x0010) +#define RECT_TOO_WIDE (0x0020) +#define RECT_TOO_TALL (0x0040) +#define RECT_ERROR (0x0080) + +class CStringRange; + +//+--------------------------------------------------------------------------- +// +// CBaseWindow +// +//---------------------------------------------------------------------------- + +class CBaseWindow +{ +public: + CBaseWindow(); + virtual ~CBaseWindow(); + + static BOOL _InitWindowClass(_In_ LPCWSTR lpwszClassName, _Out_ ATOM *patom); + static void _UninitWindowClass(ATOM atom); + + virtual BOOL _Create(ATOM atom, DWORD dwExStyle, DWORD dwStyle, _In_opt_ CBaseWindow *pParentWnd = nullptr, int wndWidth = 0, int wndHeight = 0, _In_opt_ HWND parentWndHandle = nullptr); + virtual void _Destroy(); + + virtual void _Move(int x, int y); + virtual void _Resize(int x, int y, int cx, int cy); + virtual void _Show(BOOL isShowWnd); + virtual BOOL _IsWindowVisible(); + virtual void _Enable(BOOL enableWindowReceiveInput); + virtual BOOL _IsEnabled(); + virtual void _InvalidateRect(); + virtual BOOL _GetWindowRect(_Inout_ LPRECT lpRect); + virtual BOOL _GetClientRect(_Inout_ LPRECT lpRect); + + virtual LRESULT CALLBACK _WindowProcCallback(_In_ HWND wndHandle, UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam) = 0; + virtual void _OnPaint(_In_ HDC dcHandle, _In_ PAINTSTRUCT *pps) { dcHandle; pps; } + virtual void _OnLButtonDown(POINT pt) { pt; } + virtual void _OnLButtonUp(POINT pt) { pt; } + virtual void _OnMouseMove(POINT pt) { pt; } + virtual void _OnTimer() { } + + CBaseWindow* _GetTopmostUIWnd(); + + HRESULT _GetWindowExtent(_In_ const RECT *prcTextExtent, _In_opt_ RECT *prcCandidateExtent, _Inout_ POINT *pptCandidate); + + HWND _GetWnd() + { + return _wndHandle; + } + + CBaseWindow *_GetParent() + { + return _pParentWnd; + } + + void _SetUIWnd(_In_ CBaseWindow *pUIWnd) + { + _pUIWnd = pUIWnd; + } + + CBaseWindow* _GetUIWnd() + { + return _pUIWnd; + } + + CBaseWindow *_GetCaptureObject() + { + return _pUIObjCapture; + } + + CBaseWindow *_GetTimerObject() + { + return _pTimerUIObj; + } + + UINT _GetScrollDelay() + { + return (GetDoubleClickTime() * 4 / 5); + } + + UINT _GetScrollSpeed() + { + return (_GetScrollDelay() / 8); + } + +protected: + LRESULT _NotifyCommand(UINT uMsg, DWORD dwSB, int nPos); + + void _StartCapture() + { + _SetCaptureObject(this); + } + + void _EndCapture() + { + _SetCaptureObject(nullptr); + } + + BOOL _IsCapture(); + + void _StartTimer(UINT uElapse) + { + _SetTimerObject(this, uElapse); + } + + void _EndTimer() + { + _SetTimerObject(nullptr); + } + + BOOL _IsTimer(); + +private: + void _SetCaptureObject(_In_opt_ CBaseWindow *pUIObj); + void _SetTimerObject(_In_opt_ CBaseWindow *pUIObj, UINT uElapse = 0); + + void GetWorkAreaFromPoint(_In_ const POINT& ptPoint, _Out_ LPRECT lprcWorkArea); + void CalcFitPointAroundTextExtent(_In_ const RECT *prcTextExtent, _In_ const RECT *prcWorkArea, _In_ const RECT *prcWindow, _Out_ POINT *ppt); + DWORD RectInRect(_In_ const RECT *prcLimit, _In_ const RECT *prcTarget); + + static LRESULT CALLBACK _WindowProc(_In_ HWND wndHandle, UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam); + + static CBaseWindow *_GetThis(_In_ HWND wndHandle) + { + return (CBaseWindow *)GetWindowLongPtr(wndHandle, GWLP_USERDATA); + } + + static void _SetThis(_In_ HWND wndHandle, _In_opt_ void *lpv) + { + SetWindowLongPtr(wndHandle, GWLP_USERDATA, (LONG_PTR)lpv); + } + +private: + HWND _wndHandle; + + CBaseWindow *_pParentWnd; + CBaseWindow *_pUIWnd; + + CBaseWindow *_pTimerUIObj; + CBaseWindow *_pUIObjCapture; + + BOOL _enableVirtualWnd; + BOOL _visibleVirtualWnd; + RECT _RectOfVirtualWnd; +}; diff --git a/ButtonWindow.cpp b/ButtonWindow.cpp new file mode 100644 index 00000000..0f2aef1b --- /dev/null +++ b/ButtonWindow.cpp @@ -0,0 +1,90 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#include "Private.h" +#include "BaseWindow.h" +#include "ButtonWindow.h" + +//+--------------------------------------------------------------------------- +// +// CButtonWindow +// +//---------------------------------------------------------------------------- + +CButtonWindow::CButtonWindow() +{ + typeOfControl = 0; +} + +//+--------------------------------------------------------------------------- +// +// ~CButtonWindow +// +//---------------------------------------------------------------------------- + +CButtonWindow::~CButtonWindow() +{ +} + +//+--------------------------------------------------------------------------- +// +// _OnPaint +// +//---------------------------------------------------------------------------- + +void CButtonWindow::_OnPaint(_In_ HDC dcHandle, _In_ PAINTSTRUCT *pps) +{ + dcHandle; + pps; +} + +//+--------------------------------------------------------------------------- +// +// _OnLButtonDown +// +//---------------------------------------------------------------------------- + +void CButtonWindow::_OnLButtonDown(POINT pt) +{ + pt; + + typeOfControl = DFCS_PUSHED; + _StartCapture(); +} + +//+--------------------------------------------------------------------------- +// +// _WindowProcCallback +// +//---------------------------------------------------------------------------- +LRESULT CALLBACK CButtonWindow::_WindowProcCallback(_In_ HWND wndHandle, UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam) +{ + wndHandle; + uMsg; + wParam; + lParam; + + return 0; +} + +//+--------------------------------------------------------------------------- +// +// _OnLButtonUp +// +//---------------------------------------------------------------------------- + +void CButtonWindow::_OnLButtonUp(POINT pt) +{ + pt; + + if (_IsCapture()) + { + _EndCapture(); + } + + typeOfControl = 0; +} diff --git a/ButtonWindow.h b/ButtonWindow.h new file mode 100644 index 00000000..e2fa300d --- /dev/null +++ b/ButtonWindow.h @@ -0,0 +1,27 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + + +#pragma once + +#include "BaseWindow.h" + +class CButtonWindow : public CBaseWindow +{ +public: + CButtonWindow(); + virtual ~CButtonWindow(); + + LRESULT CALLBACK _WindowProcCallback(_In_ HWND wndHandle, UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam); + + virtual void _OnPaint(_In_ HDC dcHandle, _In_ PAINTSTRUCT *pps); + virtual void _OnLButtonDown(POINT pt); + virtual void _OnLButtonUp(POINT pt); + +protected: + UINT typeOfControl; // DFCS_PUSHED, DFCS_INACTIVE or others for DrawFrameControl +}; diff --git a/CMakeDraft.txt b/CMakeDraft.txt new file mode 100644 index 00000000..a2c6d423 --- /dev/null +++ b/CMakeDraft.txt @@ -0,0 +1,85 @@ +"./ActiveLanguageProfileNotifySink.cpp" +"./BaseDictionaryEngine.cpp" +"./BaseDictionaryEngine.h" +"./BaseWindow.cpp" +"./BaseWindow.h" +"./ButtonWindow.cpp" +"./ButtonWindow.h" +"./CandidateListUIPresenter.cpp" +"./CandidateListUIPresenter.h" +"./CandidateWindow.cpp" +"./CandidateWindow.h" +"./Compartment.cpp" +"./Compartment.h" +"./Composition.cpp" +"./CompositionProcessorEngine.cpp" +"./CompositionProcessorEngine.h" +"./Define.h" +"./DictionaryParser.cpp" +"./DictionaryParser.h" +"./DictionarySearch.cpp" +"./DictionarySearch.h" +"./DisplayAttribute.cpp" +"./DisplayAttributeInfo.cpp" +"./DisplayAttributeInfo.h" +"./DisplayAttributeProvider.cpp" +"./DisplayString.h" +"./DllMain.cpp" +"./EditSession.cpp" +"./EditSession.h" +"./EndComposition.cpp" +"./EnumDisplayAttributeInfo.cpp" +"./EnumDisplayAttributeInfo.h" +"./EnumTfCandidates.cpp" +"./EnumTfCandidates.h" +"./File.cpp" +"./File.h" +"./FileMapping.cpp" +"./FileMapping.h" +"./FunctionProviderSink.cpp" +"./GetTextExtentEditSession.cpp" +"./GetTextExtentEditSession.h" +"./Globals.cpp" +"./Globals.h" +"./KeyEventSink.cpp" +"./KeyHandler.cpp" +"./KeyHandlerEditSession.cpp" +"./KeyHandlerEditSession.h" +"./KeyStateCategory.cpp" +"./KeyStateCategory.h" +"./LanguageBar.cpp" +"./LanguageBar.h" +"./Private.h" +"./Register.cpp" +"./RegKey.cpp" +"./RegKey.h" +"./resource.h" +"./SampleIME.cpp" +"./SampleIME.def" +"./SampleIME.h" +"./SampleIME.rc" +"./SampleIMEBaseStructure.cpp" +"./SampleIMEBaseStructure.h" +"./SampleIMEStructureArray.h" +"./ScrollBarWindow.cpp" +"./ScrollBarWindow.h" +"./SearchCandidateProvider.cpp" +"./SearchCandidateProvider.h" +"./Server.cpp" +"./ShadowWindow.cpp" +"./ShadowWindow.h" +"./StartComposition.cpp" +"./stdafx.h" +"./TableDictionaryEngine.cpp" +"./TableDictionaryEngine.h" +"./TextEditSink.cpp" +"./TfInputProcessorProfile.cpp" +"./TfInputProcessorProfile.h" +"./TfTextLayoutSink.cpp" +"./TfTextLayoutSink.h" +"./ThreadFocusSink.cpp" +"./ThreadMgrEventSink.cpp" +"./TipCandidateList.cpp" +"./TipCandidateList.h" +"./TipCandidateString.cpp" +"./TipCandidateString.h" diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..6a67d7d9 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,98 @@ +cmake_minimum_required(VERSION 3.25.0) + +project(fanytsfime) + +# http://www.utf8everywhere.org/ +add_definitions( + /D_UNICODE=1 + /DUNICODE=1 +) + +add_library(SampleIME SHARED + "./ActiveLanguageProfileNotifySink.cpp" + "./BaseDictionaryEngine.cpp" + "./BaseDictionaryEngine.h" + "./BaseWindow.cpp" + "./BaseWindow.h" + "./ButtonWindow.cpp" + "./ButtonWindow.h" + "./CandidateListUIPresenter.cpp" + "./CandidateListUIPresenter.h" + "./CandidateWindow.cpp" + "./CandidateWindow.h" + "./Compartment.cpp" + "./Compartment.h" + "./Composition.cpp" + "./CompositionProcessorEngine.cpp" + "./CompositionProcessorEngine.h" + "./Define.h" + "./DictionaryParser.cpp" + "./DictionaryParser.h" + "./DictionarySearch.cpp" + "./DictionarySearch.h" + "./DisplayAttribute.cpp" + "./DisplayAttributeInfo.cpp" + "./DisplayAttributeInfo.h" + "./DisplayAttributeProvider.cpp" + "./DisplayString.h" + "./DllMain.cpp" + "./EditSession.cpp" + "./EditSession.h" + "./EndComposition.cpp" + "./EnumDisplayAttributeInfo.cpp" + "./EnumDisplayAttributeInfo.h" + "./EnumTfCandidates.cpp" + "./EnumTfCandidates.h" + "./File.cpp" + "./File.h" + "./FileMapping.cpp" + "./FileMapping.h" + "./FunctionProviderSink.cpp" + "./GetTextExtentEditSession.cpp" + "./GetTextExtentEditSession.h" + "./Globals.cpp" + "./Globals.h" + "./KeyEventSink.cpp" + "./KeyHandler.cpp" + "./KeyHandlerEditSession.cpp" + "./KeyHandlerEditSession.h" + "./KeyStateCategory.cpp" + "./KeyStateCategory.h" + "./LanguageBar.cpp" + "./LanguageBar.h" + "./Private.h" + "./Register.cpp" + "./RegKey.cpp" + "./RegKey.h" + "./resource.h" + "./SampleIME.cpp" + "./SampleIME.def" + "./SampleIME.h" + "./SampleIME.rc" + "./SampleIMEBaseStructure.cpp" + "./SampleIMEBaseStructure.h" + "./SampleIMEStructureArray.h" + "./ScrollBarWindow.cpp" + "./ScrollBarWindow.h" + "./SearchCandidateProvider.cpp" + "./SearchCandidateProvider.h" + "./Server.cpp" + "./ShadowWindow.cpp" + "./ShadowWindow.h" + "./StartComposition.cpp" + "./stdafx.h" + "./TableDictionaryEngine.cpp" + "./TableDictionaryEngine.h" + "./TextEditSink.cpp" + "./TfInputProcessorProfile.cpp" + "./TfInputProcessorProfile.h" + "./TfTextLayoutSink.cpp" + "./TfTextLayoutSink.h" + "./ThreadFocusSink.cpp" + "./ThreadMgrEventSink.cpp" + "./TipCandidateList.cpp" + "./TipCandidateList.h" + "./TipCandidateString.cpp" + "./TipCandidateString.h" +) + diff --git a/CandidateListUIPresenter.cpp b/CandidateListUIPresenter.cpp new file mode 100644 index 00000000..66bfb6c7 --- /dev/null +++ b/CandidateListUIPresenter.cpp @@ -0,0 +1,1305 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#include "Private.h" +#include "SampleIME.h" +#include "CandidateWindow.h" +#include "CandidateListUIPresenter.h" +#include "CompositionProcessorEngine.h" +#include "SampleIMEBaseStructure.h" + +////////////////////////////////////////////////////////////////////// +// +// CSampleIME candidate key handler methods +// +////////////////////////////////////////////////////////////////////// + +const int MOVEUP_ONE = -1; +const int MOVEDOWN_ONE = 1; +const int MOVETO_TOP = 0; +const int MOVETO_BOTTOM = -1; +//+--------------------------------------------------------------------------- +// +// _HandleCandidateFinalize +// +//---------------------------------------------------------------------------- + +HRESULT CSampleIME::_HandleCandidateFinalize(TfEditCookie ec, _In_ ITfContext *pContext) +{ + HRESULT hr = S_OK; + DWORD_PTR candidateLen = 0; + const WCHAR* pCandidateString = nullptr; + CStringRange candidateString; + + if (nullptr == _pCandidateListUIPresenter) + { + goto NoPresenter; + } + + candidateLen = _pCandidateListUIPresenter->_GetSelectedCandidateString(&pCandidateString); + + candidateString.Set(pCandidateString, candidateLen); + + if (candidateLen) + { + hr = _AddComposingAndChar(ec, pContext, &candidateString); + + if (FAILED(hr)) + { + return hr; + } + } + +NoPresenter: + + _HandleComplete(ec, pContext); + + return hr; +} + +//+--------------------------------------------------------------------------- +// +// _HandleCandidateConvert +// +//---------------------------------------------------------------------------- + +HRESULT CSampleIME::_HandleCandidateConvert(TfEditCookie ec, _In_ ITfContext *pContext) +{ + return _HandleCandidateWorker(ec, pContext); +} + +//+--------------------------------------------------------------------------- +// +// _HandleCandidateWorker +// +//---------------------------------------------------------------------------- + +HRESULT CSampleIME::_HandleCandidateWorker(TfEditCookie ec, _In_ ITfContext *pContext) +{ + HRESULT hrReturn = E_FAIL; + DWORD_PTR candidateLen = 0; + const WCHAR* pCandidateString = nullptr; + BSTR pbstr = nullptr; + CStringRange candidateString; + CSampleImeArray candidatePhraseList; + + if (nullptr == _pCandidateListUIPresenter) + { + hrReturn = S_OK; + goto Exit; + } + + candidateLen = _pCandidateListUIPresenter->_GetSelectedCandidateString(&pCandidateString); + if (0 == candidateLen) + { + hrReturn = S_FALSE; + goto Exit; + } + + candidateString.Set(pCandidateString, candidateLen); + + BOOL fMakePhraseFromText = _pCompositionProcessorEngine->IsMakePhraseFromText(); + if (fMakePhraseFromText) + { + _pCompositionProcessorEngine->GetCandidateStringInConverted(candidateString, &candidatePhraseList); + LCID locale = _pCompositionProcessorEngine->GetLocale(); + + _pCandidateListUIPresenter->RemoveSpecificCandidateFromList(locale, candidatePhraseList, candidateString); + } + + // We have a candidate list if candidatePhraseList.Cnt is not 0 + // If we are showing reverse conversion, use CCandidateListUIPresenter + CANDIDATE_MODE tempCandMode = CANDIDATE_NONE; + CCandidateListUIPresenter* pTempCandListUIPresenter = nullptr; + if (candidatePhraseList.Count()) + { + tempCandMode = CANDIDATE_WITH_NEXT_COMPOSITION; + + pTempCandListUIPresenter = new (std::nothrow) CCandidateListUIPresenter(this, Global::AtomCandidateWindow, + CATEGORY_CANDIDATE, + _pCompositionProcessorEngine->GetCandidateListIndexRange(), + FALSE); + if (nullptr == pTempCandListUIPresenter) + { + hrReturn = E_OUTOFMEMORY; + goto Exit; + } + } + + // call _Start*Line for CCandidateListUIPresenter or CReadingLine + // we don't cache the document manager object so get it from pContext. + ITfDocumentMgr* pDocumentMgr = nullptr; + HRESULT hrStartCandidateList = E_FAIL; + if (pContext->GetDocumentMgr(&pDocumentMgr) == S_OK) + { + ITfRange* pRange = nullptr; + if (_pComposition->GetRange(&pRange) == S_OK) + { + if (pTempCandListUIPresenter) + { + hrStartCandidateList = pTempCandListUIPresenter->_StartCandidateList(_tfClientId, pDocumentMgr, pContext, ec, pRange, _pCompositionProcessorEngine->GetCandidateWindowWidth()); + } + + pRange->Release(); + } + pDocumentMgr->Release(); + } + + // set up candidate list if it is being shown + if (SUCCEEDED(hrStartCandidateList)) + { + pTempCandListUIPresenter->_SetTextColor(RGB(0, 0x80, 0), GetSysColor(COLOR_WINDOW)); // Text color is green + pTempCandListUIPresenter->_SetFillColor((HBRUSH)(COLOR_WINDOW+1)); // Background color is window + pTempCandListUIPresenter->_SetText(&candidatePhraseList, FALSE); + + // Add composing character + hrReturn = _AddComposingAndChar(ec, pContext, &candidateString); + + // close candidate list + if (_pCandidateListUIPresenter) + { + _pCandidateListUIPresenter->_EndCandidateList(); + delete _pCandidateListUIPresenter; + _pCandidateListUIPresenter = nullptr; + + _candidateMode = CANDIDATE_NONE; + _isCandidateWithWildcard = FALSE; + } + + if (hrReturn == S_OK) + { + // copy temp candidate + _pCandidateListUIPresenter = pTempCandListUIPresenter; + + _candidateMode = tempCandMode; + _isCandidateWithWildcard = FALSE; + } + } + else + { + hrReturn = _HandleCandidateFinalize(ec, pContext); + } + + if (pbstr) + { + SysFreeString(pbstr); + } + +Exit: + return hrReturn; +} + +//+--------------------------------------------------------------------------- +// +// _HandleCandidateArrowKey +// +//---------------------------------------------------------------------------- + +HRESULT CSampleIME::_HandleCandidateArrowKey(TfEditCookie ec, _In_ ITfContext *pContext, _In_ KEYSTROKE_FUNCTION keyFunction) +{ + ec; + pContext; + + _pCandidateListUIPresenter->AdviseUIChangedByArrowKey(keyFunction); + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// _HandleCandidateSelectByNumber +// +//---------------------------------------------------------------------------- + +HRESULT CSampleIME::_HandleCandidateSelectByNumber(TfEditCookie ec, _In_ ITfContext *pContext, _In_ UINT uCode) +{ + int iSelectAsNumber = _pCompositionProcessorEngine->GetCandidateListIndexRange()->GetIndex(uCode); + if (iSelectAsNumber == -1) + { + return S_FALSE; + } + + if (_pCandidateListUIPresenter) + { + if (_pCandidateListUIPresenter->_SetSelectionInPage(iSelectAsNumber)) + { + return _HandleCandidateConvert(ec, pContext); + } + } + + return S_FALSE; +} + +//+--------------------------------------------------------------------------- +// +// _HandlePhraseFinalize +// +//---------------------------------------------------------------------------- + +HRESULT CSampleIME::_HandlePhraseFinalize(TfEditCookie ec, _In_ ITfContext *pContext) +{ + HRESULT hr = S_OK; + + DWORD phraseLen = 0; + const WCHAR* pPhraseString = nullptr; + + phraseLen = (DWORD)_pCandidateListUIPresenter->_GetSelectedCandidateString(&pPhraseString); + + CStringRange phraseString; + phraseString.Set(pPhraseString, phraseLen); + + if (phraseLen) + { + if ((hr = _AddCharAndFinalize(ec, pContext, &phraseString)) != S_OK) + { + return hr; + } + } + + _HandleComplete(ec, pContext); + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// _HandlePhraseArrowKey +// +//---------------------------------------------------------------------------- + +HRESULT CSampleIME::_HandlePhraseArrowKey(TfEditCookie ec, _In_ ITfContext *pContext, _In_ KEYSTROKE_FUNCTION keyFunction) +{ + ec; + pContext; + + _pCandidateListUIPresenter->AdviseUIChangedByArrowKey(keyFunction); + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// _HandlePhraseSelectByNumber +// +//---------------------------------------------------------------------------- + +HRESULT CSampleIME::_HandlePhraseSelectByNumber(TfEditCookie ec, _In_ ITfContext *pContext, _In_ UINT uCode) +{ + int iSelectAsNumber = _pCompositionProcessorEngine->GetCandidateListIndexRange()->GetIndex(uCode); + if (iSelectAsNumber == -1) + { + return S_FALSE; + } + + if (_pCandidateListUIPresenter) + { + if (_pCandidateListUIPresenter->_SetSelectionInPage(iSelectAsNumber)) + { + return _HandlePhraseFinalize(ec, pContext); + } + } + + return S_FALSE; +} + +////////////////////////////////////////////////////////////////////// +// +// CCandidateListUIPresenter class +// +////////////////////////////////////////////////////////////////////// + +//+--------------------------------------------------------------------------- +// +// ctor +// +//---------------------------------------------------------------------------- + +CCandidateListUIPresenter::CCandidateListUIPresenter(_In_ CSampleIME *pTextService, ATOM atom, KEYSTROKE_CATEGORY Category, _In_ CCandidateRange *pIndexRange, BOOL hideWindow) : CTfTextLayoutSink(pTextService) +{ + _atom = atom; + + _pIndexRange = pIndexRange; + + _parentWndHandle = nullptr; + _pCandidateWnd = nullptr; + + _Category = Category; + + _updatedFlags = 0; + + _uiElementId = (DWORD)-1; + _isShowMode = TRUE; // store return value from BeginUIElement + _hideWindow = hideWindow; // Hide window flag from [Configuration] CandidateList.Phrase.HideWindow + + _pTextService = pTextService; + _pTextService->AddRef(); + + _refCount = 1; +} + +//+--------------------------------------------------------------------------- +// +// dtor +// +//---------------------------------------------------------------------------- + +CCandidateListUIPresenter::~CCandidateListUIPresenter() +{ + _EndCandidateList(); + _pTextService->Release(); +} + +//+--------------------------------------------------------------------------- +// +// ITfCandidateListUIElement::IUnknown::QueryInterface +// +//---------------------------------------------------------------------------- + +STDAPI CCandidateListUIPresenter::QueryInterface(REFIID riid, _Outptr_ void **ppvObj) +{ + if (CTfTextLayoutSink::QueryInterface(riid, ppvObj) == S_OK) + { + return S_OK; + } + + if (ppvObj == nullptr) + { + return E_INVALIDARG; + } + + *ppvObj = nullptr; + + if (IsEqualIID(riid, IID_ITfUIElement) || + IsEqualIID(riid, IID_ITfCandidateListUIElement)) + { + *ppvObj = (ITfCandidateListUIElement*)this; + } + else if (IsEqualIID(riid, IID_IUnknown) || + IsEqualIID(riid, IID_ITfCandidateListUIElementBehavior)) + { + *ppvObj = (ITfCandidateListUIElementBehavior*)this; + } + else if (IsEqualIID(riid, __uuidof(ITfIntegratableCandidateListUIElement))) + { + *ppvObj = (ITfIntegratableCandidateListUIElement*)this; + } + + if (*ppvObj) + { + AddRef(); + return S_OK; + } + + return E_NOINTERFACE; +} + +//+--------------------------------------------------------------------------- +// +// ITfCandidateListUIElement::IUnknown::AddRef +// +//---------------------------------------------------------------------------- + +STDAPI_(ULONG) CCandidateListUIPresenter::AddRef() +{ + CTfTextLayoutSink::AddRef(); + return ++_refCount; +} + +//+--------------------------------------------------------------------------- +// +// ITfCandidateListUIElement::IUnknown::Release +// +//---------------------------------------------------------------------------- + +STDAPI_(ULONG) CCandidateListUIPresenter::Release() +{ + CTfTextLayoutSink::Release(); + + LONG cr = --_refCount; + + assert(_refCount >= 0); + + if (_refCount == 0) + { + delete this; + } + + return cr; +} + +//+--------------------------------------------------------------------------- +// +// ITfCandidateListUIElement::ITfUIElement::GetDescription +// +//---------------------------------------------------------------------------- + +STDAPI CCandidateListUIPresenter::GetDescription(BSTR *pbstr) +{ + if (pbstr) + { + *pbstr = SysAllocString(L"Cand"); + } + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// ITfCandidateListUIElement::ITfUIElement::GetGUID +// +//---------------------------------------------------------------------------- + +STDAPI CCandidateListUIPresenter::GetGUID(GUID *pguid) +{ + *pguid = Global::SampleIMEGuidCandUIElement; + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// ITfCandidateListUIElement::ITfUIElement::Show +// +//---------------------------------------------------------------------------- + +STDAPI CCandidateListUIPresenter::Show(BOOL showCandidateWindow) +{ + if (showCandidateWindow) + { + ToShowCandidateWindow(); + } + else + { + ToHideCandidateWindow(); + } + return S_OK; +} + +HRESULT CCandidateListUIPresenter::ToShowCandidateWindow() +{ + if (_hideWindow) + { + _pCandidateWnd->_Show(FALSE); + } + else + { + _MoveWindowToTextExt(); + + _pCandidateWnd->_Show(TRUE); + } + + return S_OK; +} + +HRESULT CCandidateListUIPresenter::ToHideCandidateWindow() +{ + if (_pCandidateWnd) + { + _pCandidateWnd->_Show(FALSE); + } + + _updatedFlags = TF_CLUIE_SELECTION | TF_CLUIE_CURRENTPAGE; + _UpdateUIElement(); + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// ITfCandidateListUIElement::ITfUIElement::IsShown +// +//---------------------------------------------------------------------------- + +STDAPI CCandidateListUIPresenter::IsShown(BOOL *pIsShow) +{ + *pIsShow = _pCandidateWnd->_IsWindowVisible(); + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// ITfCandidateListUIElement::GetUpdatedFlags +// +//---------------------------------------------------------------------------- + +STDAPI CCandidateListUIPresenter::GetUpdatedFlags(DWORD *pdwFlags) +{ + *pdwFlags = _updatedFlags; + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// ITfCandidateListUIElement::GetDocumentMgr +// +//---------------------------------------------------------------------------- + +STDAPI CCandidateListUIPresenter::GetDocumentMgr(ITfDocumentMgr **ppdim) +{ + *ppdim = nullptr; + + return E_NOTIMPL; +} + +//+--------------------------------------------------------------------------- +// +// ITfCandidateListUIElement::GetCount +// +//---------------------------------------------------------------------------- + +STDAPI CCandidateListUIPresenter::GetCount(UINT *pCandidateCount) +{ + if (_pCandidateWnd) + { + *pCandidateCount = _pCandidateWnd->_GetCount(); + } + else + { + *pCandidateCount = 0; + } + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// ITfCandidateListUIElement::GetSelection +// +//---------------------------------------------------------------------------- + +STDAPI CCandidateListUIPresenter::GetSelection(UINT *pSelectedCandidateIndex) +{ + if (_pCandidateWnd) + { + *pSelectedCandidateIndex = _pCandidateWnd->_GetSelection(); + } + else + { + *pSelectedCandidateIndex = 0; + } + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// ITfCandidateListUIElement::GetString +// +//---------------------------------------------------------------------------- + +STDAPI CCandidateListUIPresenter::GetString(UINT uIndex, BSTR *pbstr) +{ + if (!_pCandidateWnd || (uIndex > _pCandidateWnd->_GetCount())) + { + return E_FAIL; + } + + DWORD candidateLen = 0; + const WCHAR* pCandidateString = nullptr; + + candidateLen = _pCandidateWnd->_GetCandidateString(uIndex, &pCandidateString); + + *pbstr = (candidateLen == 0) ? nullptr : SysAllocStringLen(pCandidateString, candidateLen); + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// ITfCandidateListUIElement::GetPageIndex +// +//---------------------------------------------------------------------------- + +STDAPI CCandidateListUIPresenter::GetPageIndex(UINT *pIndex, UINT uSize, UINT *puPageCnt) +{ + if (!_pCandidateWnd) + { + if (pIndex) + { + *pIndex = 0; + } + *puPageCnt = 0; + return S_OK; + } + return _pCandidateWnd->_GetPageIndex(pIndex, uSize, puPageCnt); +} + +//+--------------------------------------------------------------------------- +// +// ITfCandidateListUIElement::SetPageIndex +// +//---------------------------------------------------------------------------- + +STDAPI CCandidateListUIPresenter::SetPageIndex(UINT *pIndex, UINT uPageCnt) +{ + if (!_pCandidateWnd) + { + return E_FAIL; + } + return _pCandidateWnd->_SetPageIndex(pIndex, uPageCnt); +} + +//+--------------------------------------------------------------------------- +// +// ITfCandidateListUIElement::GetCurrentPage +// +//---------------------------------------------------------------------------- + +STDAPI CCandidateListUIPresenter::GetCurrentPage(UINT *puPage) +{ + if (!_pCandidateWnd) + { + *puPage = 0; + return S_OK; + } + return _pCandidateWnd->_GetCurrentPage(puPage); +} + +//+--------------------------------------------------------------------------- +// +// ITfCandidateListUIElementBehavior::SetSelection +// It is related of the mouse clicking behavior upon the suggestion window +//---------------------------------------------------------------------------- + +STDAPI CCandidateListUIPresenter::SetSelection(UINT nIndex) +{ + if (_pCandidateWnd) + { + _pCandidateWnd->_SetSelection(nIndex); + } + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// ITfCandidateListUIElementBehavior::Finalize +// It is related of the mouse clicking behavior upon the suggestion window +//---------------------------------------------------------------------------- + +STDAPI CCandidateListUIPresenter::Finalize(void) +{ + _CandidateChangeNotification(CAND_ITEM_SELECT); + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// ITfCandidateListUIElementBehavior::Abort +// +//---------------------------------------------------------------------------- + +STDAPI CCandidateListUIPresenter::Abort(void) +{ + return E_NOTIMPL; +} + +//+--------------------------------------------------------------------------- +// +// ITfIntegratableCandidateListUIElement::SetIntegrationStyle +// To show candidateNumbers on the suggestion window +//---------------------------------------------------------------------------- + +STDAPI CCandidateListUIPresenter::SetIntegrationStyle(GUID guidIntegrationStyle) +{ + return (guidIntegrationStyle == GUID_INTEGRATIONSTYLE_SEARCHBOX) ? S_OK : E_NOTIMPL; +} + +//+--------------------------------------------------------------------------- +// +// ITfIntegratableCandidateListUIElement::GetSelectionStyle +// +//---------------------------------------------------------------------------- + +STDAPI CCandidateListUIPresenter::GetSelectionStyle(_Out_ TfIntegratableCandidateListSelectionStyle *ptfSelectionStyle) +{ + *ptfSelectionStyle = STYLE_ACTIVE_SELECTION; + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// ITfIntegratableCandidateListUIElement::OnKeyDown +// +//---------------------------------------------------------------------------- + +STDAPI CCandidateListUIPresenter::OnKeyDown(_In_ WPARAM wParam, _In_ LPARAM lParam, _Out_ BOOL *pIsEaten) +{ + wParam; + lParam; + + *pIsEaten = TRUE; + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// ITfIntegratableCandidateListUIElement::ShowCandidateNumbers +// +//---------------------------------------------------------------------------- + +STDAPI CCandidateListUIPresenter::ShowCandidateNumbers(_Out_ BOOL *pIsShow) +{ + *pIsShow = TRUE; + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// ITfIntegratableCandidateListUIElement::FinalizeExactCompositionString +// +//---------------------------------------------------------------------------- + +STDAPI CCandidateListUIPresenter::FinalizeExactCompositionString() +{ + return E_NOTIMPL; +} + + +//+--------------------------------------------------------------------------- +// +// _StartCandidateList +// +//---------------------------------------------------------------------------- + +HRESULT CCandidateListUIPresenter::_StartCandidateList(TfClientId tfClientId, _In_ ITfDocumentMgr *pDocumentMgr, _In_ ITfContext *pContextDocument, TfEditCookie ec, _In_ ITfRange *pRangeComposition, UINT wndWidth) +{ + pDocumentMgr;tfClientId; + + HRESULT hr = E_FAIL; + + if (FAILED(_StartLayout(pContextDocument, ec, pRangeComposition))) + { + goto Exit; + } + + BeginUIElement(); + + hr = MakeCandidateWindow(pContextDocument, wndWidth); + if (FAILED(hr)) + { + goto Exit; + } + + Show(_isShowMode); + + RECT rcTextExt; + if (SUCCEEDED(_GetTextExt(&rcTextExt))) + { + _LayoutChangeNotification(&rcTextExt); + } + +Exit: + if (FAILED(hr)) + { + _EndCandidateList(); + } + return hr; +} + +//+--------------------------------------------------------------------------- +// +// _EndCandidateList +// +//---------------------------------------------------------------------------- + +void CCandidateListUIPresenter::_EndCandidateList() +{ + EndUIElement(); + + DisposeCandidateWindow(); + + _EndLayout(); +} + +//+--------------------------------------------------------------------------- +// +// _SetText +// +//---------------------------------------------------------------------------- + +void CCandidateListUIPresenter::_SetText(_In_ CSampleImeArray *pCandidateList, BOOL isAddFindKeyCode) +{ + AddCandidateToCandidateListUI(pCandidateList, isAddFindKeyCode); + + SetPageIndexWithScrollInfo(pCandidateList); + + if (_isShowMode) + { + _pCandidateWnd->_InvalidateRect(); + } + else + { + _updatedFlags = TF_CLUIE_COUNT | + TF_CLUIE_SELECTION | + TF_CLUIE_STRING | + TF_CLUIE_PAGEINDEX | + TF_CLUIE_CURRENTPAGE; + _UpdateUIElement(); + } +} + +void CCandidateListUIPresenter::AddCandidateToCandidateListUI(_In_ CSampleImeArray *pCandidateList, BOOL isAddFindKeyCode) +{ + for (UINT index = 0; index < pCandidateList->Count(); index++) + { + _pCandidateWnd->_AddString(pCandidateList->GetAt(index), isAddFindKeyCode); + } +} + +void CCandidateListUIPresenter::SetPageIndexWithScrollInfo(_In_ CSampleImeArray *pCandidateList) +{ + UINT candCntInPage = _pIndexRange->Count(); + UINT bufferSize = pCandidateList->Count() / candCntInPage + 1; + UINT* puPageIndex = new (std::nothrow) UINT[ bufferSize ]; + if (puPageIndex != nullptr) + { + for (UINT i = 0; i < bufferSize; i++) + { + puPageIndex[i] = i * candCntInPage; + } + + _pCandidateWnd->_SetPageIndex(puPageIndex, bufferSize); + delete [] puPageIndex; + } + _pCandidateWnd->_SetScrollInfo(pCandidateList->Count(), candCntInPage); // nMax:range of max, nPage:number of items in page + +} +//+--------------------------------------------------------------------------- +// +// _ClearList +// +//---------------------------------------------------------------------------- + +void CCandidateListUIPresenter::_ClearList() +{ + _pCandidateWnd->_ClearList(); + _pCandidateWnd->_InvalidateRect(); +} + +//+--------------------------------------------------------------------------- +// +// _SetTextColor +// _SetFillColor +// +//---------------------------------------------------------------------------- + +void CCandidateListUIPresenter::_SetTextColor(COLORREF crColor, COLORREF crBkColor) +{ + _pCandidateWnd->_SetTextColor(crColor, crBkColor); +} + +void CCandidateListUIPresenter::_SetFillColor(HBRUSH hBrush) +{ + _pCandidateWnd->_SetFillColor(hBrush); +} + +//+--------------------------------------------------------------------------- +// +// _GetSelectedCandidateString +// +//---------------------------------------------------------------------------- + +DWORD_PTR CCandidateListUIPresenter::_GetSelectedCandidateString(_Outptr_result_maybenull_ const WCHAR **ppwchCandidateString) +{ + return _pCandidateWnd->_GetSelectedCandidateString(ppwchCandidateString); +} + +//+--------------------------------------------------------------------------- +// +// _MoveSelection +// +//---------------------------------------------------------------------------- + +BOOL CCandidateListUIPresenter::_MoveSelection(_In_ int offSet) +{ + BOOL ret = _pCandidateWnd->_MoveSelection(offSet, TRUE); + if (ret) + { + if (_isShowMode) + { + _pCandidateWnd->_InvalidateRect(); + } + else + { + _updatedFlags = TF_CLUIE_SELECTION; + _UpdateUIElement(); + } + } + return ret; +} + +//+--------------------------------------------------------------------------- +// +// _SetSelection +// +//---------------------------------------------------------------------------- + +BOOL CCandidateListUIPresenter::_SetSelection(_In_ int selectedIndex) +{ + BOOL ret = _pCandidateWnd->_SetSelection(selectedIndex, TRUE); + if (ret) + { + if (_isShowMode) + { + _pCandidateWnd->_InvalidateRect(); + } + else + { + _updatedFlags = TF_CLUIE_SELECTION | + TF_CLUIE_CURRENTPAGE; + _UpdateUIElement(); + } + } + return ret; +} + +//+--------------------------------------------------------------------------- +// +// _MovePage +// +//---------------------------------------------------------------------------- + +BOOL CCandidateListUIPresenter::_MovePage(_In_ int offSet) +{ + BOOL ret = _pCandidateWnd->_MovePage(offSet, TRUE); + if (ret) + { + if (_isShowMode) + { + _pCandidateWnd->_InvalidateRect(); + } + else + { + _updatedFlags = TF_CLUIE_SELECTION | + TF_CLUIE_CURRENTPAGE; + _UpdateUIElement(); + } + } + return ret; +} + +//+--------------------------------------------------------------------------- +// +// _MoveWindowToTextExt +// +//---------------------------------------------------------------------------- + +void CCandidateListUIPresenter::_MoveWindowToTextExt() +{ + RECT rc; + + if (FAILED(_GetTextExt(&rc))) + { + return; + } + + _pCandidateWnd->_Move(rc.left, rc.bottom); +} +//+--------------------------------------------------------------------------- +// +// _LayoutChangeNotification +// +//---------------------------------------------------------------------------- + +VOID CCandidateListUIPresenter::_LayoutChangeNotification(_In_ RECT *lpRect) +{ + RECT rectCandidate = {0, 0, 0, 0}; + POINT ptCandidate = {0, 0}; + + _pCandidateWnd->_GetClientRect(&rectCandidate); + _pCandidateWnd->_GetWindowExtent(lpRect, &rectCandidate, &ptCandidate); + _pCandidateWnd->_Move(ptCandidate.x, ptCandidate.y); +} + +//+--------------------------------------------------------------------------- +// +// _LayoutDestroyNotification +// +//---------------------------------------------------------------------------- + +VOID CCandidateListUIPresenter::_LayoutDestroyNotification() +{ + _EndCandidateList(); +} + +//+--------------------------------------------------------------------------- +// +// _CandidateChangeNotifiction +// +//---------------------------------------------------------------------------- + +HRESULT CCandidateListUIPresenter::_CandidateChangeNotification(_In_ enum CANDWND_ACTION action) +{ + HRESULT hr = E_FAIL; + + TfClientId tfClientId = _pTextService->_GetClientId(); + ITfThreadMgr* pThreadMgr = nullptr; + ITfDocumentMgr* pDocumentMgr = nullptr; + ITfContext* pContext = nullptr; + + _KEYSTROKE_STATE KeyState; + KeyState.Category = _Category; + KeyState.Function = FUNCTION_FINALIZE_CANDIDATELIST; + + if (CAND_ITEM_SELECT != action) + { + goto Exit; + } + + pThreadMgr = _pTextService->_GetThreadMgr(); + if (nullptr == pThreadMgr) + { + goto Exit; + } + + hr = pThreadMgr->GetFocus(&pDocumentMgr); + if (FAILED(hr)) + { + goto Exit; + } + + hr = pDocumentMgr->GetTop(&pContext); + if (FAILED(hr)) + { + pDocumentMgr->Release(); + goto Exit; + } + + CKeyHandlerEditSession *pEditSession = new (std::nothrow) CKeyHandlerEditSession(_pTextService, pContext, 0, 0, KeyState); + if (nullptr != pEditSession) + { + HRESULT hrSession = S_OK; + hr = pContext->RequestEditSession(tfClientId, pEditSession, TF_ES_SYNC | TF_ES_READWRITE, &hrSession); + if (hrSession == TF_E_SYNCHRONOUS || hrSession == TS_E_READONLY) + { + hr = pContext->RequestEditSession(tfClientId, pEditSession, TF_ES_ASYNC | TF_ES_READWRITE, &hrSession); + } + pEditSession->Release(); + } + + pContext->Release(); + pDocumentMgr->Release(); + +Exit: + return hr; +} + +//+--------------------------------------------------------------------------- +// +// _CandWndCallback +// +//---------------------------------------------------------------------------- + +// static +HRESULT CCandidateListUIPresenter::_CandWndCallback(_In_ void *pv, _In_ enum CANDWND_ACTION action) +{ + CCandidateListUIPresenter* fakeThis = (CCandidateListUIPresenter*)pv; + + return fakeThis->_CandidateChangeNotification(action); +} + +//+--------------------------------------------------------------------------- +// +// _UpdateUIElement +// +//---------------------------------------------------------------------------- + +HRESULT CCandidateListUIPresenter::_UpdateUIElement() +{ + HRESULT hr = S_OK; + + ITfThreadMgr* pThreadMgr = _pTextService->_GetThreadMgr(); + if (nullptr == pThreadMgr) + { + return S_OK; + } + + ITfUIElementMgr* pUIElementMgr = nullptr; + + hr = pThreadMgr->QueryInterface(IID_ITfUIElementMgr, (void **)&pUIElementMgr); + if (hr == S_OK) + { + pUIElementMgr->UpdateUIElement(_uiElementId); + pUIElementMgr->Release(); + } + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// OnSetThreadFocus +// +//---------------------------------------------------------------------------- + +HRESULT CCandidateListUIPresenter::OnSetThreadFocus() +{ + if (_isShowMode) + { + Show(TRUE); + } + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// OnKillThreadFocus +// +//---------------------------------------------------------------------------- + +HRESULT CCandidateListUIPresenter::OnKillThreadFocus() +{ + if (_isShowMode) + { + Show(FALSE); + } + return S_OK; +} + +void CCandidateListUIPresenter::RemoveSpecificCandidateFromList(_In_ LCID Locale, _Inout_ CSampleImeArray &candidateList, _In_ CStringRange &candidateString) +{ + for (UINT index = 0; index < candidateList.Count();) + { + CCandidateListItem* pLI = candidateList.GetAt(index); + + if (CStringRange::Compare(Locale, &candidateString, &pLI->_ItemString) == CSTR_EQUAL) + { + candidateList.RemoveAt(index); + continue; + } + + index++; + } +} + +void CCandidateListUIPresenter::AdviseUIChangedByArrowKey(_In_ KEYSTROKE_FUNCTION arrowKey) +{ + switch (arrowKey) + { + case FUNCTION_MOVE_UP: + { + _MoveSelection(MOVEUP_ONE); + break; + } + case FUNCTION_MOVE_DOWN: + { + _MoveSelection(MOVEDOWN_ONE); + break; + } + case FUNCTION_MOVE_PAGE_UP: + { + _MovePage(MOVEUP_ONE); + break; + } + case FUNCTION_MOVE_PAGE_DOWN: + { + _MovePage(MOVEDOWN_ONE); + break; + } + case FUNCTION_MOVE_PAGE_TOP: + { + _SetSelection(MOVETO_TOP); + break; + } + case FUNCTION_MOVE_PAGE_BOTTOM: + { + _SetSelection(MOVETO_BOTTOM); + break; + } + default: + break; + } +} + +HRESULT CCandidateListUIPresenter::BeginUIElement() +{ + HRESULT hr = S_OK; + + ITfThreadMgr* pThreadMgr = _pTextService->_GetThreadMgr(); + if (nullptr ==pThreadMgr) + { + hr = E_FAIL; + goto Exit; + } + + ITfUIElementMgr* pUIElementMgr = nullptr; + hr = pThreadMgr->QueryInterface(IID_ITfUIElementMgr, (void **)&pUIElementMgr); + if (hr == S_OK) + { + pUIElementMgr->BeginUIElement(this, &_isShowMode, &_uiElementId); + pUIElementMgr->Release(); + } + +Exit: + return hr; +} + +HRESULT CCandidateListUIPresenter::EndUIElement() +{ + HRESULT hr = S_OK; + + ITfThreadMgr* pThreadMgr = _pTextService->_GetThreadMgr(); + if ((nullptr == pThreadMgr) || (-1 == _uiElementId)) + { + hr = E_FAIL; + goto Exit; + } + + ITfUIElementMgr* pUIElementMgr = nullptr; + hr = pThreadMgr->QueryInterface(IID_ITfUIElementMgr, (void **)&pUIElementMgr); + if (hr == S_OK) + { + pUIElementMgr->EndUIElement(_uiElementId); + pUIElementMgr->Release(); + } + +Exit: + return hr; +} + +HRESULT CCandidateListUIPresenter::MakeCandidateWindow(_In_ ITfContext *pContextDocument, _In_ UINT wndWidth) +{ + HRESULT hr = S_OK; + + if (nullptr != _pCandidateWnd) + { + return hr; + } + + _pCandidateWnd = new (std::nothrow) CCandidateWindow(_CandWndCallback, this, _pIndexRange, _pTextService->_IsStoreAppMode()); + if (_pCandidateWnd == nullptr) + { + hr = E_OUTOFMEMORY; + goto Exit; + } + + HWND parentWndHandle = nullptr; + ITfContextView* pView = nullptr; + if (SUCCEEDED(pContextDocument->GetActiveView(&pView))) + { + pView->GetWnd(&parentWndHandle); + } + + if (!_pCandidateWnd->_Create(_atom, wndWidth, parentWndHandle)) + { + hr = E_OUTOFMEMORY; + goto Exit; + } + +Exit: + return hr; +} + +void CCandidateListUIPresenter::DisposeCandidateWindow() +{ + if (nullptr == _pCandidateWnd) + { + return; + } + + _pCandidateWnd->_Destroy(); + + delete _pCandidateWnd; + _pCandidateWnd = nullptr; +} \ No newline at end of file diff --git a/CandidateListUIPresenter.h b/CandidateListUIPresenter.h new file mode 100644 index 00000000..e618f944 --- /dev/null +++ b/CandidateListUIPresenter.h @@ -0,0 +1,139 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + + +#pragma once + +#include "KeyHandlerEditSession.h" +#include "CandidateWindow.h" +#include "TfTextLayoutSink.h" +#include "SampleIME.h" +#include "SampleIMEBaseStructure.h" + +class CReadingLine; + +//+--------------------------------------------------------------------------- +// +// CCandidateListUIPresenter +// +// ITfCandidateListUIElement / ITfIntegratableCandidateListUIElement is used for +// UILess mode support +// ITfCandidateListUIElementBehavior sends the Selection behavior message to +// 3rd party IME. +//---------------------------------------------------------------------------- + +class CCandidateListUIPresenter : public CTfTextLayoutSink, + public ITfCandidateListUIElementBehavior, + public ITfIntegratableCandidateListUIElement +{ +public: + CCandidateListUIPresenter(_In_ CSampleIME *pTextService, ATOM atom, + KEYSTROKE_CATEGORY Category, + _In_ CCandidateRange *pIndexRange, + BOOL hideWindow); + virtual ~CCandidateListUIPresenter(); + + // IUnknown + STDMETHODIMP QueryInterface(REFIID riid, _Outptr_ void **ppvObj); + STDMETHODIMP_(ULONG) AddRef(void); + STDMETHODIMP_(ULONG) Release(void); + + // ITfUIElement + STDMETHODIMP GetDescription(BSTR *pbstr); + STDMETHODIMP GetGUID(GUID *pguid); + STDMETHODIMP Show(BOOL showCandidateWindow); + STDMETHODIMP IsShown(BOOL *pIsShow); + + // ITfCandidateListUIElement + STDMETHODIMP GetUpdatedFlags(DWORD *pdwFlags); + STDMETHODIMP GetDocumentMgr(ITfDocumentMgr **ppdim); + STDMETHODIMP GetCount(UINT *pCandidateCount); + STDMETHODIMP GetSelection(UINT *pSelectedCandidateIndex); + STDMETHODIMP GetString(UINT uIndex, BSTR *pbstr); + STDMETHODIMP GetPageIndex(UINT *pIndex, UINT uSize, UINT *puPageCnt); + STDMETHODIMP SetPageIndex(UINT *pIndex, UINT uPageCnt); + STDMETHODIMP GetCurrentPage(UINT *puPage); + + // ITfCandidateListUIElementBehavior methods + STDMETHODIMP SetSelection(UINT nIndex); + STDMETHODIMP Finalize(void); + STDMETHODIMP Abort(void); + + // ITfIntegratableCandidateListUIElement + STDMETHODIMP SetIntegrationStyle(GUID guidIntegrationStyle); + STDMETHODIMP GetSelectionStyle(_Out_ TfIntegratableCandidateListSelectionStyle *ptfSelectionStyle); + STDMETHODIMP OnKeyDown(_In_ WPARAM wParam, _In_ LPARAM lParam, _Out_ BOOL *pIsEaten); + STDMETHODIMP ShowCandidateNumbers(_Out_ BOOL *pIsShow); + STDMETHODIMP FinalizeExactCompositionString(); + + virtual HRESULT _StartCandidateList(TfClientId tfClientId, _In_ ITfDocumentMgr *pDocumentMgr, _In_ ITfContext *pContextDocument, TfEditCookie ec, _In_ ITfRange *pRangeComposition, UINT wndWidth); + void _EndCandidateList(); + + void _SetText(_In_ CSampleImeArray *pCandidateList, BOOL isAddFindKeyCode); + void _ClearList(); + VOID _SetTextColor(COLORREF crColor, COLORREF crBkColor); + VOID _SetFillColor(HBRUSH hBrush); + + DWORD_PTR _GetSelectedCandidateString(_Outptr_result_maybenull_ const WCHAR **ppwchCandidateString); + BOOL _SetSelectionInPage(int nPos) { return _pCandidateWnd->_SetSelectionInPage(nPos); } + + BOOL _MoveSelection(_In_ int offSet); + BOOL _SetSelection(_In_ int selectedIndex); + BOOL _MovePage(_In_ int offSet); + + void _MoveWindowToTextExt(); + + // CTfTextLayoutSink + virtual VOID _LayoutChangeNotification(_In_ RECT *lpRect); + virtual VOID _LayoutDestroyNotification(); + + // Event for ITfThreadFocusSink + virtual HRESULT OnSetThreadFocus(); + virtual HRESULT OnKillThreadFocus(); + + void RemoveSpecificCandidateFromList(_In_ LCID Locale, _Inout_ CSampleImeArray &candidateList, _In_ CStringRange &srgCandidateString); + void AdviseUIChangedByArrowKey(_In_ KEYSTROKE_FUNCTION arrowKey); + +private: + virtual HRESULT CALLBACK _CandidateChangeNotification(_In_ enum CANDWND_ACTION action); + + static HRESULT _CandWndCallback(_In_ void *pv, _In_ enum CANDWND_ACTION action); + + friend COLORREF _AdjustTextColor(_In_ COLORREF crColor, _In_ COLORREF crBkColor); + + HRESULT _UpdateUIElement(); + + HRESULT ToShowCandidateWindow(); + + HRESULT ToHideCandidateWindow(); + + HRESULT BeginUIElement(); + HRESULT EndUIElement(); + + HRESULT MakeCandidateWindow(_In_ ITfContext *pContextDocument, _In_ UINT wndWidth); + void DisposeCandidateWindow(); + + void AddCandidateToCandidateListUI(_In_ CSampleImeArray *pCandidateList, BOOL isAddFindKeyCode); + + void SetPageIndexWithScrollInfo(_In_ CSampleImeArray *pCandidateList); + +protected: + CCandidateWindow *_pCandidateWnd; + BOOL _isShowMode; + BOOL _hideWindow; + +private: + + HWND _parentWndHandle; + ATOM _atom; + CCandidateRange* _pIndexRange; + KEYSTROKE_CATEGORY _Category; + DWORD _updatedFlags; + DWORD _uiElementId; + CSampleIME* _pTextService; + LONG _refCount; +}; diff --git a/CandidateWindow.cpp b/CandidateWindow.cpp new file mode 100644 index 00000000..da58b13c --- /dev/null +++ b/CandidateWindow.cpp @@ -0,0 +1,1438 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#include "Private.h" +#include "Globals.h" +#include "BaseWindow.h" +#include "CandidateWindow.h" + +//+--------------------------------------------------------------------------- +// +// ctor +// +//---------------------------------------------------------------------------- + +CCandidateWindow::CCandidateWindow(_In_ CANDWNDCALLBACK pfnCallback, _In_ void *pv, _In_ CCandidateRange *pIndexRange, _In_ BOOL isStoreAppMode) +{ + _currentSelection = 0; + + _SetTextColor(CANDWND_ITEM_COLOR, GetSysColor(COLOR_WINDOW)); // text color is black + _SetFillColor((HBRUSH)(COLOR_WINDOW+1)); + + _pIndexRange = pIndexRange; + + _pfnCallback = pfnCallback; + _pObj = pv; + + _pShadowWnd = nullptr; + + _cyRow = CANDWND_ROW_WIDTH; + _cxTitle = 0; + + _pVScrollBarWnd = nullptr; + + _wndWidth = 0; + + _dontAdjustOnEmptyItemPage = FALSE; + + _isStoreAppMode = isStoreAppMode; +} + +//+--------------------------------------------------------------------------- +// +// dtor +// +//---------------------------------------------------------------------------- + +CCandidateWindow::~CCandidateWindow() +{ + _ClearList(); + _DeleteShadowWnd(); + _DeleteVScrollBarWnd(); +} + +//+--------------------------------------------------------------------------- +// +// _Create +// +// CandidateWinow is the top window +//---------------------------------------------------------------------------- + +BOOL CCandidateWindow::_Create(ATOM atom, _In_ UINT wndWidth, _In_opt_ HWND parentWndHandle) +{ + BOOL ret = FALSE; + _wndWidth = wndWidth; + + ret = _CreateMainWindow(atom, parentWndHandle); + if (FALSE == ret) + { + goto Exit; + } + + ret = _CreateBackGroundShadowWindow(); + if (FALSE == ret) + { + goto Exit; + } + + ret = _CreateVScrollWindow(); + if (FALSE == ret) + { + goto Exit; + } + + _ResizeWindow(); + +Exit: + return TRUE; +} + +BOOL CCandidateWindow::_CreateMainWindow(ATOM atom, _In_opt_ HWND parentWndHandle) +{ + _SetUIWnd(this); + + if (!CBaseWindow::_Create(atom, + WS_EX_TOPMOST | WS_EX_TOOLWINDOW, + WS_BORDER | WS_POPUP, + NULL, 0, 0, parentWndHandle)) + { + return FALSE; + } + + return TRUE; +} + +BOOL CCandidateWindow::_CreateBackGroundShadowWindow() +{ + _pShadowWnd = new (std::nothrow) CShadowWindow(this); + if (_pShadowWnd == nullptr) + { + return FALSE; + } + + if (!_pShadowWnd->_Create(Global::AtomShadowWindow, + WS_EX_TOPMOST | WS_EX_TOOLWINDOW | WS_EX_LAYERED, + WS_DISABLED | WS_POPUP, this)) + { + _DeleteShadowWnd(); + return FALSE; + } + + return TRUE; +} + +BOOL CCandidateWindow::_CreateVScrollWindow() +{ + BOOL ret = FALSE; + + SHELL_MODE shellMode = _isStoreAppMode ? STOREAPP : DESKTOP; + CScrollBarWindowFactory* pFactory = CScrollBarWindowFactory::Instance(); + _pVScrollBarWnd = pFactory->MakeScrollBarWindow(shellMode); + + if (_pVScrollBarWnd == nullptr) + { + _DeleteShadowWnd(); + goto Exit; + } + + _pVScrollBarWnd->_SetUIWnd(this); + + if (!_pVScrollBarWnd->_Create(Global::AtomScrollBarWindow, WS_EX_TOPMOST | WS_EX_TOOLWINDOW, WS_CHILD, this)) + { + _DeleteVScrollBarWnd(); + _DeleteShadowWnd(); + goto Exit; + } + + ret = TRUE; + +Exit: + pFactory->Release(); + return ret; +} + +void CCandidateWindow::_ResizeWindow() +{ + SIZE size = {0, 0}; + + _cxTitle = max(_cxTitle, size.cx + 2 * GetSystemMetrics(SM_CXFRAME)); + + int candidateListPageCnt = _pIndexRange->Count(); + CBaseWindow::_Resize(0, 0, _cxTitle, _cyRow * candidateListPageCnt); + + RECT rcCandRect = {0, 0, 0, 0}; + _GetClientRect(&rcCandRect); + + int letf = rcCandRect.right - GetSystemMetrics(SM_CXVSCROLL) * 2 - CANDWND_BORDER_WIDTH; + int top = rcCandRect.top + CANDWND_BORDER_WIDTH; + int width = GetSystemMetrics(SM_CXVSCROLL) * 2; + int height = rcCandRect.bottom - rcCandRect.top - CANDWND_BORDER_WIDTH * 2; + + _pVScrollBarWnd->_Resize(letf, top, width, height); +} + +//+--------------------------------------------------------------------------- +// +// _Move +// +//---------------------------------------------------------------------------- + +void CCandidateWindow::_Move(int x, int y) +{ + CBaseWindow::_Move(x, y); +} + +//+--------------------------------------------------------------------------- +// +// _Show +// +//---------------------------------------------------------------------------- + +void CCandidateWindow::_Show(BOOL isShowWnd) +{ + if (_pShadowWnd) + { + _pShadowWnd->_Show(isShowWnd); + } + CBaseWindow::_Show(isShowWnd); +} + +//+--------------------------------------------------------------------------- +// +// _SetTextColor +// _SetFillColor +// +//---------------------------------------------------------------------------- + +VOID CCandidateWindow::_SetTextColor(_In_ COLORREF crColor, _In_ COLORREF crBkColor) +{ + _crTextColor = _AdjustTextColor(crColor, crBkColor); + _crBkColor = crBkColor; +} + +VOID CCandidateWindow::_SetFillColor(_In_ HBRUSH hBrush) +{ + _brshBkColor = hBrush; +} + +//+--------------------------------------------------------------------------- +// +// _WindowProcCallback +// +// Cand window proc. +//---------------------------------------------------------------------------- + +const int PageCountPosition = 1; +const int StringPosition = 4; + +LRESULT CALLBACK CCandidateWindow::_WindowProcCallback(_In_ HWND wndHandle, UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam) +{ + switch (uMsg) + { + case WM_CREATE: + { + HDC dcHandle = nullptr; + + dcHandle = GetDC(wndHandle); + if (dcHandle) + { + HFONT hFontOld = (HFONT)SelectObject(dcHandle, Global::defaultlFontHandle); + GetTextMetrics(dcHandle, &_TextMetric); + + _cxTitle = _TextMetric.tmMaxCharWidth * _wndWidth; + SelectObject(dcHandle, hFontOld); + ReleaseDC(wndHandle, dcHandle); + } + } + return 0; + + case WM_DESTROY: + _DeleteShadowWnd(); + return 0; + + case WM_WINDOWPOSCHANGED: + { + WINDOWPOS* pWndPos = (WINDOWPOS*)lParam; + + // move shadow + if (_pShadowWnd) + { + _pShadowWnd->_OnOwnerWndMoved((pWndPos->flags & SWP_NOSIZE) == 0); + } + + // move v-scroll + if (_pVScrollBarWnd) + { + _pVScrollBarWnd->_OnOwnerWndMoved((pWndPos->flags & SWP_NOSIZE) == 0); + } + + _FireMessageToLightDismiss(wndHandle, pWndPos); + } + break; + + case WM_WINDOWPOSCHANGING: + { + WINDOWPOS* pWndPos = (WINDOWPOS*)lParam; + + // show/hide shadow + if (_pShadowWnd) + { + if ((pWndPos->flags & SWP_HIDEWINDOW) != 0) + { + _pShadowWnd->_Show(FALSE); + } + + // don't go behaind of shadow + if (((pWndPos->flags & SWP_NOZORDER) == 0) && (pWndPos->hwndInsertAfter == _pShadowWnd->_GetWnd())) + { + pWndPos->flags |= SWP_NOZORDER; + } + + _pShadowWnd->_OnOwnerWndMoved((pWndPos->flags & SWP_NOSIZE) == 0); + } + + // show/hide v-scroll + if (_pVScrollBarWnd) + { + if ((pWndPos->flags & SWP_HIDEWINDOW) != 0) + { + _pVScrollBarWnd->_Show(FALSE); + } + + _pVScrollBarWnd->_OnOwnerWndMoved((pWndPos->flags & SWP_NOSIZE) == 0); + } + } + break; + + case WM_SHOWWINDOW: + // show/hide shadow + if (_pShadowWnd) + { + _pShadowWnd->_Show((BOOL)wParam); + } + + // show/hide v-scroll + if (_pVScrollBarWnd) + { + _pVScrollBarWnd->_Show((BOOL)wParam); + } + break; + + case WM_PAINT: + { + HDC dcHandle = nullptr; + PAINTSTRUCT ps; + + dcHandle = BeginPaint(wndHandle, &ps); + _OnPaint(dcHandle, &ps); + _DrawBorder(wndHandle, CANDWND_BORDER_WIDTH*2); + EndPaint(wndHandle, &ps); + } + return 0; + + case WM_SETCURSOR: + { + POINT cursorPoint; + + GetCursorPos(&cursorPoint); + MapWindowPoints(NULL, wndHandle, &cursorPoint, 1); + + // handle mouse message + _HandleMouseMsg(HIWORD(lParam), cursorPoint); + } + return 1; + + case WM_MOUSEMOVE: + case WM_LBUTTONDOWN: + case WM_MBUTTONDOWN: + case WM_RBUTTONDOWN: + case WM_LBUTTONUP: + case WM_MBUTTONUP: + case WM_RBUTTONUP: + { + POINT point; + + POINTSTOPOINT(point, MAKEPOINTS(lParam)); + + // handle mouse message + _HandleMouseMsg(uMsg, point); + } + // we processes this message, it should return zero. + return 0; + + case WM_MOUSEACTIVATE: + { + WORD mouseEvent = HIWORD(lParam); + if (mouseEvent == WM_LBUTTONDOWN || + mouseEvent == WM_RBUTTONDOWN || + mouseEvent == WM_MBUTTONDOWN) + { + return MA_NOACTIVATE; + } + } + break; + + case WM_POINTERACTIVATE: + return PA_NOACTIVATE; + + case WM_VSCROLL: + _OnVScroll(LOWORD(wParam), HIWORD(wParam)); + return 0; + } + + return DefWindowProc(wndHandle, uMsg, wParam, lParam); +} + +//+--------------------------------------------------------------------------- +// +// _HandleMouseMsg +// +//---------------------------------------------------------------------------- + +void CCandidateWindow::_HandleMouseMsg(_In_ UINT mouseMsg, _In_ POINT point) +{ + switch (mouseMsg) + { + case WM_MOUSEMOVE: + _OnMouseMove(point); + break; + case WM_LBUTTONDOWN: + _OnLButtonDown(point); + break; + case WM_LBUTTONUP: + _OnLButtonUp(point); + break; + } +} + +//+--------------------------------------------------------------------------- +// +// _OnPaint +// +//---------------------------------------------------------------------------- + +void CCandidateWindow::_OnPaint(_In_ HDC dcHandle, _In_ PAINTSTRUCT *pPaintStruct) +{ + SetBkMode(dcHandle, TRANSPARENT); + + HFONT hFontOld = (HFONT)SelectObject(dcHandle, Global::defaultlFontHandle); + + FillRect(dcHandle, &pPaintStruct->rcPaint, _brshBkColor); + + UINT currentPageIndex = 0; + UINT currentPage = 0; + + if (FAILED(_GetCurrentPage(¤tPage))) + { + goto cleanup; + } + + _AdjustPageIndex(currentPage, currentPageIndex); + + _DrawList(dcHandle, currentPageIndex, &pPaintStruct->rcPaint); + +cleanup: + SelectObject(dcHandle, hFontOld); +} + +//+--------------------------------------------------------------------------- +// +// _OnLButtonDown +// +//---------------------------------------------------------------------------- + +void CCandidateWindow::_OnLButtonDown(POINT pt) +{ + RECT rcWindow = {0, 0, 0, 0};; + _GetClientRect(&rcWindow); + + int cyLine = _cyRow; + + UINT candidateListPageCnt = _pIndexRange->Count(); + UINT index = 0; + int currentPage = 0; + + if (FAILED(_GetCurrentPage(¤tPage))) + { + return; + } + + // Hit test on list items + index = *_PageIndex.GetAt(currentPage); + + for (UINT pageCount = 0; (index < _candidateList.Count()) && (pageCount < candidateListPageCnt); index++, pageCount++) + { + RECT rc = {0, 0, 0, 0}; + + rc.left = rcWindow.left; + rc.right = rcWindow.right - GetSystemMetrics(SM_CXVSCROLL) * 2; + rc.top = rcWindow.top + (pageCount * cyLine); + rc.bottom = rcWindow.top + ((pageCount + 1) * cyLine); + + if (PtInRect(&rc, pt) && _pfnCallback) + { + SetCursor(LoadCursor(NULL, IDC_HAND)); + _currentSelection = index; + _pfnCallback(_pObj, CAND_ITEM_SELECT); + return; + } + } + + SetCursor(LoadCursor(NULL, IDC_ARROW)); + + if (_pVScrollBarWnd) + { + RECT rc = {0, 0, 0, 0}; + + _pVScrollBarWnd->_GetClientRect(&rc); + MapWindowPoints(_GetWnd(), _pVScrollBarWnd->_GetWnd(), &pt, 1); + + if (PtInRect(&rc, pt)) + { + _pVScrollBarWnd->_OnLButtonDown(pt); + } + } +} + +//+--------------------------------------------------------------------------- +// +// _OnLButtonUp +// +//---------------------------------------------------------------------------- + +void CCandidateWindow::_OnLButtonUp(POINT pt) +{ + if (nullptr == _pVScrollBarWnd) + { + return; + } + + RECT rc = {0, 0, 0, 0}; + _pVScrollBarWnd->_GetClientRect(&rc); + MapWindowPoints(_GetWnd(), _pVScrollBarWnd->_GetWnd(), &pt, 1); + + if (_IsCapture()) + { + _pVScrollBarWnd->_OnLButtonUp(pt); + } + else if (PtInRect(&rc, pt)) + { + _pVScrollBarWnd->_OnLButtonUp(pt); + } +} + +//+--------------------------------------------------------------------------- +// +// _OnMouseMove +// +//---------------------------------------------------------------------------- + +void CCandidateWindow::_OnMouseMove(POINT pt) +{ + RECT rcWindow = {0, 0, 0, 0}; + + _GetClientRect(&rcWindow); + + RECT rc = {0, 0, 0, 0}; + + rc.left = rcWindow.left; + rc.right = rcWindow.right - GetSystemMetrics(SM_CXVSCROLL) * 2; + + rc.top = rcWindow.top; + rc.bottom = rcWindow.bottom; + + if (PtInRect(&rc, pt)) + { + SetCursor(LoadCursor(NULL, IDC_HAND)); + return; + } + + SetCursor(LoadCursor(NULL, IDC_ARROW)); + + if (_pVScrollBarWnd) + { + _pVScrollBarWnd->_GetClientRect(&rc); + MapWindowPoints(_GetWnd(), _pVScrollBarWnd->_GetWnd(), &pt, 1); + + if (PtInRect(&rc, pt)) + { + _pVScrollBarWnd->_OnMouseMove(pt); + } + } +} + +//+--------------------------------------------------------------------------- +// +// _OnVScroll +// +//---------------------------------------------------------------------------- + +void CCandidateWindow::_OnVScroll(DWORD dwSB, _In_ DWORD nPos) +{ + switch (dwSB) + { + case SB_LINEDOWN: + _SetSelectionOffset(+1); + _InvalidateRect(); + break; + case SB_LINEUP: + _SetSelectionOffset(-1); + _InvalidateRect(); + break; + case SB_PAGEDOWN: + _MovePage(+1, FALSE); + _InvalidateRect(); + break; + case SB_PAGEUP: + _MovePage(-1, FALSE); + _InvalidateRect(); + break; + case SB_THUMBPOSITION: + _SetSelection(nPos, FALSE); + _InvalidateRect(); + break; + } +} + +//+--------------------------------------------------------------------------- +// +// _DrawList +// +//---------------------------------------------------------------------------- + +void CCandidateWindow::_DrawList(_In_ HDC dcHandle, _In_ UINT iIndex, _In_ RECT *prc) +{ + int pageCount = 0; + int candidateListPageCnt = _pIndexRange->Count(); + + int cxLine = _TextMetric.tmAveCharWidth; + int cyLine = max(_cyRow, _TextMetric.tmHeight); + int cyOffset = (cyLine == _cyRow ? (cyLine-_TextMetric.tmHeight)/2 : 0); + + RECT rc; + + const size_t lenOfPageCount = 16; + for (; + (iIndex < _candidateList.Count()) && (pageCount < candidateListPageCnt); + iIndex++, pageCount++) + { + WCHAR pageCountString[lenOfPageCount] = {'\0'}; + CCandidateListItem* pItemList = nullptr; + + rc.top = prc->top + pageCount * cyLine; + rc.bottom = rc.top + cyLine; + + rc.left = prc->left + PageCountPosition * cxLine; + rc.right = prc->left + StringPosition * cxLine; + + // Number Font Color And BK + SetTextColor(dcHandle, CANDWND_NUM_COLOR); + SetBkColor(dcHandle, GetSysColor(COLOR_3DHIGHLIGHT)); + + StringCchPrintf(pageCountString, ARRAYSIZE(pageCountString), L"%d", (LONG)*_pIndexRange->GetAt(pageCount)); + ExtTextOut(dcHandle, PageCountPosition * cxLine, pageCount * cyLine + cyOffset, ETO_OPAQUE, &rc, pageCountString, lenOfPageCount, NULL); + + rc.left = prc->left + StringPosition * cxLine; + rc.right = prc->right; + + // Candidate Font Color And BK + if (_currentSelection != iIndex) + { + SetTextColor(dcHandle, _crTextColor); + SetBkColor(dcHandle, GetSysColor(COLOR_3DHIGHLIGHT)); + } + else + { + SetTextColor(dcHandle, CANDWND_SELECTED_ITEM_COLOR); + SetBkColor(dcHandle, CANDWND_SELECTED_BK_COLOR); + } + + pItemList = _candidateList.GetAt(iIndex); + ExtTextOut(dcHandle, StringPosition * cxLine, pageCount * cyLine + cyOffset, ETO_OPAQUE, &rc, pItemList->_ItemString.Get(), (DWORD)pItemList->_ItemString.GetLength(), NULL); + } + for (; (pageCount < candidateListPageCnt); pageCount++) + { + rc.top = prc->top + pageCount * cyLine; + rc.bottom = rc.top + cyLine; + + rc.left = prc->left + PageCountPosition * cxLine; + rc.right = prc->left + StringPosition * cxLine; + + FillRect(dcHandle, &rc, (HBRUSH)(COLOR_3DHIGHLIGHT+1)); + } +} + +//+--------------------------------------------------------------------------- +// +// _DrawBorder +// +//---------------------------------------------------------------------------- +void CCandidateWindow::_DrawBorder(_In_ HWND wndHandle, _In_ int cx) +{ + RECT rcWnd; + + HDC dcHandle = GetWindowDC(wndHandle); + + GetWindowRect(wndHandle, &rcWnd); + // zero based + OffsetRect(&rcWnd, -rcWnd.left, -rcWnd.top); + + HPEN hPen = CreatePen(PS_DOT, cx, CANDWND_BORDER_COLOR); + HPEN hPenOld = (HPEN)SelectObject(dcHandle, hPen); + HBRUSH hBorderBrush = (HBRUSH)GetStockObject(NULL_BRUSH); + HBRUSH hBorderBrushOld = (HBRUSH)SelectObject(dcHandle, hBorderBrush); + + Rectangle(dcHandle, rcWnd.left, rcWnd.top, rcWnd.right, rcWnd.bottom); + + SelectObject(dcHandle, hPenOld); + SelectObject(dcHandle, hBorderBrushOld); + DeleteObject(hPen); + DeleteObject(hBorderBrush); + ReleaseDC(wndHandle, dcHandle); + +} + +//+--------------------------------------------------------------------------- +// +// _AddString +// +//---------------------------------------------------------------------------- + +void CCandidateWindow::_AddString(_Inout_ CCandidateListItem *pCandidateItem, _In_ BOOL isAddFindKeyCode) +{ + DWORD_PTR dwItemString = pCandidateItem->_ItemString.GetLength(); + const WCHAR* pwchString = nullptr; + if (dwItemString) + { + pwchString = new (std::nothrow) WCHAR[ dwItemString ]; + if (!pwchString) + { + return; + } + memcpy((void*)pwchString, pCandidateItem->_ItemString.Get(), dwItemString * sizeof(WCHAR)); + } + + DWORD_PTR itemWildcard = pCandidateItem->_FindKeyCode.GetLength(); + const WCHAR* pwchWildcard = nullptr; + if (itemWildcard && isAddFindKeyCode) + { + pwchWildcard = new (std::nothrow) WCHAR[ itemWildcard ]; + if (!pwchWildcard) + { + if (pwchString) + { + delete [] pwchString; + } + return; + } + memcpy((void*)pwchWildcard, pCandidateItem->_FindKeyCode.Get(), itemWildcard * sizeof(WCHAR)); + } + + CCandidateListItem* pLI = nullptr; + pLI = _candidateList.Append(); + if (!pLI) + { + if (pwchString) + { + delete [] pwchString; + pwchString = nullptr; + } + if (pwchWildcard) + { + delete [] pwchWildcard; + pwchWildcard = nullptr; + } + return; + } + + if (pwchString) + { + pLI->_ItemString.Set(pwchString, dwItemString); + } + if (pwchWildcard) + { + pLI->_FindKeyCode.Set(pwchWildcard, itemWildcard); + } + + return; +} + +//+--------------------------------------------------------------------------- +// +// _ClearList +// +//---------------------------------------------------------------------------- + +void CCandidateWindow::_ClearList() +{ + for (UINT index = 0; index < _candidateList.Count(); index++) + { + CCandidateListItem* pItemList = nullptr; + pItemList = _candidateList.GetAt(index); + delete [] pItemList->_ItemString.Get(); + delete [] pItemList->_FindKeyCode.Get(); + } + _currentSelection = 0; + _candidateList.Clear(); + _PageIndex.Clear(); +} + +//+--------------------------------------------------------------------------- +// +// _SetScrollInfo +// +//---------------------------------------------------------------------------- + +void CCandidateWindow::_SetScrollInfo(_In_ int nMax, _In_ int nPage) +{ + CScrollInfo si; + si.nMax = nMax; + si.nPage = nPage; + si.nPos = 0; + + if (_pVScrollBarWnd) + { + _pVScrollBarWnd->_SetScrollInfo(&si); + } +} + +//+--------------------------------------------------------------------------- +// +// _GetCandidateString +// +//---------------------------------------------------------------------------- + +DWORD CCandidateWindow::_GetCandidateString(_In_ int iIndex, _Outptr_result_maybenull_z_ const WCHAR **ppwchCandidateString) +{ + CCandidateListItem* pItemList = nullptr; + + if (iIndex < 0 ) + { + *ppwchCandidateString = nullptr; + return 0; + } + + UINT index = static_cast(iIndex); + + if (index >= _candidateList.Count()) + { + *ppwchCandidateString = nullptr; + return 0; + } + + pItemList = _candidateList.GetAt(iIndex); + if (ppwchCandidateString) + { + *ppwchCandidateString = pItemList->_ItemString.Get(); + } + return (DWORD)pItemList->_ItemString.GetLength(); +} + +//+--------------------------------------------------------------------------- +// +// _GetSelectedCandidateString +// +//---------------------------------------------------------------------------- + +DWORD CCandidateWindow::_GetSelectedCandidateString(_Outptr_result_maybenull_ const WCHAR **ppwchCandidateString) +{ + CCandidateListItem* pItemList = nullptr; + + if (_currentSelection >= _candidateList.Count()) + { + *ppwchCandidateString = nullptr; + return 0; + } + + pItemList = _candidateList.GetAt(_currentSelection); + if (ppwchCandidateString) + { + *ppwchCandidateString = pItemList->_ItemString.Get(); + } + return (DWORD)pItemList->_ItemString.GetLength(); +} + +//+--------------------------------------------------------------------------- +// +// _SetSelectionInPage +// +//---------------------------------------------------------------------------- + +BOOL CCandidateWindow::_SetSelectionInPage(int nPos) +{ + if (nPos < 0) + { + return FALSE; + } + + UINT pos = static_cast(nPos); + + if (pos >= _candidateList.Count()) + { + return FALSE; + } + + int currentPage = 0; + if (FAILED(_GetCurrentPage(¤tPage))) + { + return FALSE; + } + + _currentSelection = *_PageIndex.GetAt(currentPage) + nPos; + + return TRUE; +} + +//+--------------------------------------------------------------------------- +// +// _MoveSelection +// +//---------------------------------------------------------------------------- + +BOOL CCandidateWindow::_MoveSelection(_In_ int offSet, _In_ BOOL isNotify) +{ + if (_currentSelection + offSet >= _candidateList.Count()) + { + return FALSE; + } + + _currentSelection += offSet; + + _dontAdjustOnEmptyItemPage = TRUE; + + if (_pVScrollBarWnd && isNotify) + { + _pVScrollBarWnd->_ShiftLine(offSet, isNotify); + } + + return TRUE; +} + +//+--------------------------------------------------------------------------- +// +// _SetSelection +// +//---------------------------------------------------------------------------- + +BOOL CCandidateWindow::_SetSelection(_In_ int selectedIndex, _In_ BOOL isNotify) +{ + if (selectedIndex == -1) + { + selectedIndex = _candidateList.Count() - 1; + } + + if (selectedIndex < 0) + { + return FALSE; + } + + int candCnt = static_cast(_candidateList.Count()); + if (selectedIndex >= candCnt) + { + return FALSE; + } + + _currentSelection = static_cast(selectedIndex); + + BOOL ret = _AdjustPageIndexForSelection(); + + if (_pVScrollBarWnd && isNotify) + { + _pVScrollBarWnd->_ShiftPosition(selectedIndex, isNotify); + } + + return ret; +} + +//+--------------------------------------------------------------------------- +// +// _SetSelection +// +//---------------------------------------------------------------------------- +void CCandidateWindow::_SetSelection(_In_ int nIndex) +{ + _currentSelection = nIndex; +} + +//+--------------------------------------------------------------------------- +// +// _MovePage +// +//---------------------------------------------------------------------------- + +BOOL CCandidateWindow::_MovePage(_In_ int offSet, _In_ BOOL isNotify) +{ + if (offSet == 0) + { + return TRUE; + } + + int currentPage = 0; + int selectionOffset = 0; + int newPage = 0; + + if (FAILED(_GetCurrentPage(¤tPage))) + { + return FALSE; + } + + newPage = currentPage + offSet; + if ((newPage < 0) || (newPage >= static_cast(_PageIndex.Count()))) + { + return FALSE; + } + + // If current selection is at the top of the page AND + // we are on the "default" page border, then we don't + // want adjustment to eliminate empty entries. + // + // We do this for keeping behavior inline with downlevel. + if (_currentSelection % _pIndexRange->Count() == 0 && + _currentSelection == *_PageIndex.GetAt(currentPage)) + { + _dontAdjustOnEmptyItemPage = TRUE; + } + + selectionOffset = _currentSelection - *_PageIndex.GetAt(currentPage); + _currentSelection = *_PageIndex.GetAt(newPage) + selectionOffset; + _currentSelection = _candidateList.Count() > _currentSelection ? _currentSelection : _candidateList.Count() - 1; + + // adjust scrollbar position + if (_pVScrollBarWnd && isNotify) + { + _pVScrollBarWnd->_ShiftPage(offSet, isNotify); + } + + return TRUE; +} + +//+--------------------------------------------------------------------------- +// +// _SetSelectionOffset +// +//---------------------------------------------------------------------------- + +BOOL CCandidateWindow::_SetSelectionOffset(_In_ int offSet) +{ + if (_currentSelection + offSet >= _candidateList.Count()) + { + return FALSE; + } + + BOOL fCurrentPageHasEmptyItems = FALSE; + BOOL fAdjustPageIndex = TRUE; + + _CurrentPageHasEmptyItems(&fCurrentPageHasEmptyItems); + + int newOffset = _currentSelection + offSet; + + // For SB_LINEUP and SB_LINEDOWN, we need to special case if CurrentPageHasEmptyItems. + // CurrentPageHasEmptyItems if we are on the last page. + if ((offSet == 1 || offSet == -1) && + fCurrentPageHasEmptyItems && _PageIndex.Count() > 1) + { + int iPageIndex = *_PageIndex.GetAt(_PageIndex.Count() - 1); + // Moving on the last page and last page has empty items. + if (newOffset >= iPageIndex) + { + fAdjustPageIndex = FALSE; + } + // Moving across page border. + else if (newOffset < iPageIndex) + { + fAdjustPageIndex = TRUE; + } + + _dontAdjustOnEmptyItemPage = TRUE; + } + + _currentSelection = newOffset; + + if (fAdjustPageIndex) + { + return _AdjustPageIndexForSelection(); + } + + return TRUE; +} + +//+--------------------------------------------------------------------------- +// +// _GetPageIndex +// +//---------------------------------------------------------------------------- + +HRESULT CCandidateWindow::_GetPageIndex(UINT *pIndex, _In_ UINT uSize, _Inout_ UINT *puPageCnt) +{ + HRESULT hr = S_OK; + + if (uSize > _PageIndex.Count()) + { + uSize = _PageIndex.Count(); + } + else + { + hr = S_FALSE; + } + + if (pIndex) + { + for (UINT i = 0; i < uSize; i++) + { + *pIndex = *_PageIndex.GetAt(i); + pIndex++; + } + } + + *puPageCnt = _PageIndex.Count(); + + return hr; +} + +//+--------------------------------------------------------------------------- +// +// _SetPageIndex +// +//---------------------------------------------------------------------------- + +HRESULT CCandidateWindow::_SetPageIndex(UINT *pIndex, _In_ UINT uPageCnt) +{ + uPageCnt; + + _PageIndex.Clear(); + + for (UINT i = 0; i < uPageCnt; i++) + { + UINT *pLastNewPageIndex = _PageIndex.Append(); + if (pLastNewPageIndex != nullptr) + { + *pLastNewPageIndex = *pIndex; + pIndex++; + } + } + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// _GetCurrentPage +// +//---------------------------------------------------------------------------- + +HRESULT CCandidateWindow::_GetCurrentPage(_Inout_ UINT *pCurrentPage) +{ + HRESULT hr = S_OK; + + if (pCurrentPage == nullptr) + { + hr = E_INVALIDARG; + goto Exit; + } + + *pCurrentPage = 0; + + if (_PageIndex.Count() == 0) + { + hr = E_UNEXPECTED; + goto Exit; + } + + if (_PageIndex.Count() == 1) + { + *pCurrentPage = 0; + goto Exit; + } + + UINT i = 0; + for (i = 1; i < _PageIndex.Count(); i++) + { + UINT uPageIndex = *_PageIndex.GetAt(i); + + if (uPageIndex > _currentSelection) + { + break; + } + } + + *pCurrentPage = i - 1; + +Exit: + return hr; +} + +//+--------------------------------------------------------------------------- +// +// _GetCurrentPage +// +//---------------------------------------------------------------------------- + +HRESULT CCandidateWindow::_GetCurrentPage(_Inout_ int *pCurrentPage) +{ + HRESULT hr = E_FAIL; + UINT needCastCurrentPage = 0; + + if (nullptr == pCurrentPage) + { + goto Exit; + } + + *pCurrentPage = 0; + + hr = _GetCurrentPage(&needCastCurrentPage); + if (FAILED(hr)) + { + goto Exit; + } + + hr = UIntToInt(needCastCurrentPage, pCurrentPage); + if (FAILED(hr)) + { + goto Exit; + } + +Exit: + return hr; +} + +//+--------------------------------------------------------------------------- +// +// _AdjustPageIndexForSelection +// +//---------------------------------------------------------------------------- + +BOOL CCandidateWindow::_AdjustPageIndexForSelection() +{ + UINT candidateListPageCnt = _pIndexRange->Count(); + UINT* pNewPageIndex = nullptr; + UINT newPageCnt = 0; + + if (_candidateList.Count() < candidateListPageCnt) + { + // no needed to restruct page index + return TRUE; + } + + // B is number of pages before the current page + // A is number of pages after the current page + // uNewPageCount is A + B + 1; + // A is (uItemsAfter - 1) / candidateListPageCnt + 1 -> + // (_CandidateListCount - _currentSelection - CandidateListPageCount - 1) / candidateListPageCnt + 1-> + // (_CandidateListCount - _currentSelection - 1) / candidateListPageCnt + // B is (uItemsBefore - 1) / candidateListPageCnt + 1 -> + // (_currentSelection - 1) / candidateListPageCnt + 1 + // A + B is (_CandidateListCount - 2) / candidateListPageCnt + 1 + + BOOL isBefore = _currentSelection; + BOOL isAfter = _candidateList.Count() > _currentSelection + candidateListPageCnt; + + // only have current page + if (!isBefore && !isAfter) + { + newPageCnt = 1; + } + // only have after pages; just count the total number of pages + else if (!isBefore && isAfter) + { + newPageCnt = (_candidateList.Count() - 1) / candidateListPageCnt + 1; + } + // we are at the last page + else if (isBefore && !isAfter) + { + newPageCnt = 2 + (_currentSelection - 1) / candidateListPageCnt; + } + else if (isBefore && isAfter) + { + newPageCnt = (_candidateList.Count() - 2) / candidateListPageCnt + 2; + } + + pNewPageIndex = new (std::nothrow) UINT[ newPageCnt ]; + if (pNewPageIndex == nullptr) + { + return FALSE; + } + pNewPageIndex[0] = 0; + UINT firstPage = _currentSelection % candidateListPageCnt; + if (firstPage && newPageCnt > 1) + { + pNewPageIndex[1] = firstPage; + } + + for (UINT i = firstPage ? 2 : 1; i < newPageCnt; ++i) + { + pNewPageIndex[i] = pNewPageIndex[i - 1] + candidateListPageCnt; + } + + _SetPageIndex(pNewPageIndex, newPageCnt); + + delete [] pNewPageIndex; + + return TRUE; +} + +//+--------------------------------------------------------------------------- +// +// _AdjustTextColor +// +//---------------------------------------------------------------------------- + +COLORREF _AdjustTextColor(_In_ COLORREF crColor, _In_ COLORREF crBkColor) +{ + if (!Global::IsTooSimilar(crColor, crBkColor)) + { + return crColor; + } + else + { + return crColor ^ RGB(255, 255, 255); + } +} + +//+--------------------------------------------------------------------------- +// +// _CurrentPageHasEmptyItems +// +//---------------------------------------------------------------------------- + +HRESULT CCandidateWindow::_CurrentPageHasEmptyItems(_Inout_ BOOL *hasEmptyItems) +{ + int candidateListPageCnt = _pIndexRange->Count(); + UINT currentPage = 0; + + if (FAILED(_GetCurrentPage(¤tPage))) + { + return S_FALSE; + } + + if ((currentPage == 0 || currentPage == _PageIndex.Count()-1) && + (_PageIndex.Count() > 0) && + (*_PageIndex.GetAt(currentPage) > (UINT)(_candidateList.Count() - candidateListPageCnt))) + { + *hasEmptyItems = TRUE; + } + else + { + *hasEmptyItems = FALSE; + } + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// _FireMessageToLightDismiss +// fire EVENT_OBJECT_IME_xxx to let LightDismiss know about IME window. +//---------------------------------------------------------------------------- + +void CCandidateWindow::_FireMessageToLightDismiss(_In_ HWND wndHandle, _In_ WINDOWPOS *pWndPos) +{ + if (nullptr == pWndPos) + { + return; + } + + BOOL isShowWnd = ((pWndPos->flags & SWP_SHOWWINDOW) != 0); + BOOL isHide = ((pWndPos->flags & SWP_HIDEWINDOW) != 0); + BOOL needResize = ((pWndPos->flags & SWP_NOSIZE) == 0); + BOOL needMove = ((pWndPos->flags & SWP_NOMOVE) == 0); + BOOL needRedraw = ((pWndPos->flags & SWP_NOREDRAW) == 0); + + if (isShowWnd) + { + NotifyWinEvent(EVENT_OBJECT_IME_SHOW, wndHandle, OBJID_CLIENT, CHILDID_SELF); + } + else if (isHide) + { + NotifyWinEvent(EVENT_OBJECT_IME_HIDE, wndHandle, OBJID_CLIENT, CHILDID_SELF); + } + else if (needResize || needMove || needRedraw) + { + if (IsWindowVisible(wndHandle)) + { + NotifyWinEvent(EVENT_OBJECT_IME_CHANGE, wndHandle, OBJID_CLIENT, CHILDID_SELF); + } + } + +} + +HRESULT CCandidateWindow::_AdjustPageIndex(_Inout_ UINT & currentPage, _Inout_ UINT & currentPageIndex) +{ + HRESULT hr = E_FAIL; + UINT candidateListPageCnt = _pIndexRange->Count(); + + currentPageIndex = *_PageIndex.GetAt(currentPage); + + BOOL hasEmptyItems = FALSE; + if (FAILED(_CurrentPageHasEmptyItems(&hasEmptyItems))) + { + goto Exit; + } + + if (FALSE == hasEmptyItems) + { + goto Exit; + } + + if (TRUE == _dontAdjustOnEmptyItemPage) + { + goto Exit; + } + + UINT tempSelection = _currentSelection; + + // Last page + UINT candNum = _candidateList.Count(); + UINT pageNum = _PageIndex.Count(); + + if ((currentPageIndex > candNum - candidateListPageCnt) && (pageNum > 0) && (currentPage == (pageNum - 1))) + { + _currentSelection = candNum - candidateListPageCnt; + + _AdjustPageIndexForSelection(); + + _currentSelection = tempSelection; + + if (FAILED(_GetCurrentPage(¤tPage))) + { + goto Exit; + } + + currentPageIndex = *_PageIndex.GetAt(currentPage); + } + // First page + else if ((currentPageIndex < candidateListPageCnt) && (currentPage == 0)) + { + _currentSelection = 0; + + _AdjustPageIndexForSelection(); + + _currentSelection = tempSelection; + } + + _dontAdjustOnEmptyItemPage = FALSE; + hr = S_OK; + +Exit: + return hr; +} +void CCandidateWindow::_DeleteShadowWnd() +{ + if (nullptr != _pShadowWnd) + { + delete _pShadowWnd; + _pShadowWnd = nullptr; + } +} + +void CCandidateWindow::_DeleteVScrollBarWnd() +{ + if (nullptr != _pVScrollBarWnd) + { + delete _pVScrollBarWnd; + _pVScrollBarWnd = nullptr; + } +} \ No newline at end of file diff --git a/CandidateWindow.h b/CandidateWindow.h new file mode 100644 index 00000000..a7c8f274 --- /dev/null +++ b/CandidateWindow.h @@ -0,0 +1,118 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + + +#pragma once + +#include "private.h" +#include "BaseWindow.h" +#include "ShadowWindow.h" +#include "ScrollBarWindow.h" +#include "SampleIMEBaseStructure.h" + +enum CANDWND_ACTION +{ + CAND_ITEM_SELECT +}; + +typedef HRESULT (*CANDWNDCALLBACK)(void *pv, enum CANDWND_ACTION action); + +class CCandidateWindow : public CBaseWindow +{ +public: + CCandidateWindow(_In_ CANDWNDCALLBACK pfnCallback, _In_ void *pv, _In_ CCandidateRange *pIndexRange, _In_ BOOL isStoreAppMode); + virtual ~CCandidateWindow(); + + BOOL _Create(ATOM atom, _In_ UINT wndWidth, _In_opt_ HWND parentWndHandle); + + void _Move(int x, int y); + void _Show(BOOL isShowWnd); + + VOID _SetTextColor(_In_ COLORREF crColor, _In_ COLORREF crBkColor); + VOID _SetFillColor(_In_ HBRUSH hBrush); + + LRESULT CALLBACK _WindowProcCallback(_In_ HWND wndHandle, UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam); + void _OnPaint(_In_ HDC dcHandle, _In_ PAINTSTRUCT *pps); + void _OnLButtonDown(POINT pt); + void _OnLButtonUp(POINT pt); + void _OnMouseMove(POINT pt); + void _OnVScroll(DWORD dwSB, _In_ DWORD nPos); + + void _AddString(_Inout_ CCandidateListItem *pCandidateItem, _In_ BOOL isAddFindKeyCode); + void _ClearList(); + UINT _GetCount() + { + return _candidateList.Count(); + } + UINT _GetSelection() + { + return _currentSelection; + } + void _SetScrollInfo(_In_ int nMax, _In_ int nPage); + + DWORD _GetCandidateString(_In_ int iIndex, _Outptr_result_maybenull_z_ const WCHAR **ppwchCandidateString); + DWORD _GetSelectedCandidateString(_Outptr_result_maybenull_ const WCHAR **ppwchCandidateString); + + BOOL _MoveSelection(_In_ int offSet, _In_ BOOL isNotify); + BOOL _SetSelection(_In_ int iPage, _In_ BOOL isNotify); + void _SetSelection(_In_ int nIndex); + BOOL _MovePage(_In_ int offSet, _In_ BOOL isNotify); + BOOL _SetSelectionInPage(int nPos); + + HRESULT _GetPageIndex(UINT *pIndex, _In_ UINT uSize, _Inout_ UINT *puPageCnt); + HRESULT _SetPageIndex(UINT *pIndex, _In_ UINT uPageCnt); + HRESULT _GetCurrentPage(_Inout_ UINT *pCurrentPage); + HRESULT _GetCurrentPage(_Inout_ int *pCurrentPage); + +private: + void _HandleMouseMsg(_In_ UINT mouseMsg, _In_ POINT point); + void _DrawList(_In_ HDC dcHandle, _In_ UINT iIndex, _In_ RECT *prc); + void _DrawBorder(_In_ HWND wndHandle, _In_ int cx); + BOOL _SetSelectionOffset(_In_ int offSet); + BOOL _AdjustPageIndexForSelection(); + HRESULT _CurrentPageHasEmptyItems(_Inout_ BOOL *pfHasEmptyItems); + + // LightDismiss feature support, it will fire messages lightdismiss-related to the light dismiss layout. + void _FireMessageToLightDismiss(_In_ HWND wndHandle, _In_ WINDOWPOS *pWndPos); + + BOOL _CreateMainWindow(ATOM atom, _In_opt_ HWND parentWndHandle); + BOOL _CreateBackGroundShadowWindow(); + BOOL _CreateVScrollWindow(); + + HRESULT _AdjustPageIndex(_Inout_ UINT & currentPage, _Inout_ UINT & currentPageIndex); + + void _ResizeWindow(); + void _DeleteShadowWnd(); + void _DeleteVScrollBarWnd(); + + friend COLORREF _AdjustTextColor(_In_ COLORREF crColor, _In_ COLORREF crBkColor); + +private: + UINT _currentSelection; + CSampleImeArray _candidateList; + CSampleImeArray _PageIndex; + + COLORREF _crTextColor; + COLORREF _crBkColor; + HBRUSH _brshBkColor; + + TEXTMETRIC _TextMetric; + int _cyRow; + int _cxTitle; + UINT _wndWidth; + + CCandidateRange* _pIndexRange; + + CANDWNDCALLBACK _pfnCallback; + void* _pObj; + + CShadowWindow* _pShadowWnd; + CScrollBarWindow* _pVScrollBarWnd; + + BOOL _dontAdjustOnEmptyItemPage; + BOOL _isStoreAppMode; +}; diff --git a/Compartment.cpp b/Compartment.cpp new file mode 100644 index 00000000..46595cb1 --- /dev/null +++ b/Compartment.cpp @@ -0,0 +1,343 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#include "Private.h" +#include "Compartment.h" +#include "Globals.h" + +////////////////////////////////////////////////////////////////////// +// +// CCompartment +// +////////////////////////////////////////////////////////////////////// + +//+--------------------------------------------------------------------------- +// ctor +//---------------------------------------------------------------------------- + +CCompartment::CCompartment(_In_ IUnknown* punk, TfClientId tfClientId, _In_ REFGUID guidCompartment) +{ + _guidCompartment = guidCompartment; + + _punk = punk; + _punk->AddRef(); + + _tfClientId = tfClientId; +} + +//+--------------------------------------------------------------------------- +// dtor +//---------------------------------------------------------------------------- + +CCompartment::~CCompartment() +{ + _punk->Release(); +} + +//+--------------------------------------------------------------------------- +// _GetCompartment +//---------------------------------------------------------------------------- + +HRESULT CCompartment::_GetCompartment(_Outptr_ ITfCompartment **ppCompartment) +{ + HRESULT hr = S_OK; + ITfCompartmentMgr* pCompartmentMgr = nullptr; + + hr = _punk->QueryInterface(IID_ITfCompartmentMgr, (void **)&pCompartmentMgr); + if (SUCCEEDED(hr)) + { + hr = pCompartmentMgr->GetCompartment(_guidCompartment, ppCompartment); + pCompartmentMgr->Release(); + } + + return hr; +} + +//+--------------------------------------------------------------------------- +// _GetCompartmentBOOL +//---------------------------------------------------------------------------- + +HRESULT CCompartment::_GetCompartmentBOOL(_Out_ BOOL &flag) +{ + HRESULT hr = S_OK; + ITfCompartment* pCompartment = nullptr; + flag = FALSE; + + if ((hr = _GetCompartment(&pCompartment)) == S_OK) + { + VARIANT var; + if ((hr = pCompartment->GetValue(&var)) == S_OK) + { + if (var.vt == VT_I4) // Even VT_EMPTY, GetValue() can succeed + { + flag = (BOOL)var.lVal; + } + else + { + hr = S_FALSE; + } + } + pCompartment->Release(); + } + + return hr; +} + +//+--------------------------------------------------------------------------- +// _SetCompartmentBOOL +//---------------------------------------------------------------------------- + +HRESULT CCompartment::_SetCompartmentBOOL(_In_ BOOL flag) +{ + HRESULT hr = S_OK; + ITfCompartment* pCompartment = nullptr; + + hr = _GetCompartment(&pCompartment); + if (SUCCEEDED(hr)) + { + VARIANT var; + var.vt = VT_I4; + var.lVal = flag; + hr = pCompartment->SetValue(_tfClientId, &var); + pCompartment->Release(); + } + + return hr; +} + +//+--------------------------------------------------------------------------- +// _GetCompartmentDWORD +//---------------------------------------------------------------------------- + +HRESULT CCompartment::_GetCompartmentDWORD(_Out_ DWORD &dw) +{ + HRESULT hr = S_OK; + ITfCompartment* pCompartment = nullptr; + dw = 0; + + hr = _GetCompartment(&pCompartment); + if (SUCCEEDED(hr)) + { + VARIANT var; + if ((hr = pCompartment->GetValue(&var)) == S_OK) + { + if (var.vt == VT_I4) // Even VT_EMPTY, GetValue() can succeed + { + dw = (DWORD)var.lVal; + } + else + { + hr = S_FALSE; + } + } + pCompartment->Release(); + } + + return hr; +} + +//+--------------------------------------------------------------------------- +// _SetCompartmentDWORD +//---------------------------------------------------------------------------- + +HRESULT CCompartment::_SetCompartmentDWORD(_In_ DWORD dw) +{ + HRESULT hr = S_OK; + ITfCompartment* pCompartment = nullptr; + + hr = _GetCompartment(&pCompartment); + if (SUCCEEDED(hr)) + { + VARIANT var; + var.vt = VT_I4; + var.lVal = dw; + hr = pCompartment->SetValue(_tfClientId, &var); + pCompartment->Release(); + } + + return hr; +} + +//+--------------------------------------------------------------------------- +// +// _ClearCompartment +// +//---------------------------------------------------------------------------- + +HRESULT CCompartment::_ClearCompartment() +{ + if (IsEqualGUID(_guidCompartment, GUID_COMPARTMENT_KEYBOARD_OPENCLOSE)) + { + return S_FALSE; + } + + HRESULT hr = S_OK; + ITfCompartmentMgr* pCompartmentMgr = nullptr; + + if ((hr = _punk->QueryInterface(IID_ITfCompartmentMgr, (void **)&pCompartmentMgr)) == S_OK) + { + hr = pCompartmentMgr->ClearCompartment(_tfClientId, _guidCompartment); + pCompartmentMgr->Release(); + } + + return hr; +} + +////////////////////////////////////////////////////////////////////// +// +// CCompartmentEventSink +// +////////////////////////////////////////////////////////////////////// + +//+--------------------------------------------------------------------------- +// ctor +//---------------------------------------------------------------------------- + +CCompartmentEventSink::CCompartmentEventSink(_In_ CESCALLBACK pfnCallback, _In_ void *pv) +{ + _pfnCallback = pfnCallback; + _pv = pv; + _refCount = 1; +} + +//+--------------------------------------------------------------------------- +// dtor +//---------------------------------------------------------------------------- + +CCompartmentEventSink::~CCompartmentEventSink() +{ +} + +//+--------------------------------------------------------------------------- +// +// QueryInterface +// +//---------------------------------------------------------------------------- + +STDAPI CCompartmentEventSink::QueryInterface(REFIID riid, _Outptr_ void **ppvObj) +{ + if (ppvObj == nullptr) + return E_INVALIDARG; + + *ppvObj = nullptr; + + if (IsEqualIID(riid, IID_IUnknown) || + IsEqualIID(riid, IID_ITfCompartmentEventSink)) + { + *ppvObj = (CCompartmentEventSink *)this; + } + + if (*ppvObj) + { + AddRef(); + return S_OK; + } + + return E_NOINTERFACE; +} + + +//+--------------------------------------------------------------------------- +// +// AddRef +// +//---------------------------------------------------------------------------- + +STDAPI_(ULONG) CCompartmentEventSink::AddRef() +{ + return ++_refCount; +} + +//+--------------------------------------------------------------------------- +// +// Release +// +//---------------------------------------------------------------------------- + +STDAPI_(ULONG) CCompartmentEventSink::Release() +{ + LONG cr = --_refCount; + + assert(_refCount >= 0); + + if (_refCount == 0) + { + delete this; + } + + return cr; +} + +//+--------------------------------------------------------------------------- +// +// OnChange +// +//---------------------------------------------------------------------------- + +STDAPI CCompartmentEventSink::OnChange(_In_ REFGUID guidCompartment) +{ + return _pfnCallback(_pv, guidCompartment); +} + +//+--------------------------------------------------------------------------- +// +// _Advise +// +//---------------------------------------------------------------------------- + +HRESULT CCompartmentEventSink::_Advise(_In_ IUnknown *punk, _In_ REFGUID guidCompartment) +{ + HRESULT hr = S_OK; + ITfCompartmentMgr* pCompartmentMgr = nullptr; + ITfSource* pSource = nullptr; + + hr = punk->QueryInterface(IID_ITfCompartmentMgr, (void **)&pCompartmentMgr); + if (FAILED(hr)) + { + return hr; + } + + hr = pCompartmentMgr->GetCompartment(guidCompartment, &_pCompartment); + if (SUCCEEDED(hr)) + { + hr = _pCompartment->QueryInterface(IID_ITfSource, (void **)&pSource); + if (SUCCEEDED(hr)) + { + hr = pSource->AdviseSink(IID_ITfCompartmentEventSink, this, &_dwCookie); + pSource->Release(); + } + } + + pCompartmentMgr->Release(); + + return hr; +} + +//+--------------------------------------------------------------------------- +// +// _Unadvise +// +//---------------------------------------------------------------------------- + +HRESULT CCompartmentEventSink::_Unadvise() +{ + HRESULT hr = S_OK; + ITfSource* pSource = nullptr; + + hr = _pCompartment->QueryInterface(IID_ITfSource, (void **)&pSource); + if (SUCCEEDED(hr)) + { + hr = pSource->UnadviseSink(_dwCookie); + pSource->Release(); + } + + _pCompartment->Release(); + _pCompartment = nullptr; + _dwCookie = 0; + + return hr; +} diff --git a/Compartment.h b/Compartment.h new file mode 100644 index 00000000..9fde0698 --- /dev/null +++ b/Compartment.h @@ -0,0 +1,61 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#pragma once + +class CCompartment +{ +public: + CCompartment(_In_ IUnknown *punk, TfClientId tfClientId, _In_ REFGUID guidCompartment); + ~CCompartment(); + + HRESULT _GetCompartment(_Outptr_ ITfCompartment **ppCompartment); + HRESULT _GetCompartmentBOOL(_Out_ BOOL &flag); + HRESULT _SetCompartmentBOOL(_In_ BOOL flag); + HRESULT _GetCompartmentDWORD(_Out_ DWORD &dw); + HRESULT _SetCompartmentDWORD(_In_ DWORD dw); + HRESULT _ClearCompartment(); + + VOID _GetGUID(GUID *pguid) + { + *pguid = _guidCompartment; + } + +private: + GUID _guidCompartment; + IUnknown* _punk; + TfClientId _tfClientId; +}; + +typedef HRESULT (*CESCALLBACK)(void *pv, REFGUID guidCompartment); + +class CCompartmentEventSink : public ITfCompartmentEventSink +{ +public: + CCompartmentEventSink(_In_ CESCALLBACK pfnCallback, _In_ void *pv); + ~CCompartmentEventSink(); + + // IUnknown + STDMETHODIMP QueryInterface(REFIID riid, _Outptr_ void **ppvObj); + STDMETHODIMP_(ULONG) AddRef(void); + STDMETHODIMP_(ULONG) Release(void); + + // ITfCompartmentEventSink + STDMETHODIMP OnChange(_In_ REFGUID guid); + + // function + HRESULT _Advise(_In_ IUnknown *punk, _In_ REFGUID guidCompartment); + HRESULT _Unadvise(); + +private: + ITfCompartment *_pCompartment; + DWORD _dwCookie; + CESCALLBACK _pfnCallback; + void *_pv; + + LONG _refCount; +}; diff --git a/Composition.cpp b/Composition.cpp new file mode 100644 index 00000000..9b9ac253 --- /dev/null +++ b/Composition.cpp @@ -0,0 +1,377 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#include "Private.h" +#include "Globals.h" +#include "SampleIME.h" +#include "CompositionProcessorEngine.h" + +//+--------------------------------------------------------------------------- +// +// ITfCompositionSink::OnCompositionTerminated +// +// Callback for ITfCompositionSink. The system calls this method whenever +// someone other than this service ends a composition. +//---------------------------------------------------------------------------- + +STDAPI CSampleIME::OnCompositionTerminated(TfEditCookie ecWrite, _In_ ITfComposition *pComposition) +{ + // Clear dummy composition + _RemoveDummyCompositionForComposing(ecWrite, pComposition); + + // Clear display attribute and end composition, _EndComposition will release composition for us + ITfContext* pContext = _pContext; + if (pContext) + { + pContext->AddRef(); + } + + _EndComposition(_pContext); + + _DeleteCandidateList(FALSE, pContext); + + if (pContext) + { + pContext->Release(); + pContext = nullptr; + } + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// _IsComposing +// +//---------------------------------------------------------------------------- + +BOOL CSampleIME::_IsComposing() +{ + return _pComposition != nullptr; +} + +//+--------------------------------------------------------------------------- +// +// _SetComposition +// +//---------------------------------------------------------------------------- + +void CSampleIME::_SetComposition(_In_ ITfComposition *pComposition) +{ + _pComposition = pComposition; +} + +//+--------------------------------------------------------------------------- +// +// _AddComposingAndChar +// +//---------------------------------------------------------------------------- + +HRESULT CSampleIME::_AddComposingAndChar(TfEditCookie ec, _In_ ITfContext *pContext, _In_ CStringRange *pstrAddString) +{ + HRESULT hr = S_OK; + + ULONG fetched = 0; + TF_SELECTION tfSelection; + + if (pContext->GetSelection(ec, TF_DEFAULT_SELECTION, 1, &tfSelection, &fetched) != S_OK || fetched == 0) + return S_FALSE; + + // + // make range start to selection + // + ITfRange* pAheadSelection = nullptr; + hr = pContext->GetStart(ec, &pAheadSelection); + if (SUCCEEDED(hr)) + { + hr = pAheadSelection->ShiftEndToRange(ec, tfSelection.range, TF_ANCHOR_START); + if (SUCCEEDED(hr)) + { + ITfRange* pRange = nullptr; + BOOL exist_composing = _FindComposingRange(ec, pContext, pAheadSelection, &pRange); + + _SetInputString(ec, pContext, pRange, pstrAddString, exist_composing); + + if (pRange) + { + pRange->Release(); + } + } + } + + tfSelection.range->Release(); + + if (pAheadSelection) + { + pAheadSelection->Release(); + } + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// _AddCharAndFinalize +// +//---------------------------------------------------------------------------- + +HRESULT CSampleIME::_AddCharAndFinalize(TfEditCookie ec, _In_ ITfContext *pContext, _In_ CStringRange *pstrAddString) +{ + HRESULT hr = E_FAIL; + + ULONG fetched = 0; + TF_SELECTION tfSelection; + + if ((hr = pContext->GetSelection(ec, TF_DEFAULT_SELECTION, 1, &tfSelection, &fetched)) != S_OK || fetched != 1) + return hr; + + // we use SetText here instead of InsertTextAtSelection because we've already started a composition + // we don't want to the app to adjust the insertion point inside our composition + hr = tfSelection.range->SetText(ec, 0, pstrAddString->Get(), (LONG)pstrAddString->GetLength()); + if (hr == S_OK) + { + // update the selection, we'll make it an insertion point just past + // the inserted text. + tfSelection.range->Collapse(ec, TF_ANCHOR_END); + pContext->SetSelection(ec, 1, &tfSelection); + } + + tfSelection.range->Release(); + + return hr; +} + +//+--------------------------------------------------------------------------- +// +// _FindComposingRange +// +//---------------------------------------------------------------------------- + +BOOL CSampleIME::_FindComposingRange(TfEditCookie ec, _In_ ITfContext *pContext, _In_ ITfRange *pSelection, _Outptr_result_maybenull_ ITfRange **ppRange) +{ + if (ppRange == nullptr) + { + return FALSE; + } + + *ppRange = nullptr; + + // find GUID_PROP_COMPOSING + ITfProperty* pPropComp = nullptr; + IEnumTfRanges* enumComp = nullptr; + + HRESULT hr = pContext->GetProperty(GUID_PROP_COMPOSING, &pPropComp); + if (FAILED(hr) || pPropComp == nullptr) + { + return FALSE; + } + + hr = pPropComp->EnumRanges(ec, &enumComp, pSelection); + if (FAILED(hr) || enumComp == nullptr) + { + pPropComp->Release(); + return FALSE; + } + + BOOL isCompExist = FALSE; + VARIANT var; + ULONG fetched = 0; + + while (enumComp->Next(1, ppRange, &fetched) == S_OK && fetched == 1) + { + hr = pPropComp->GetValue(ec, *ppRange, &var); + if (hr == S_OK) + { + if (var.vt == VT_I4 && var.lVal != 0) + { + isCompExist = TRUE; + break; + } + } + (*ppRange)->Release(); + *ppRange = nullptr; + } + + pPropComp->Release(); + enumComp->Release(); + + return isCompExist; +} + +//+--------------------------------------------------------------------------- +// +// _SetInputString +// +//---------------------------------------------------------------------------- + +HRESULT CSampleIME::_SetInputString(TfEditCookie ec, _In_ ITfContext *pContext, _Out_opt_ ITfRange *pRange, _In_ CStringRange *pstrAddString, BOOL exist_composing) +{ + ITfRange* pRangeInsert = nullptr; + if (!exist_composing) + { + _InsertAtSelection(ec, pContext, pstrAddString, &pRangeInsert); + if (pRangeInsert == nullptr) + { + return S_OK; + } + pRange = pRangeInsert; + } + if (pRange != nullptr) + { + pRange->SetText(ec, 0, pstrAddString->Get(), (LONG)pstrAddString->GetLength()); + } + + _SetCompositionLanguage(ec, pContext); + + _SetCompositionDisplayAttributes(ec, pContext, _gaDisplayAttributeInput); + + // update the selection, we'll make it an insertion point just past + // the inserted text. + ITfRange* pSelection = nullptr; + TF_SELECTION sel; + + if ((pRange != nullptr) && (pRange->Clone(&pSelection) == S_OK)) + { + pSelection->Collapse(ec, TF_ANCHOR_END); + + sel.range = pSelection; + sel.style.ase = TF_AE_NONE; + sel.style.fInterimChar = FALSE; + pContext->SetSelection(ec, 1, &sel); + pSelection->Release(); + } + + if (pRangeInsert) + { + pRangeInsert->Release(); + } + + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// _InsertAtSelection +// +//---------------------------------------------------------------------------- + +HRESULT CSampleIME::_InsertAtSelection(TfEditCookie ec, _In_ ITfContext *pContext, _In_ CStringRange *pstrAddString, _Outptr_ ITfRange **ppCompRange) +{ + ITfRange* rangeInsert = nullptr; + ITfInsertAtSelection* pias = nullptr; + HRESULT hr = S_OK; + + if (ppCompRange == nullptr) + { + hr = E_INVALIDARG; + goto Exit; + } + + *ppCompRange = nullptr; + + hr = pContext->QueryInterface(IID_ITfInsertAtSelection, (void **)&pias); + if (FAILED(hr)) + { + goto Exit; + } + + hr = pias->InsertTextAtSelection(ec, TF_IAS_QUERYONLY, pstrAddString->Get(), (LONG)pstrAddString->GetLength(), &rangeInsert); + + if ( FAILED(hr) || rangeInsert == nullptr) + { + rangeInsert = nullptr; + pias->Release(); + goto Exit; + } + + *ppCompRange = rangeInsert; + pias->Release(); + hr = S_OK; + +Exit: + return hr; +} + +//+--------------------------------------------------------------------------- +// +// _RemoveDummyCompositionForComposing +// +//---------------------------------------------------------------------------- + +HRESULT CSampleIME::_RemoveDummyCompositionForComposing(TfEditCookie ec, _In_ ITfComposition *pComposition) +{ + HRESULT hr = S_OK; + + ITfRange* pRange = nullptr; + + if (pComposition) + { + hr = pComposition->GetRange(&pRange); + if (SUCCEEDED(hr)) + { + pRange->SetText(ec, 0, nullptr, 0); + pRange->Release(); + } + } + + return hr; +} + +//+--------------------------------------------------------------------------- +// +// _SetCompositionLanguage +// +//---------------------------------------------------------------------------- + +BOOL CSampleIME::_SetCompositionLanguage(TfEditCookie ec, _In_ ITfContext *pContext) +{ + HRESULT hr = S_OK; + BOOL ret = TRUE; + + CCompositionProcessorEngine* pCompositionProcessorEngine = nullptr; + pCompositionProcessorEngine = _pCompositionProcessorEngine; + + LANGID langidProfile = 0; + pCompositionProcessorEngine->GetLanguageProfile(&langidProfile); + + ITfRange* pRangeComposition = nullptr; + ITfProperty* pLanguageProperty = nullptr; + + // we need a range and the context it lives in + hr = _pComposition->GetRange(&pRangeComposition); + if (FAILED(hr)) + { + ret = FALSE; + goto Exit; + } + + // get our the language property + hr = pContext->GetProperty(GUID_PROP_LANGID, &pLanguageProperty); + if (FAILED(hr)) + { + ret = FALSE; + goto Exit; + } + + VARIANT var; + var.vt = VT_I4; // we're going to set DWORD + var.lVal = langidProfile; + + hr = pLanguageProperty->SetValue(ec, pRangeComposition, &var); + if (FAILED(hr)) + { + ret = FALSE; + goto Exit; + } + + pLanguageProperty->Release(); + pRangeComposition->Release(); + +Exit: + return ret; +} diff --git a/CompositionProcessorEngine.cpp b/CompositionProcessorEngine.cpp new file mode 100644 index 00000000..2133dc6c --- /dev/null +++ b/CompositionProcessorEngine.cpp @@ -0,0 +1,1940 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#include "Private.h" +#include "SampleIME.h" +#include "CompositionProcessorEngine.h" +#include "TableDictionaryEngine.h" +#include "DictionarySearch.h" +#include "TfInputProcessorProfile.h" +#include "Globals.h" +#include "Compartment.h" +#include "LanguageBar.h" +#include "RegKey.h" + +////////////////////////////////////////////////////////////////////// +// +// CSampleIME implementation. +// +////////////////////////////////////////////////////////////////////// + +//+--------------------------------------------------------------------------- +// +// _AddTextProcessorEngine +// +//---------------------------------------------------------------------------- + +BOOL CSampleIME::_AddTextProcessorEngine() +{ + LANGID langid = 0; + CLSID clsid = GUID_NULL; + GUID guidProfile = GUID_NULL; + + // Get default profile. + CTfInputProcessorProfile profile; + + if (FAILED(profile.CreateInstance())) + { + return FALSE; + } + + if (FAILED(profile.GetCurrentLanguage(&langid))) + { + return FALSE; + } + + if (FAILED(profile.GetDefaultLanguageProfile(langid, GUID_TFCAT_TIP_KEYBOARD, &clsid, &guidProfile))) + { + return FALSE; + } + + // Is this already added? + if (_pCompositionProcessorEngine != nullptr) + { + LANGID langidProfile = 0; + GUID guidLanguageProfile = GUID_NULL; + + guidLanguageProfile = _pCompositionProcessorEngine->GetLanguageProfile(&langidProfile); + if ((langid == langidProfile) && IsEqualGUID(guidProfile, guidLanguageProfile)) + { + return TRUE; + } + } + + // Create composition processor engine + if (_pCompositionProcessorEngine == nullptr) + { + _pCompositionProcessorEngine = new (std::nothrow) CCompositionProcessorEngine(); + } + if (!_pCompositionProcessorEngine) + { + return FALSE; + } + + // setup composition processor engine + if (FALSE == _pCompositionProcessorEngine->SetupLanguageProfile(langid, guidProfile, _GetThreadMgr(), _GetClientId(), _IsSecureMode(), _IsComLess())) + { + return FALSE; + } + + return TRUE; +} + +////////////////////////////////////////////////////////////////////// +// +// CompositionProcessorEngine implementation. +// +////////////////////////////////////////////////////////////////////// + +//+--------------------------------------------------------------------------- +// +// ctor +// +//---------------------------------------------------------------------------- + +CCompositionProcessorEngine::CCompositionProcessorEngine() +{ + _pTableDictionaryEngine = nullptr; + _pDictionaryFile = nullptr; + + _langid = 0xffff; + _guidProfile = GUID_NULL; + _tfClientId = TF_CLIENTID_NULL; + + _pLanguageBar_IMEMode = nullptr; + _pLanguageBar_DoubleSingleByte = nullptr; + _pLanguageBar_Punctuation = nullptr; + + _pCompartmentConversion = nullptr; + _pCompartmentKeyboardOpenEventSink = nullptr; + _pCompartmentConversionEventSink = nullptr; + _pCompartmentDoubleSingleByteEventSink = nullptr; + _pCompartmentPunctuationEventSink = nullptr; + + _hasWildcardIncludedInKeystrokeBuffer = FALSE; + + _isWildcard = FALSE; + _isDisableWildcardAtFirst = FALSE; + _hasMakePhraseFromText = FALSE; + _isKeystrokeSort = FALSE; + + _candidateListPhraseModifier = 0; + + _candidateWndWidth = CAND_WIDTH; + + InitKeyStrokeTable(); +} + +//+--------------------------------------------------------------------------- +// +// dtor +// +//---------------------------------------------------------------------------- + +CCompositionProcessorEngine::~CCompositionProcessorEngine() +{ + if (_pTableDictionaryEngine) + { + delete _pTableDictionaryEngine; + _pTableDictionaryEngine = nullptr; + } + + if (_pLanguageBar_IMEMode) + { + _pLanguageBar_IMEMode->CleanUp(); + _pLanguageBar_IMEMode->Release(); + _pLanguageBar_IMEMode = nullptr; + } + if (_pLanguageBar_DoubleSingleByte) + { + _pLanguageBar_DoubleSingleByte->CleanUp(); + _pLanguageBar_DoubleSingleByte->Release(); + _pLanguageBar_DoubleSingleByte = nullptr; + } + if (_pLanguageBar_Punctuation) + { + _pLanguageBar_Punctuation->CleanUp(); + _pLanguageBar_Punctuation->Release(); + _pLanguageBar_Punctuation = nullptr; + } + + if (_pCompartmentConversion) + { + delete _pCompartmentConversion; + _pCompartmentConversion = nullptr; + } + if (_pCompartmentKeyboardOpenEventSink) + { + _pCompartmentKeyboardOpenEventSink->_Unadvise(); + delete _pCompartmentKeyboardOpenEventSink; + _pCompartmentKeyboardOpenEventSink = nullptr; + } + if (_pCompartmentConversionEventSink) + { + _pCompartmentConversionEventSink->_Unadvise(); + delete _pCompartmentConversionEventSink; + _pCompartmentConversionEventSink = nullptr; + } + if (_pCompartmentDoubleSingleByteEventSink) + { + _pCompartmentDoubleSingleByteEventSink->_Unadvise(); + delete _pCompartmentDoubleSingleByteEventSink; + _pCompartmentDoubleSingleByteEventSink = nullptr; + } + if (_pCompartmentPunctuationEventSink) + { + _pCompartmentPunctuationEventSink->_Unadvise(); + delete _pCompartmentPunctuationEventSink; + _pCompartmentPunctuationEventSink = nullptr; + } + + if (_pDictionaryFile) + { + delete _pDictionaryFile; + _pDictionaryFile = nullptr; + } +} + +//+--------------------------------------------------------------------------- +// +// SetupLanguageProfile +// +// Setup language profile for Composition Processor Engine. +// param +// [in] LANGID langid = Specify language ID +// [in] GUID guidLanguageProfile - Specify GUID language profile which GUID is as same as Text Service Framework language profile. +// [in] ITfThreadMgr - pointer ITfThreadMgr. +// [in] tfClientId - TfClientId value. +// [in] isSecureMode - secure mode +// returns +// If setup succeeded, returns true. Otherwise returns false. +// N.B. For reverse conversion, ITfThreadMgr is NULL, TfClientId is 0 and isSecureMode is ignored. +//+--------------------------------------------------------------------------- + +BOOL CCompositionProcessorEngine::SetupLanguageProfile(LANGID langid, REFGUID guidLanguageProfile, _In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId, BOOL isSecureMode, BOOL isComLessMode) +{ + BOOL ret = TRUE; + if ((tfClientId == 0) && (pThreadMgr == nullptr)) + { + ret = FALSE; + goto Exit; + } + + _isComLessMode = isComLessMode; + _langid = langid; + _guidProfile = guidLanguageProfile; + _tfClientId = tfClientId; + + SetupPreserved(pThreadMgr, tfClientId); + InitializeSampleIMECompartment(pThreadMgr, tfClientId); + SetupPunctuationPair(); + SetupLanguageBar(pThreadMgr, tfClientId, isSecureMode); + SetupKeystroke(); + SetupConfiguration(); + SetupDictionaryFile(); + +Exit: + return ret; +} + +//+--------------------------------------------------------------------------- +// +// AddVirtualKey +// Add virtual key code to Composition Processor Engine for used to parse keystroke data. +// param +// [in] uCode - Specify virtual key code. +// returns +// State of Text Processor Engine. +//---------------------------------------------------------------------------- + +BOOL CCompositionProcessorEngine::AddVirtualKey(WCHAR wch) +{ + if (!wch) + { + return FALSE; + } + + // + // append one keystroke in buffer. + // + DWORD_PTR srgKeystrokeBufLen = _keystrokeBuffer.GetLength(); + PWCHAR pwch = new (std::nothrow) WCHAR[ srgKeystrokeBufLen + 1 ]; + if (!pwch) + { + return FALSE; + } + + memcpy(pwch, _keystrokeBuffer.Get(), srgKeystrokeBufLen * sizeof(WCHAR)); + pwch[ srgKeystrokeBufLen ] = wch; + + if (_keystrokeBuffer.Get()) + { + delete [] _keystrokeBuffer.Get(); + } + + _keystrokeBuffer.Set(pwch, srgKeystrokeBufLen + 1); + + return TRUE; +} + +//+--------------------------------------------------------------------------- +// +// RemoveVirtualKey +// Remove stored virtual key code. +// param +// [in] dwIndex - Specified index. +// returns +// none. +//---------------------------------------------------------------------------- + +void CCompositionProcessorEngine::RemoveVirtualKey(DWORD_PTR dwIndex) +{ + DWORD_PTR srgKeystrokeBufLen = _keystrokeBuffer.GetLength(); + + if (dwIndex + 1 < srgKeystrokeBufLen) + { + // shift following eles left + memmove((BYTE*)_keystrokeBuffer.Get() + (dwIndex * sizeof(WCHAR)), + (BYTE*)_keystrokeBuffer.Get() + ((dwIndex + 1) * sizeof(WCHAR)), + (srgKeystrokeBufLen - dwIndex - 1) * sizeof(WCHAR)); + } + + _keystrokeBuffer.Set(_keystrokeBuffer.Get(), srgKeystrokeBufLen - 1); +} + +//+--------------------------------------------------------------------------- +// +// PurgeVirtualKey +// Purge stored virtual key code. +// param +// none. +// returns +// none. +//---------------------------------------------------------------------------- + +void CCompositionProcessorEngine::PurgeVirtualKey() +{ + if (_keystrokeBuffer.Get()) + { + delete [] _keystrokeBuffer.Get(); + _keystrokeBuffer.Set(NULL, 0); + } +} + +WCHAR CCompositionProcessorEngine::GetVirtualKey(DWORD_PTR dwIndex) +{ + if (dwIndex < _keystrokeBuffer.GetLength()) + { + return *(_keystrokeBuffer.Get() + dwIndex); + } + return 0; +} +//+--------------------------------------------------------------------------- +// +// GetReadingStrings +// Retrieves string from Composition Processor Engine. +// param +// [out] pReadingStrings - Specified returns pointer of CUnicodeString. +// returns +// none +// +//---------------------------------------------------------------------------- + +void CCompositionProcessorEngine::GetReadingStrings(_Inout_ CSampleImeArray *pReadingStrings, _Out_ BOOL *pIsWildcardIncluded) +{ + CStringRange oneKeystroke; + + _hasWildcardIncludedInKeystrokeBuffer = FALSE; + + if (pReadingStrings->Count() == 0 && _keystrokeBuffer.GetLength()) + { + CStringRange* pNewString = nullptr; + + pNewString = pReadingStrings->Append(); + if (pNewString) + { + *pNewString = _keystrokeBuffer; + } + + for (DWORD index = 0; index < _keystrokeBuffer.GetLength(); index++) + { + oneKeystroke.Set(_keystrokeBuffer.Get() + index, 1); + + if (IsWildcard() && IsWildcardChar(*oneKeystroke.Get())) + { + _hasWildcardIncludedInKeystrokeBuffer = TRUE; + } + } + } + + *pIsWildcardIncluded = _hasWildcardIncludedInKeystrokeBuffer; +} + +//+--------------------------------------------------------------------------- +// +// GetCandidateList +// +//---------------------------------------------------------------------------- + +void CCompositionProcessorEngine::GetCandidateList(_Inout_ CSampleImeArray *pCandidateList, BOOL isIncrementalWordSearch, BOOL isWildcardSearch) +{ + if (!IsDictionaryAvailable()) + { + return; + } + + if (isIncrementalWordSearch) + { + CStringRange wildcardSearch; + DWORD_PTR keystrokeBufLen = _keystrokeBuffer.GetLength() + 2; + PWCHAR pwch = new (std::nothrow) WCHAR[ keystrokeBufLen ]; + if (!pwch) + { + return; + } + + // check keystroke buffer already has wildcard char which end user want wildcard serach + DWORD wildcardIndex = 0; + BOOL isFindWildcard = FALSE; + + if (IsWildcard()) + { + for (wildcardIndex = 0; wildcardIndex < _keystrokeBuffer.GetLength(); wildcardIndex++) + { + if (IsWildcardChar(*(_keystrokeBuffer.Get() + wildcardIndex))) + { + isFindWildcard = TRUE; + break; + } + } + } + + StringCchCopyN(pwch, keystrokeBufLen, _keystrokeBuffer.Get(), _keystrokeBuffer.GetLength()); + + if (!isFindWildcard) + { + // add wildcard char for incremental search + StringCchCat(pwch, keystrokeBufLen, L"*"); + } + + size_t len = 0; + if (StringCchLength(pwch, STRSAFE_MAX_CCH, &len) == S_OK) + { + wildcardSearch.Set(pwch, len); + } + else + { + return; + } + + _pTableDictionaryEngine->CollectWordForWildcard(&wildcardSearch, pCandidateList); + + if (0 >= pCandidateList->Count()) + { + return; + } + + if (IsKeystrokeSort()) + { + _pTableDictionaryEngine->SortListItemByFindKeyCode(pCandidateList); + } + + // Incremental search would show keystroke data from all candidate list items + // but wont show identical keystroke data for user inputted. + for (UINT index = 0; index < pCandidateList->Count(); index++) + { + CCandidateListItem *pLI = pCandidateList->GetAt(index); + DWORD_PTR keystrokeBufferLen = 0; + + if (IsWildcard()) + { + keystrokeBufferLen = wildcardIndex; + } + else + { + keystrokeBufferLen = _keystrokeBuffer.GetLength(); + } + + CStringRange newFindKeyCode; + newFindKeyCode.Set(pLI->_FindKeyCode.Get() + keystrokeBufferLen, pLI->_FindKeyCode.GetLength() - keystrokeBufferLen); + pLI->_FindKeyCode.Set(newFindKeyCode); + } + + delete [] pwch; + } + else if (isWildcardSearch) + { + _pTableDictionaryEngine->CollectWordForWildcard(&_keystrokeBuffer, pCandidateList); + } + else + { + _pTableDictionaryEngine->CollectWord(&_keystrokeBuffer, pCandidateList); + } + + for (UINT index = 0; index < pCandidateList->Count();) + { + CCandidateListItem *pLI = pCandidateList->GetAt(index); + CStringRange startItemString; + CStringRange endItemString; + + startItemString.Set(pLI->_ItemString.Get(), 1); + endItemString.Set(pLI->_ItemString.Get() + pLI->_ItemString.GetLength() - 1, 1); + + index++; + } +} + +//+--------------------------------------------------------------------------- +// +// GetCandidateStringInConverted +// +//---------------------------------------------------------------------------- + +void CCompositionProcessorEngine::GetCandidateStringInConverted(CStringRange &searchString, _In_ CSampleImeArray *pCandidateList) +{ + if (!IsDictionaryAvailable()) + { + return; + } + + // Search phrase from SECTION_TEXT's converted string list + CStringRange wildcardSearch; + DWORD_PTR srgKeystrokeBufLen = searchString.GetLength() + 2; + PWCHAR pwch = new (std::nothrow) WCHAR[ srgKeystrokeBufLen ]; + if (!pwch) + { + return; + } + + StringCchCopyN(pwch, srgKeystrokeBufLen, searchString.Get(), searchString.GetLength()); + StringCchCat(pwch, srgKeystrokeBufLen, L"*"); + + // add wildcard char + size_t len = 0; + if (StringCchLength(pwch, STRSAFE_MAX_CCH, &len) != S_OK) + { + return; + } + wildcardSearch.Set(pwch, len); + + _pTableDictionaryEngine->CollectWordFromConvertedStringForWildcard(&wildcardSearch, pCandidateList); + + if (IsKeystrokeSort()) + { + _pTableDictionaryEngine->SortListItemByFindKeyCode(pCandidateList); + } + + wildcardSearch.Clear(); + delete [] pwch; +} + +//+--------------------------------------------------------------------------- +// +// IsPunctuation +// +//---------------------------------------------------------------------------- + +BOOL CCompositionProcessorEngine::IsPunctuation(WCHAR wch) +{ + for (int i = 0; i < ARRAYSIZE(Global::PunctuationTable); i++) + { + if (Global::PunctuationTable[i]._Code == wch) + { + return TRUE; + } + } + + for (UINT j = 0; j < _PunctuationPair.Count(); j++) + { + CPunctuationPair* pPuncPair = _PunctuationPair.GetAt(j); + + if (pPuncPair->_punctuation._Code == wch) + { + return TRUE; + } + } + + for (UINT k = 0; k < _PunctuationNestPair.Count(); k++) + { + CPunctuationNestPair* pPuncNestPair = _PunctuationNestPair.GetAt(k); + + if (pPuncNestPair->_punctuation_begin._Code == wch) + { + return TRUE; + } + if (pPuncNestPair->_punctuation_end._Code == wch) + { + return TRUE; + } + } + return FALSE; +} + +//+--------------------------------------------------------------------------- +// +// GetPunctuationPair +// +//---------------------------------------------------------------------------- + +WCHAR CCompositionProcessorEngine::GetPunctuation(WCHAR wch) +{ + for (int i = 0; i < ARRAYSIZE(Global::PunctuationTable); i++) + { + if (Global::PunctuationTable[i]._Code == wch) + { + return Global::PunctuationTable[i]._Punctuation; + } + } + + for (UINT j = 0; j < _PunctuationPair.Count(); j++) + { + CPunctuationPair* pPuncPair = _PunctuationPair.GetAt(j); + + if (pPuncPair->_punctuation._Code == wch) + { + if (! pPuncPair->_isPairToggle) + { + pPuncPair->_isPairToggle = TRUE; + return pPuncPair->_punctuation._Punctuation; + } + else + { + pPuncPair->_isPairToggle = FALSE; + return pPuncPair->_pairPunctuation; + } + } + } + + for (UINT k = 0; k < _PunctuationNestPair.Count(); k++) + { + CPunctuationNestPair* pPuncNestPair = _PunctuationNestPair.GetAt(k); + + if (pPuncNestPair->_punctuation_begin._Code == wch) + { + if (pPuncNestPair->_nestCount++ == 0) + { + return pPuncNestPair->_punctuation_begin._Punctuation; + } + else + { + return pPuncNestPair->_pairPunctuation_begin; + } + } + if (pPuncNestPair->_punctuation_end._Code == wch) + { + if (--pPuncNestPair->_nestCount == 0) + { + return pPuncNestPair->_punctuation_end._Punctuation; + } + else + { + return pPuncNestPair->_pairPunctuation_end; + } + } + } + return 0; +} + +//+--------------------------------------------------------------------------- +// +// IsDoubleSingleByte +// +//---------------------------------------------------------------------------- + +BOOL CCompositionProcessorEngine::IsDoubleSingleByte(WCHAR wch) +{ + if (L' ' <= wch && wch <= L'~') + { + return TRUE; + } + return FALSE; +} + +//+--------------------------------------------------------------------------- +// +// SetupKeystroke +// +//---------------------------------------------------------------------------- + +void CCompositionProcessorEngine::SetupKeystroke() +{ + SetKeystrokeTable(&_KeystrokeComposition); + return; +} + +//+--------------------------------------------------------------------------- +// +// SetKeystrokeTable +// +//---------------------------------------------------------------------------- + +void CCompositionProcessorEngine::SetKeystrokeTable(_Inout_ CSampleImeArray<_KEYSTROKE> *pKeystroke) +{ + for (int i = 0; i < 26; i++) + { + _KEYSTROKE* pKS = nullptr; + + pKS = pKeystroke->Append(); + if (!pKS) + { + break; + } + *pKS = _keystrokeTable[i]; + } +} + +//+--------------------------------------------------------------------------- +// +// SetupPreserved +// +//---------------------------------------------------------------------------- + +void CCompositionProcessorEngine::SetupPreserved(_In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId) +{ + TF_PRESERVEDKEY preservedKeyImeMode; + preservedKeyImeMode.uVKey = VK_SHIFT; + preservedKeyImeMode.uModifiers = _TF_MOD_ON_KEYUP_SHIFT_ONLY; + SetPreservedKey(Global::SampleIMEGuidImeModePreserveKey, preservedKeyImeMode, Global::ImeModeDescription, &_PreservedKey_IMEMode); + + TF_PRESERVEDKEY preservedKeyDoubleSingleByte; + preservedKeyDoubleSingleByte.uVKey = VK_SPACE; + preservedKeyDoubleSingleByte.uModifiers = TF_MOD_SHIFT; + SetPreservedKey(Global::SampleIMEGuidDoubleSingleBytePreserveKey, preservedKeyDoubleSingleByte, Global::DoubleSingleByteDescription, &_PreservedKey_DoubleSingleByte); + + TF_PRESERVEDKEY preservedKeyPunctuation; + preservedKeyPunctuation.uVKey = VK_OEM_PERIOD; + preservedKeyPunctuation.uModifiers = TF_MOD_CONTROL; + SetPreservedKey(Global::SampleIMEGuidPunctuationPreserveKey, preservedKeyPunctuation, Global::PunctuationDescription, &_PreservedKey_Punctuation); + + InitPreservedKey(&_PreservedKey_IMEMode, pThreadMgr, tfClientId); + InitPreservedKey(&_PreservedKey_DoubleSingleByte, pThreadMgr, tfClientId); + InitPreservedKey(&_PreservedKey_Punctuation, pThreadMgr, tfClientId); + + return; +} + +//+--------------------------------------------------------------------------- +// +// SetKeystrokeTable +// +//---------------------------------------------------------------------------- + +void CCompositionProcessorEngine::SetPreservedKey(const CLSID clsid, TF_PRESERVEDKEY & tfPreservedKey, _In_z_ LPCWSTR pwszDescription, _Out_ XPreservedKey *pXPreservedKey) +{ + pXPreservedKey->Guid = clsid; + + TF_PRESERVEDKEY *ptfPsvKey1 = pXPreservedKey->TSFPreservedKeyTable.Append(); + if (!ptfPsvKey1) + { + return; + } + *ptfPsvKey1 = tfPreservedKey; + + size_t srgKeystrokeBufLen = 0; + if (StringCchLength(pwszDescription, STRSAFE_MAX_CCH, &srgKeystrokeBufLen) != S_OK) + { + return; + } + pXPreservedKey->Description = new (std::nothrow) WCHAR[srgKeystrokeBufLen + 1]; + if (!pXPreservedKey->Description) + { + return; + } + + StringCchCopy((LPWSTR)pXPreservedKey->Description, srgKeystrokeBufLen, pwszDescription); + + return; +} +//+--------------------------------------------------------------------------- +// +// InitPreservedKey +// +// Register a hot key. +// +//---------------------------------------------------------------------------- + +BOOL CCompositionProcessorEngine::InitPreservedKey(_In_ XPreservedKey *pXPreservedKey, _In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId) +{ + ITfKeystrokeMgr *pKeystrokeMgr = nullptr; + + if (IsEqualGUID(pXPreservedKey->Guid, GUID_NULL)) + { + return FALSE; + } + + if (pThreadMgr->QueryInterface(IID_ITfKeystrokeMgr, (void **)&pKeystrokeMgr) != S_OK) + { + return FALSE; + } + + for (UINT i = 0; i < pXPreservedKey->TSFPreservedKeyTable.Count(); i++) + { + TF_PRESERVEDKEY preservedKey = *pXPreservedKey->TSFPreservedKeyTable.GetAt(i); + preservedKey.uModifiers &= 0xffff; + + size_t lenOfDesc = 0; + if (StringCchLength(pXPreservedKey->Description, STRSAFE_MAX_CCH, &lenOfDesc) != S_OK) + { + return FALSE; + } + pKeystrokeMgr->PreserveKey(tfClientId, pXPreservedKey->Guid, &preservedKey, pXPreservedKey->Description, static_cast(lenOfDesc)); + } + + pKeystrokeMgr->Release(); + + return TRUE; +} + +//+--------------------------------------------------------------------------- +// +// CheckShiftKeyOnly +// +//---------------------------------------------------------------------------- + +BOOL CCompositionProcessorEngine::CheckShiftKeyOnly(_In_ CSampleImeArray *pTSFPreservedKeyTable) +{ + for (UINT i = 0; i < pTSFPreservedKeyTable->Count(); i++) + { + TF_PRESERVEDKEY *ptfPsvKey = pTSFPreservedKeyTable->GetAt(i); + + if (((ptfPsvKey->uModifiers & (_TF_MOD_ON_KEYUP_SHIFT_ONLY & 0xffff0000)) && !Global::IsShiftKeyDownOnly) || + ((ptfPsvKey->uModifiers & (_TF_MOD_ON_KEYUP_CONTROL_ONLY & 0xffff0000)) && !Global::IsControlKeyDownOnly) || + ((ptfPsvKey->uModifiers & (_TF_MOD_ON_KEYUP_ALT_ONLY & 0xffff0000)) && !Global::IsAltKeyDownOnly) ) + { + return FALSE; + } + } + + return TRUE; +} + +//+--------------------------------------------------------------------------- +// +// OnPreservedKey +// +//---------------------------------------------------------------------------- + +void CCompositionProcessorEngine::OnPreservedKey(REFGUID rguid, _Out_ BOOL *pIsEaten, _In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId) +{ + if (IsEqualGUID(rguid, _PreservedKey_IMEMode.Guid)) + { + if (!CheckShiftKeyOnly(&_PreservedKey_IMEMode.TSFPreservedKeyTable)) + { + *pIsEaten = FALSE; + return; + } + BOOL isOpen = FALSE; + CCompartment CompartmentKeyboardOpen(pThreadMgr, tfClientId, GUID_COMPARTMENT_KEYBOARD_OPENCLOSE); + CompartmentKeyboardOpen._GetCompartmentBOOL(isOpen); + CompartmentKeyboardOpen._SetCompartmentBOOL(isOpen ? FALSE : TRUE); + + *pIsEaten = TRUE; + } + else if (IsEqualGUID(rguid, _PreservedKey_DoubleSingleByte.Guid)) + { + if (!CheckShiftKeyOnly(&_PreservedKey_DoubleSingleByte.TSFPreservedKeyTable)) + { + *pIsEaten = FALSE; + return; + } + BOOL isDouble = FALSE; + CCompartment CompartmentDoubleSingleByte(pThreadMgr, tfClientId, Global::SampleIMEGuidCompartmentDoubleSingleByte); + CompartmentDoubleSingleByte._GetCompartmentBOOL(isDouble); + CompartmentDoubleSingleByte._SetCompartmentBOOL(isDouble ? FALSE : TRUE); + *pIsEaten = TRUE; + } + else if (IsEqualGUID(rguid, _PreservedKey_Punctuation.Guid)) + { + if (!CheckShiftKeyOnly(&_PreservedKey_Punctuation.TSFPreservedKeyTable)) + { + *pIsEaten = FALSE; + return; + } + BOOL isPunctuation = FALSE; + CCompartment CompartmentPunctuation(pThreadMgr, tfClientId, Global::SampleIMEGuidCompartmentPunctuation); + CompartmentPunctuation._GetCompartmentBOOL(isPunctuation); + CompartmentPunctuation._SetCompartmentBOOL(isPunctuation ? FALSE : TRUE); + *pIsEaten = TRUE; + } + else + { + *pIsEaten = FALSE; + } + *pIsEaten = TRUE; +} + +//+--------------------------------------------------------------------------- +// +// SetupConfiguration +// +//---------------------------------------------------------------------------- + +void CCompositionProcessorEngine::SetupConfiguration() +{ + _isWildcard = TRUE; + _isDisableWildcardAtFirst = TRUE; + _hasMakePhraseFromText = TRUE; + _isKeystrokeSort = TRUE; + _candidateWndWidth = CAND_WIDTH; + + SetInitialCandidateListRange(); + + SetDefaultCandidateTextFont(); + + return; +} + +//+--------------------------------------------------------------------------- +// +// SetupLanguageBar +// +//---------------------------------------------------------------------------- + +void CCompositionProcessorEngine::SetupLanguageBar(_In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId, BOOL isSecureMode) +{ + DWORD dwEnable = 1; + CreateLanguageBarButton(dwEnable, GUID_LBI_INPUTMODE, Global::LangbarImeModeDescription, Global::ImeModeDescription, Global::ImeModeOnIcoIndex, Global::ImeModeOffIcoIndex, &_pLanguageBar_IMEMode, isSecureMode); + CreateLanguageBarButton(dwEnable, Global::SampleIMEGuidLangBarDoubleSingleByte, Global::LangbarDoubleSingleByteDescription, Global::DoubleSingleByteDescription, Global::DoubleSingleByteOnIcoIndex, Global::DoubleSingleByteOffIcoIndex, &_pLanguageBar_DoubleSingleByte, isSecureMode); + CreateLanguageBarButton(dwEnable, Global::SampleIMEGuidLangBarPunctuation, Global::LangbarPunctuationDescription, Global::PunctuationDescription, Global::PunctuationOnIcoIndex, Global::PunctuationOffIcoIndex, &_pLanguageBar_Punctuation, isSecureMode); + + InitLanguageBar(_pLanguageBar_IMEMode, pThreadMgr, tfClientId, GUID_COMPARTMENT_KEYBOARD_OPENCLOSE); + InitLanguageBar(_pLanguageBar_DoubleSingleByte, pThreadMgr, tfClientId, Global::SampleIMEGuidCompartmentDoubleSingleByte); + InitLanguageBar(_pLanguageBar_Punctuation, pThreadMgr, tfClientId, Global::SampleIMEGuidCompartmentPunctuation); + + _pCompartmentConversion = new (std::nothrow) CCompartment(pThreadMgr, tfClientId, GUID_COMPARTMENT_KEYBOARD_INPUTMODE_CONVERSION); + _pCompartmentKeyboardOpenEventSink = new (std::nothrow) CCompartmentEventSink(CompartmentCallback, this); + _pCompartmentConversionEventSink = new (std::nothrow) CCompartmentEventSink(CompartmentCallback, this); + _pCompartmentDoubleSingleByteEventSink = new (std::nothrow) CCompartmentEventSink(CompartmentCallback, this); + _pCompartmentPunctuationEventSink = new (std::nothrow) CCompartmentEventSink(CompartmentCallback, this); + + if (_pCompartmentKeyboardOpenEventSink) + { + _pCompartmentKeyboardOpenEventSink->_Advise(pThreadMgr, GUID_COMPARTMENT_KEYBOARD_OPENCLOSE); + } + if (_pCompartmentConversionEventSink) + { + _pCompartmentConversionEventSink->_Advise(pThreadMgr, GUID_COMPARTMENT_KEYBOARD_INPUTMODE_CONVERSION); + } + if (_pCompartmentDoubleSingleByteEventSink) + { + _pCompartmentDoubleSingleByteEventSink->_Advise(pThreadMgr, Global::SampleIMEGuidCompartmentDoubleSingleByte); + } + if (_pCompartmentPunctuationEventSink) + { + _pCompartmentPunctuationEventSink->_Advise(pThreadMgr, Global::SampleIMEGuidCompartmentPunctuation); + } + + return; +} + +//+--------------------------------------------------------------------------- +// +// CreateLanguageBarButton +// +//---------------------------------------------------------------------------- + +void CCompositionProcessorEngine::CreateLanguageBarButton(DWORD dwEnable, GUID guidLangBar, _In_z_ LPCWSTR pwszDescriptionValue, _In_z_ LPCWSTR pwszTooltipValue, DWORD dwOnIconIndex, DWORD dwOffIconIndex, _Outptr_result_maybenull_ CLangBarItemButton **ppLangBarItemButton, BOOL isSecureMode) +{ + dwEnable; + + if (ppLangBarItemButton) + { + *ppLangBarItemButton = new (std::nothrow) CLangBarItemButton(guidLangBar, pwszDescriptionValue, pwszTooltipValue, dwOnIconIndex, dwOffIconIndex, isSecureMode); + } + + return; +} + +//+--------------------------------------------------------------------------- +// +// InitLanguageBar +// +//---------------------------------------------------------------------------- + +BOOL CCompositionProcessorEngine::InitLanguageBar(_In_ CLangBarItemButton *pLangBarItemButton, _In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId, REFGUID guidCompartment) +{ + if (pLangBarItemButton) + { + if (pLangBarItemButton->_AddItem(pThreadMgr) == S_OK) + { + if (pLangBarItemButton->_RegisterCompartment(pThreadMgr, tfClientId, guidCompartment)) + { + return TRUE; + } + } + } + return FALSE; +} + +//+--------------------------------------------------------------------------- +// +// SetupDictionaryFile +// +//---------------------------------------------------------------------------- + +BOOL CCompositionProcessorEngine::SetupDictionaryFile() +{ + // Not yet registered + // Register CFileMapping + WCHAR wszFileName[MAX_PATH] = {'\0'}; + DWORD cchA = GetModuleFileName(Global::dllInstanceHandle, wszFileName, ARRAYSIZE(wszFileName)); + size_t iDicFileNameLen = cchA + wcslen(TEXTSERVICE_DIC); + WCHAR *pwszFileName = new (std::nothrow) WCHAR[iDicFileNameLen + 1]; + if (!pwszFileName) + { + goto ErrorExit; + } + *pwszFileName = L'\0'; + + // find the last '/' + while (cchA--) + { + WCHAR wszChar = wszFileName[cchA]; + if (wszChar == '\\' || wszChar == '/') + { + StringCchCopyN(pwszFileName, iDicFileNameLen + 1, wszFileName, cchA + 1); + StringCchCatN(pwszFileName, iDicFileNameLen + 1, TEXTSERVICE_DIC, wcslen(TEXTSERVICE_DIC)); + break; + } + } + + // create CFileMapping object + if (_pDictionaryFile == nullptr) + { + _pDictionaryFile = new (std::nothrow) CFileMapping(); + if (!_pDictionaryFile) + { + goto ErrorExit; + } + } + if (!(_pDictionaryFile)->CreateFile(pwszFileName, GENERIC_READ, OPEN_EXISTING, FILE_SHARE_READ)) + { + goto ErrorExit; + } + + _pTableDictionaryEngine = new (std::nothrow) CTableDictionaryEngine(GetLocale(), _pDictionaryFile); + if (!_pTableDictionaryEngine) + { + goto ErrorExit; + } + + delete []pwszFileName; + return TRUE; +ErrorExit: + if (pwszFileName) + { + delete []pwszFileName; + } + return FALSE; +} + +//+--------------------------------------------------------------------------- +// +// GetDictionaryFile +// +//---------------------------------------------------------------------------- + +CFile* CCompositionProcessorEngine::GetDictionaryFile() +{ + return _pDictionaryFile; +} + +//+--------------------------------------------------------------------------- +// +// SetupPunctuationPair +// +//---------------------------------------------------------------------------- + +void CCompositionProcessorEngine::SetupPunctuationPair() +{ + // Punctuation pair + const int pair_count = 2; + CPunctuationPair punc_quotation_mark(L'"', 0x201C, 0x201D); + CPunctuationPair punc_apostrophe(L'\'', 0x2018, 0x2019); + + CPunctuationPair puncPairs[pair_count] = { + punc_quotation_mark, + punc_apostrophe, + }; + + for (int i = 0; i < pair_count; ++i) + { + CPunctuationPair *pPuncPair = _PunctuationPair.Append(); + *pPuncPair = puncPairs[i]; + } + + // Punctuation nest pair + CPunctuationNestPair punc_angle_bracket(L'<', 0x300A, 0x3008, L'>', 0x300B, 0x3009); + + CPunctuationNestPair* pPuncNestPair = _PunctuationNestPair.Append(); + *pPuncNestPair = punc_angle_bracket; +} + +void CCompositionProcessorEngine::InitializeSampleIMECompartment(_In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId) +{ + // set initial mode + CCompartment CompartmentKeyboardOpen(pThreadMgr, tfClientId, GUID_COMPARTMENT_KEYBOARD_OPENCLOSE); + CompartmentKeyboardOpen._SetCompartmentBOOL(TRUE); + + CCompartment CompartmentDoubleSingleByte(pThreadMgr, tfClientId, Global::SampleIMEGuidCompartmentDoubleSingleByte); + CompartmentDoubleSingleByte._SetCompartmentBOOL(FALSE); + + CCompartment CompartmentPunctuation(pThreadMgr, tfClientId, Global::SampleIMEGuidCompartmentPunctuation); + CompartmentPunctuation._SetCompartmentBOOL(TRUE); + + PrivateCompartmentsUpdated(pThreadMgr); +} +//+--------------------------------------------------------------------------- +// +// CompartmentCallback +// +//---------------------------------------------------------------------------- + +// static +HRESULT CCompositionProcessorEngine::CompartmentCallback(_In_ void *pv, REFGUID guidCompartment) +{ + CCompositionProcessorEngine* fakeThis = (CCompositionProcessorEngine*)pv; + if (nullptr == fakeThis) + { + return E_INVALIDARG; + } + + ITfThreadMgr* pThreadMgr = nullptr; + HRESULT hr = CoCreateInstance(CLSID_TF_ThreadMgr, nullptr, CLSCTX_INPROC_SERVER, IID_ITfThreadMgr, (void**)&pThreadMgr); + if (FAILED(hr)) + { + return E_FAIL; + } + + if (IsEqualGUID(guidCompartment, Global::SampleIMEGuidCompartmentDoubleSingleByte) || + IsEqualGUID(guidCompartment, Global::SampleIMEGuidCompartmentPunctuation)) + { + fakeThis->PrivateCompartmentsUpdated(pThreadMgr); + } + else if (IsEqualGUID(guidCompartment, GUID_COMPARTMENT_KEYBOARD_INPUTMODE_CONVERSION) || + IsEqualGUID(guidCompartment, GUID_COMPARTMENT_KEYBOARD_INPUTMODE_SENTENCE)) + { + fakeThis->ConversionModeCompartmentUpdated(pThreadMgr); + } + else if (IsEqualGUID(guidCompartment, GUID_COMPARTMENT_KEYBOARD_OPENCLOSE)) + { + fakeThis->KeyboardOpenCompartmentUpdated(pThreadMgr); + } + + pThreadMgr->Release(); + pThreadMgr = nullptr; + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// UpdatePrivateCompartments +// +//---------------------------------------------------------------------------- + +void CCompositionProcessorEngine::ConversionModeCompartmentUpdated(_In_ ITfThreadMgr *pThreadMgr) +{ + if (!_pCompartmentConversion) + { + return; + } + + DWORD conversionMode = 0; + if (FAILED(_pCompartmentConversion->_GetCompartmentDWORD(conversionMode))) + { + return; + } + + BOOL isDouble = FALSE; + CCompartment CompartmentDoubleSingleByte(pThreadMgr, _tfClientId, Global::SampleIMEGuidCompartmentDoubleSingleByte); + if (SUCCEEDED(CompartmentDoubleSingleByte._GetCompartmentBOOL(isDouble))) + { + if (!isDouble && (conversionMode & TF_CONVERSIONMODE_FULLSHAPE)) + { + CompartmentDoubleSingleByte._SetCompartmentBOOL(TRUE); + } + else if (isDouble && !(conversionMode & TF_CONVERSIONMODE_FULLSHAPE)) + { + CompartmentDoubleSingleByte._SetCompartmentBOOL(FALSE); + } + } + BOOL isPunctuation = FALSE; + CCompartment CompartmentPunctuation(pThreadMgr, _tfClientId, Global::SampleIMEGuidCompartmentPunctuation); + if (SUCCEEDED(CompartmentPunctuation._GetCompartmentBOOL(isPunctuation))) + { + if (!isPunctuation && (conversionMode & TF_CONVERSIONMODE_SYMBOL)) + { + CompartmentPunctuation._SetCompartmentBOOL(TRUE); + } + else if (isPunctuation && !(conversionMode & TF_CONVERSIONMODE_SYMBOL)) + { + CompartmentPunctuation._SetCompartmentBOOL(FALSE); + } + } + + BOOL fOpen = FALSE; + CCompartment CompartmentKeyboardOpen(pThreadMgr, _tfClientId, GUID_COMPARTMENT_KEYBOARD_OPENCLOSE); + if (SUCCEEDED(CompartmentKeyboardOpen._GetCompartmentBOOL(fOpen))) + { + if (fOpen && !(conversionMode & TF_CONVERSIONMODE_NATIVE)) + { + CompartmentKeyboardOpen._SetCompartmentBOOL(FALSE); + } + else if (!fOpen && (conversionMode & TF_CONVERSIONMODE_NATIVE)) + { + CompartmentKeyboardOpen._SetCompartmentBOOL(TRUE); + } + } +} + +//+--------------------------------------------------------------------------- +// +// PrivateCompartmentsUpdated() +// +//---------------------------------------------------------------------------- + +void CCompositionProcessorEngine::PrivateCompartmentsUpdated(_In_ ITfThreadMgr *pThreadMgr) +{ + if (!_pCompartmentConversion) + { + return; + } + + DWORD conversionMode = 0; + DWORD conversionModePrev = 0; + if (FAILED(_pCompartmentConversion->_GetCompartmentDWORD(conversionMode))) + { + return; + } + + conversionModePrev = conversionMode; + + BOOL isDouble = FALSE; + CCompartment CompartmentDoubleSingleByte(pThreadMgr, _tfClientId, Global::SampleIMEGuidCompartmentDoubleSingleByte); + if (SUCCEEDED(CompartmentDoubleSingleByte._GetCompartmentBOOL(isDouble))) + { + if (!isDouble && (conversionMode & TF_CONVERSIONMODE_FULLSHAPE)) + { + conversionMode &= ~TF_CONVERSIONMODE_FULLSHAPE; + } + else if (isDouble && !(conversionMode & TF_CONVERSIONMODE_FULLSHAPE)) + { + conversionMode |= TF_CONVERSIONMODE_FULLSHAPE; + } + } + + BOOL isPunctuation = FALSE; + CCompartment CompartmentPunctuation(pThreadMgr, _tfClientId, Global::SampleIMEGuidCompartmentPunctuation); + if (SUCCEEDED(CompartmentPunctuation._GetCompartmentBOOL(isPunctuation))) + { + if (!isPunctuation && (conversionMode & TF_CONVERSIONMODE_SYMBOL)) + { + conversionMode &= ~TF_CONVERSIONMODE_SYMBOL; + } + else if (isPunctuation && !(conversionMode & TF_CONVERSIONMODE_SYMBOL)) + { + conversionMode |= TF_CONVERSIONMODE_SYMBOL; + } + } + + if (conversionMode != conversionModePrev) + { + _pCompartmentConversion->_SetCompartmentDWORD(conversionMode); + } +} + +//+--------------------------------------------------------------------------- +// +// KeyboardOpenCompartmentUpdated +// +//---------------------------------------------------------------------------- + +void CCompositionProcessorEngine::KeyboardOpenCompartmentUpdated(_In_ ITfThreadMgr *pThreadMgr) +{ + if (!_pCompartmentConversion) + { + return; + } + + DWORD conversionMode = 0; + DWORD conversionModePrev = 0; + if (FAILED(_pCompartmentConversion->_GetCompartmentDWORD(conversionMode))) + { + return; + } + + conversionModePrev = conversionMode; + + BOOL isOpen = FALSE; + CCompartment CompartmentKeyboardOpen(pThreadMgr, _tfClientId, GUID_COMPARTMENT_KEYBOARD_OPENCLOSE); + if (SUCCEEDED(CompartmentKeyboardOpen._GetCompartmentBOOL(isOpen))) + { + if (isOpen && !(conversionMode & TF_CONVERSIONMODE_NATIVE)) + { + conversionMode |= TF_CONVERSIONMODE_NATIVE; + } + else if (!isOpen && (conversionMode & TF_CONVERSIONMODE_NATIVE)) + { + conversionMode &= ~TF_CONVERSIONMODE_NATIVE; + } + } + + if (conversionMode != conversionModePrev) + { + _pCompartmentConversion->_SetCompartmentDWORD(conversionMode); + } +} + + +////////////////////////////////////////////////////////////////////// +// +// XPreservedKey implementation. +// +////////////////////////////////////////////////////////////////////// + +//+--------------------------------------------------------------------------- +// +// UninitPreservedKey +// +//---------------------------------------------------------------------------- + +BOOL CCompositionProcessorEngine::XPreservedKey::UninitPreservedKey(_In_ ITfThreadMgr *pThreadMgr) +{ + ITfKeystrokeMgr* pKeystrokeMgr = nullptr; + + if (IsEqualGUID(Guid, GUID_NULL)) + { + return FALSE; + } + + if (FAILED(pThreadMgr->QueryInterface(IID_ITfKeystrokeMgr, (void **)&pKeystrokeMgr))) + { + return FALSE; + } + + for (UINT i = 0; i < TSFPreservedKeyTable.Count(); i++) + { + TF_PRESERVEDKEY pPreservedKey = *TSFPreservedKeyTable.GetAt(i); + pPreservedKey.uModifiers &= 0xffff; + + pKeystrokeMgr->UnpreserveKey(Guid, &pPreservedKey); + } + + pKeystrokeMgr->Release(); + + return TRUE; +} + +CCompositionProcessorEngine::XPreservedKey::XPreservedKey() +{ + Guid = GUID_NULL; + Description = nullptr; +} + +CCompositionProcessorEngine::XPreservedKey::~XPreservedKey() +{ + ITfThreadMgr* pThreadMgr = nullptr; + + HRESULT hr = CoCreateInstance(CLSID_TF_ThreadMgr, NULL, CLSCTX_INPROC_SERVER, IID_ITfThreadMgr, (void**)&pThreadMgr); + if (SUCCEEDED(hr)) + { + UninitPreservedKey(pThreadMgr); + pThreadMgr->Release(); + pThreadMgr = nullptr; + } + + if (Description) + { + delete [] Description; + } +} +//+--------------------------------------------------------------------------- +// +// CSampleIME::CreateInstance +// +//---------------------------------------------------------------------------- + +HRESULT CSampleIME::CreateInstance(REFCLSID rclsid, REFIID riid, _Outptr_result_maybenull_ LPVOID* ppv, _Out_opt_ HINSTANCE* phInst, BOOL isComLessMode) +{ + HRESULT hr = S_OK; + if (phInst == nullptr) + { + return E_INVALIDARG; + } + + *phInst = nullptr; + + if (!isComLessMode) + { + hr = ::CoCreateInstance(rclsid, + NULL, + CLSCTX_INPROC_SERVER, + riid, + ppv); + } + else + { + hr = CSampleIME::ComLessCreateInstance(rclsid, riid, ppv, phInst); + } + + return hr; +} + +//+--------------------------------------------------------------------------- +// +// CSampleIME::ComLessCreateInstance +// +//---------------------------------------------------------------------------- + +HRESULT CSampleIME::ComLessCreateInstance(REFGUID rclsid, REFIID riid, _Outptr_result_maybenull_ void **ppv, _Out_opt_ HINSTANCE *phInst) +{ + HRESULT hr = S_OK; + HINSTANCE sampleIMEDllHandle = nullptr; + WCHAR wchPath[MAX_PATH] = {'\0'}; + WCHAR szExpandedPath[MAX_PATH] = {'\0'}; + DWORD dwCnt = 0; + *ppv = nullptr; + + hr = phInst ? S_OK : E_FAIL; + if (SUCCEEDED(hr)) + { + *phInst = nullptr; + hr = CSampleIME::GetComModuleName(rclsid, wchPath, ARRAYSIZE(wchPath)); + if (SUCCEEDED(hr)) + { + dwCnt = ExpandEnvironmentStringsW(wchPath, szExpandedPath, ARRAYSIZE(szExpandedPath)); + hr = (0 < dwCnt && dwCnt <= ARRAYSIZE(szExpandedPath)) ? S_OK : E_FAIL; + if (SUCCEEDED(hr)) + { + sampleIMEDllHandle = LoadLibraryEx(szExpandedPath, NULL, 0); + hr = sampleIMEDllHandle ? S_OK : E_FAIL; + if (SUCCEEDED(hr)) + { + *phInst = sampleIMEDllHandle; + FARPROC pfn = GetProcAddress(sampleIMEDllHandle, "DllGetClassObject"); + hr = pfn ? S_OK : E_FAIL; + if (SUCCEEDED(hr)) + { + IClassFactory *pClassFactory = nullptr; + hr = ((HRESULT (STDAPICALLTYPE *)(REFCLSID rclsid, REFIID riid, LPVOID *ppv))(pfn))(rclsid, IID_IClassFactory, (void **)&pClassFactory); + if (SUCCEEDED(hr) && pClassFactory) + { + hr = pClassFactory->CreateInstance(NULL, riid, ppv); + pClassFactory->Release(); + } + } + } + } + } + } + + if (!SUCCEEDED(hr) && phInst && *phInst) + { + FreeLibrary(*phInst); + *phInst = 0; + } + return hr; +} + +//+--------------------------------------------------------------------------- +// +// CSampleIME::GetComModuleName +// +//---------------------------------------------------------------------------- + +HRESULT CSampleIME::GetComModuleName(REFGUID rclsid, _Out_writes_(cchPath)WCHAR* wchPath, DWORD cchPath) +{ + HRESULT hr = S_OK; + + CRegKey key; + WCHAR wchClsid[CLSID_STRLEN + 1]; + hr = CLSIDToString(rclsid, wchClsid) ? S_OK : E_FAIL; + if (SUCCEEDED(hr)) + { + WCHAR wchKey[MAX_PATH]; + hr = StringCchPrintfW(wchKey, ARRAYSIZE(wchKey), L"CLSID\\%s\\InProcServer32", wchClsid); + if (SUCCEEDED(hr)) + { + hr = (key.Open(HKEY_CLASSES_ROOT, wchKey, KEY_READ) == ERROR_SUCCESS) ? S_OK : E_FAIL; + if (SUCCEEDED(hr)) + { + WCHAR wszModel[MAX_PATH]; + ULONG cch = ARRAYSIZE(wszModel); + hr = (key.QueryStringValue(L"ThreadingModel", wszModel, &cch) == ERROR_SUCCESS) ? S_OK : E_FAIL; + if (SUCCEEDED(hr)) + { + if (CompareStringOrdinal(wszModel, + -1, + L"Apartment", + -1, + TRUE) == CSTR_EQUAL) + { + hr = (key.QueryStringValue(NULL, wchPath, &cchPath) == ERROR_SUCCESS) ? S_OK : E_FAIL; + } + else + { + hr = E_FAIL; + } + } + } + } + } + + return hr; +} + +void CCompositionProcessorEngine::InitKeyStrokeTable() +{ + for (int i = 0; i < 26; i++) + { + _keystrokeTable[i].VirtualKey = 'A' + i; + _keystrokeTable[i].Modifiers = 0; + _keystrokeTable[i].Function = FUNCTION_INPUT; + } +} + +void CCompositionProcessorEngine::ShowAllLanguageBarIcons() +{ + SetLanguageBarStatus(TF_LBI_STATUS_HIDDEN, FALSE); +} + +void CCompositionProcessorEngine::HideAllLanguageBarIcons() +{ + SetLanguageBarStatus(TF_LBI_STATUS_HIDDEN, TRUE); +} + +void CCompositionProcessorEngine::SetInitialCandidateListRange() +{ + for (DWORD i = 1; i <= 10; i++) + { + DWORD* pNewIndexRange = nullptr; + + pNewIndexRange = _candidateListIndexRange.Append(); + if (pNewIndexRange != nullptr) + { + if (i != 10) + { + *pNewIndexRange = i; + } + else + { + *pNewIndexRange = 0; + } + } + } +} + +void CCompositionProcessorEngine::SetDefaultCandidateTextFont() +{ + // Candidate Text Font + if (Global::defaultlFontHandle == nullptr) + { + WCHAR fontName[50] = {'\0'}; + LoadString(Global::dllInstanceHandle, IDS_DEFAULT_FONT, fontName, 50); + Global::defaultlFontHandle = CreateFont(-MulDiv(10, GetDeviceCaps(GetDC(NULL), LOGPIXELSY), 72), 0, 0, 0, FW_MEDIUM, 0, 0, 0, 0, 0, 0, 0, 0, fontName); + if (!Global::defaultlFontHandle) + { + LOGFONT lf; + SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(LOGFONT), &lf, 0); + // Fall back to the default GUI font on failure. + Global::defaultlFontHandle = CreateFont(-MulDiv(10, GetDeviceCaps(GetDC(NULL), LOGPIXELSY), 72), 0, 0, 0, FW_MEDIUM, 0, 0, 0, 0, 0, 0, 0, 0, lf.lfFaceName); + } + } +} + +////////////////////////////////////////////////////////////////////// +// +// CCompositionProcessorEngine +// +////////////////////////////////////////////////////////////////////// + +//+--------------------------------------------------------------------------- +// +// CCompositionProcessorEngine::IsVirtualKeyNeed +// +// Test virtual key code need to the Composition Processor Engine. +// param +// [in] uCode - Specify virtual key code. +// [in/out] pwch - char code +// [in] fComposing - Specified composing. +// [in] fCandidateMode - Specified candidate mode. +// [out] pKeyState - Returns function regarding virtual key. +// returns +// If engine need this virtual key code, returns true. Otherwise returns false. +//---------------------------------------------------------------------------- + +BOOL CCompositionProcessorEngine::IsVirtualKeyNeed(UINT uCode, _In_reads_(1) WCHAR *pwch, BOOL fComposing, CANDIDATE_MODE candidateMode, BOOL hasCandidateWithWildcard, _Out_opt_ _KEYSTROKE_STATE *pKeyState) +{ + if (pKeyState) + { + pKeyState->Category = CATEGORY_NONE; + pKeyState->Function = FUNCTION_NONE; + } + + if (candidateMode == CANDIDATE_ORIGINAL || candidateMode == CANDIDATE_PHRASE || candidateMode == CANDIDATE_WITH_NEXT_COMPOSITION) + { + fComposing = FALSE; + } + + if (fComposing || candidateMode == CANDIDATE_INCREMENTAL || candidateMode == CANDIDATE_NONE) + { + if (IsVirtualKeyKeystrokeComposition(uCode, pKeyState, FUNCTION_NONE)) + { + return TRUE; + } + else if ((IsWildcard() && IsWildcardChar(*pwch) && !IsDisableWildcardAtFirst()) || + (IsWildcard() && IsWildcardChar(*pwch) && IsDisableWildcardAtFirst() && _keystrokeBuffer.GetLength())) + { + if (pKeyState) + { + pKeyState->Category = CATEGORY_COMPOSING; + pKeyState->Function = FUNCTION_INPUT; + } + return TRUE; + } + else if (_hasWildcardIncludedInKeystrokeBuffer && uCode == VK_SPACE) + { + if (pKeyState) { pKeyState->Category = CATEGORY_COMPOSING; pKeyState->Function = FUNCTION_CONVERT_WILDCARD; } return TRUE; + } + } + + if (candidateMode == CANDIDATE_ORIGINAL || candidateMode == CANDIDATE_PHRASE || candidateMode == CANDIDATE_WITH_NEXT_COMPOSITION) + { + BOOL isRetCode = TRUE; + if (IsVirtualKeyKeystrokeCandidate(uCode, pKeyState, candidateMode, &isRetCode, &_KeystrokeCandidate)) + { + return isRetCode; + } + + if (hasCandidateWithWildcard) + { + if (IsVirtualKeyKeystrokeCandidate(uCode, pKeyState, candidateMode, &isRetCode, &_KeystrokeCandidateWildcard)) + { + return isRetCode; + } + } + + // Candidate list could not handle key. We can try to restart the composition. + if (IsVirtualKeyKeystrokeComposition(uCode, pKeyState, FUNCTION_INPUT)) + { + if (candidateMode != CANDIDATE_ORIGINAL) + { + return TRUE; + } + else + { + if (pKeyState) { pKeyState->Category = CATEGORY_CANDIDATE; pKeyState->Function = FUNCTION_FINALIZE_CANDIDATELIST_AND_INPUT; } + return TRUE; + } + } + } + + // CANDIDATE_INCREMENTAL should process Keystroke.Candidate virtual keys. + else if (candidateMode == CANDIDATE_INCREMENTAL) + { + BOOL isRetCode = TRUE; + if (IsVirtualKeyKeystrokeCandidate(uCode, pKeyState, candidateMode, &isRetCode, &_KeystrokeCandidate)) + { + return isRetCode; + } + } + + if (!fComposing && candidateMode != CANDIDATE_ORIGINAL && candidateMode != CANDIDATE_PHRASE && candidateMode != CANDIDATE_WITH_NEXT_COMPOSITION) + { + if (IsVirtualKeyKeystrokeComposition(uCode, pKeyState, FUNCTION_INPUT)) + { + return TRUE; + } + } + + // System pre-defined keystroke + if (fComposing) + { + if ((candidateMode != CANDIDATE_INCREMENTAL)) + { + switch (uCode) + { + case VK_LEFT: if (pKeyState) { pKeyState->Category = CATEGORY_COMPOSING; pKeyState->Function = FUNCTION_MOVE_LEFT; } return TRUE; + case VK_RIGHT: if (pKeyState) { pKeyState->Category = CATEGORY_COMPOSING; pKeyState->Function = FUNCTION_MOVE_RIGHT; } return TRUE; + case VK_RETURN: if (pKeyState) { pKeyState->Category = CATEGORY_COMPOSING; pKeyState->Function = FUNCTION_FINALIZE_CANDIDATELIST; } return TRUE; + case VK_ESCAPE: if (pKeyState) { pKeyState->Category = CATEGORY_COMPOSING; pKeyState->Function = FUNCTION_CANCEL; } return TRUE; + case VK_BACK: if (pKeyState) { pKeyState->Category = CATEGORY_COMPOSING; pKeyState->Function = FUNCTION_BACKSPACE; } return TRUE; + + case VK_UP: if (pKeyState) { pKeyState->Category = CATEGORY_COMPOSING; pKeyState->Function = FUNCTION_MOVE_UP; } return TRUE; + case VK_DOWN: if (pKeyState) { pKeyState->Category = CATEGORY_COMPOSING; pKeyState->Function = FUNCTION_MOVE_DOWN; } return TRUE; + case VK_PRIOR: if (pKeyState) { pKeyState->Category = CATEGORY_COMPOSING; pKeyState->Function = FUNCTION_MOVE_PAGE_UP; } return TRUE; + case VK_NEXT: if (pKeyState) { pKeyState->Category = CATEGORY_COMPOSING; pKeyState->Function = FUNCTION_MOVE_PAGE_DOWN; } return TRUE; + + case VK_HOME: if (pKeyState) { pKeyState->Category = CATEGORY_COMPOSING; pKeyState->Function = FUNCTION_MOVE_PAGE_TOP; } return TRUE; + case VK_END: if (pKeyState) { pKeyState->Category = CATEGORY_COMPOSING; pKeyState->Function = FUNCTION_MOVE_PAGE_BOTTOM; } return TRUE; + + case VK_SPACE: if (pKeyState) { pKeyState->Category = CATEGORY_COMPOSING; pKeyState->Function = FUNCTION_CONVERT; } return TRUE; + } + } + else if ((candidateMode == CANDIDATE_INCREMENTAL)) + { + switch (uCode) + { + // VK_LEFT, VK_RIGHT - set *pIsEaten = FALSE for application could move caret left or right. + // and for CUAS, invoke _HandleCompositionCancel() edit session due to ignore CUAS default key handler for send out terminate composition + case VK_LEFT: + case VK_RIGHT: + { + if (pKeyState) + { + pKeyState->Category = CATEGORY_INVOKE_COMPOSITION_EDIT_SESSION; + pKeyState->Function = FUNCTION_CANCEL; + } + } + return FALSE; + + case VK_RETURN: if (pKeyState) { pKeyState->Category = CATEGORY_CANDIDATE; pKeyState->Function = FUNCTION_FINALIZE_CANDIDATELIST; } return TRUE; + case VK_ESCAPE: if (pKeyState) { pKeyState->Category = CATEGORY_CANDIDATE; pKeyState->Function = FUNCTION_CANCEL; } return TRUE; + + // VK_BACK - remove one char from reading string. + case VK_BACK: if (pKeyState) { pKeyState->Category = CATEGORY_COMPOSING; pKeyState->Function = FUNCTION_BACKSPACE; } return TRUE; + + case VK_UP: if (pKeyState) { pKeyState->Category = CATEGORY_CANDIDATE; pKeyState->Function = FUNCTION_MOVE_UP; } return TRUE; + case VK_DOWN: if (pKeyState) { pKeyState->Category = CATEGORY_CANDIDATE; pKeyState->Function = FUNCTION_MOVE_DOWN; } return TRUE; + case VK_PRIOR: if (pKeyState) { pKeyState->Category = CATEGORY_CANDIDATE; pKeyState->Function = FUNCTION_MOVE_PAGE_UP; } return TRUE; + case VK_NEXT: if (pKeyState) { pKeyState->Category = CATEGORY_CANDIDATE; pKeyState->Function = FUNCTION_MOVE_PAGE_DOWN; } return TRUE; + + case VK_HOME: if (pKeyState) { pKeyState->Category = CATEGORY_CANDIDATE; pKeyState->Function = FUNCTION_MOVE_PAGE_TOP; } return TRUE; + case VK_END: if (pKeyState) { pKeyState->Category = CATEGORY_CANDIDATE; pKeyState->Function = FUNCTION_MOVE_PAGE_BOTTOM; } return TRUE; + + case VK_SPACE: + { + if (candidateMode == CANDIDATE_INCREMENTAL) + { + if (pKeyState) { pKeyState->Category = CATEGORY_CANDIDATE; pKeyState->Function = FUNCTION_CONVERT; } return TRUE; + } + else + { + if (pKeyState) { pKeyState->Category = CATEGORY_COMPOSING; pKeyState->Function = FUNCTION_CONVERT; } return TRUE; + } + } + } + } + } + + if ((candidateMode == CANDIDATE_ORIGINAL) || (candidateMode == CANDIDATE_WITH_NEXT_COMPOSITION)) + { + switch (uCode) + { + case VK_UP: if (pKeyState) { pKeyState->Category = CATEGORY_CANDIDATE; pKeyState->Function = FUNCTION_MOVE_UP; } return TRUE; + case VK_DOWN: if (pKeyState) { pKeyState->Category = CATEGORY_CANDIDATE; pKeyState->Function = FUNCTION_MOVE_DOWN; } return TRUE; + case VK_PRIOR: if (pKeyState) { pKeyState->Category = CATEGORY_CANDIDATE; pKeyState->Function = FUNCTION_MOVE_PAGE_UP; } return TRUE; + case VK_NEXT: if (pKeyState) { pKeyState->Category = CATEGORY_CANDIDATE; pKeyState->Function = FUNCTION_MOVE_PAGE_DOWN; } return TRUE; + case VK_HOME: if (pKeyState) { pKeyState->Category = CATEGORY_CANDIDATE; pKeyState->Function = FUNCTION_MOVE_PAGE_TOP; } return TRUE; + case VK_END: if (pKeyState) { pKeyState->Category = CATEGORY_CANDIDATE; pKeyState->Function = FUNCTION_MOVE_PAGE_BOTTOM; } return TRUE; + case VK_RETURN: if (pKeyState) { pKeyState->Category = CATEGORY_CANDIDATE; pKeyState->Function = FUNCTION_FINALIZE_CANDIDATELIST; } return TRUE; + case VK_SPACE: if (pKeyState) { pKeyState->Category = CATEGORY_CANDIDATE; pKeyState->Function = FUNCTION_CONVERT; } return TRUE; + case VK_BACK: if (pKeyState) { pKeyState->Category = CATEGORY_CANDIDATE; pKeyState->Function = FUNCTION_CANCEL; } return TRUE; + + case VK_ESCAPE: + { + if (candidateMode == CANDIDATE_WITH_NEXT_COMPOSITION) + { + if (pKeyState) + { + pKeyState->Category = CATEGORY_INVOKE_COMPOSITION_EDIT_SESSION; + pKeyState->Function = FUNCTION_FINALIZE_TEXTSTORE; + } + return TRUE; + } + else + { + if (pKeyState) + { + pKeyState->Category = CATEGORY_CANDIDATE; + pKeyState->Function = FUNCTION_CANCEL; + } + return TRUE; + } + } + } + + if (candidateMode == CANDIDATE_WITH_NEXT_COMPOSITION) + { + if (IsVirtualKeyKeystrokeComposition(uCode, NULL, FUNCTION_NONE)) + { + if (pKeyState) { pKeyState->Category = CATEGORY_COMPOSING; pKeyState->Function = FUNCTION_FINALIZE_TEXTSTORE_AND_INPUT; } return TRUE; + } + } + } + + if (candidateMode == CANDIDATE_PHRASE) + { + switch (uCode) + { + case VK_UP: if (pKeyState) { pKeyState->Category = CATEGORY_PHRASE; pKeyState->Function = FUNCTION_MOVE_UP; } return TRUE; + case VK_DOWN: if (pKeyState) { pKeyState->Category = CATEGORY_PHRASE; pKeyState->Function = FUNCTION_MOVE_DOWN; } return TRUE; + case VK_PRIOR: if (pKeyState) { pKeyState->Category = CATEGORY_PHRASE; pKeyState->Function = FUNCTION_MOVE_PAGE_UP; } return TRUE; + case VK_NEXT: if (pKeyState) { pKeyState->Category = CATEGORY_PHRASE; pKeyState->Function = FUNCTION_MOVE_PAGE_DOWN; } return TRUE; + case VK_HOME: if (pKeyState) { pKeyState->Category = CATEGORY_PHRASE; pKeyState->Function = FUNCTION_MOVE_PAGE_TOP; } return TRUE; + case VK_END: if (pKeyState) { pKeyState->Category = CATEGORY_PHRASE; pKeyState->Function = FUNCTION_MOVE_PAGE_BOTTOM; } return TRUE; + case VK_RETURN: if (pKeyState) { pKeyState->Category = CATEGORY_PHRASE; pKeyState->Function = FUNCTION_FINALIZE_CANDIDATELIST; } return TRUE; + case VK_SPACE: if (pKeyState) { pKeyState->Category = CATEGORY_PHRASE; pKeyState->Function = FUNCTION_CONVERT; } return TRUE; + case VK_ESCAPE: if (pKeyState) { pKeyState->Category = CATEGORY_PHRASE; pKeyState->Function = FUNCTION_CANCEL; } return TRUE; + case VK_BACK: if (pKeyState) { pKeyState->Category = CATEGORY_CANDIDATE; pKeyState->Function = FUNCTION_CANCEL; } return TRUE; + } + } + + if (IsKeystrokeRange(uCode, pKeyState, candidateMode)) + { + return TRUE; + } + else if (pKeyState && pKeyState->Category != CATEGORY_NONE) + { + return FALSE; + } + + if (*pwch && !IsVirtualKeyKeystrokeComposition(uCode, pKeyState, FUNCTION_NONE)) + { + if (pKeyState) + { + pKeyState->Category = CATEGORY_INVOKE_COMPOSITION_EDIT_SESSION; + pKeyState->Function = FUNCTION_FINALIZE_TEXTSTORE; + } + return FALSE; + } + + return FALSE; +} + +//+--------------------------------------------------------------------------- +// +// CCompositionProcessorEngine::IsVirtualKeyKeystrokeComposition +// +//---------------------------------------------------------------------------- + +BOOL CCompositionProcessorEngine::IsVirtualKeyKeystrokeComposition(UINT uCode, _Out_opt_ _KEYSTROKE_STATE *pKeyState, KEYSTROKE_FUNCTION function) +{ + if (pKeyState == nullptr) + { + return FALSE; + } + + pKeyState->Category = CATEGORY_NONE; + pKeyState->Function = FUNCTION_NONE; + + for (UINT i = 0; i < _KeystrokeComposition.Count(); i++) + { + _KEYSTROKE *pKeystroke = nullptr; + + pKeystroke = _KeystrokeComposition.GetAt(i); + + if ((pKeystroke->VirtualKey == uCode) && Global::CheckModifiers(Global::ModifiersValue, pKeystroke->Modifiers)) + { + if (function == FUNCTION_NONE) + { + pKeyState->Category = CATEGORY_COMPOSING; + pKeyState->Function = pKeystroke->Function; + return TRUE; + } + else if (function == pKeystroke->Function) + { + pKeyState->Category = CATEGORY_COMPOSING; + pKeyState->Function = pKeystroke->Function; + return TRUE; + } + } + } + + return FALSE; +} + +//+--------------------------------------------------------------------------- +// +// CCompositionProcessorEngine::IsVirtualKeyKeystrokeCandidate +// +//---------------------------------------------------------------------------- + +BOOL CCompositionProcessorEngine::IsVirtualKeyKeystrokeCandidate(UINT uCode, _In_ _KEYSTROKE_STATE *pKeyState, CANDIDATE_MODE candidateMode, _Out_ BOOL *pfRetCode, _In_ CSampleImeArray<_KEYSTROKE> *pKeystrokeMetric) +{ + if (pfRetCode == nullptr) + { + return FALSE; + } + *pfRetCode = FALSE; + + for (UINT i = 0; i < pKeystrokeMetric->Count(); i++) + { + _KEYSTROKE *pKeystroke = nullptr; + + pKeystroke = pKeystrokeMetric->GetAt(i); + + if ((pKeystroke->VirtualKey == uCode) && Global::CheckModifiers(Global::ModifiersValue, pKeystroke->Modifiers)) + { + *pfRetCode = TRUE; + if (pKeyState) + { + pKeyState->Category = (candidateMode == CANDIDATE_ORIGINAL ? CATEGORY_CANDIDATE : + candidateMode == CANDIDATE_PHRASE ? CATEGORY_PHRASE : CATEGORY_CANDIDATE); + + pKeyState->Function = pKeystroke->Function; + } + return TRUE; + } + } + + return FALSE; +} + +//+--------------------------------------------------------------------------- +// +// CCompositionProcessorEngine::IsKeyKeystrokeRange +// +//---------------------------------------------------------------------------- + +BOOL CCompositionProcessorEngine::IsKeystrokeRange(UINT uCode, _Out_ _KEYSTROKE_STATE *pKeyState, CANDIDATE_MODE candidateMode) +{ + if (pKeyState == nullptr) + { + return FALSE; + } + + pKeyState->Category = CATEGORY_NONE; + pKeyState->Function = FUNCTION_NONE; + + if (_candidateListIndexRange.IsRange(uCode)) + { + if (candidateMode == CANDIDATE_PHRASE) + { + // Candidate phrase could specify modifier + if ((GetCandidateListPhraseModifier() == 0 && Global::ModifiersValue == 0) || + (GetCandidateListPhraseModifier() != 0 && Global::CheckModifiers(Global::ModifiersValue, GetCandidateListPhraseModifier()))) + { + pKeyState->Category = CATEGORY_PHRASE; pKeyState->Function = FUNCTION_SELECT_BY_NUMBER; + return TRUE; + } + else + { + pKeyState->Category = CATEGORY_INVOKE_COMPOSITION_EDIT_SESSION; pKeyState->Function = FUNCTION_FINALIZE_TEXTSTORE_AND_INPUT; + return FALSE; + } + } + else if (candidateMode == CANDIDATE_WITH_NEXT_COMPOSITION) + { + // Candidate phrase could specify modifier + if ((GetCandidateListPhraseModifier() == 0 && Global::ModifiersValue == 0) || + (GetCandidateListPhraseModifier() != 0 && Global::CheckModifiers(Global::ModifiersValue, GetCandidateListPhraseModifier()))) + { + pKeyState->Category = CATEGORY_CANDIDATE; pKeyState->Function = FUNCTION_SELECT_BY_NUMBER; + return TRUE; + } + // else next composition + } + else if (candidateMode != CANDIDATE_NONE) + { + pKeyState->Category = CATEGORY_CANDIDATE; pKeyState->Function = FUNCTION_SELECT_BY_NUMBER; + return TRUE; + } + } + return FALSE; +} \ No newline at end of file diff --git a/CompositionProcessorEngine.h b/CompositionProcessorEngine.h new file mode 100644 index 00000000..8b6ac5d1 --- /dev/null +++ b/CompositionProcessorEngine.h @@ -0,0 +1,195 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + + +#pragma once + +#include "sal.h" +#include "TableDictionaryEngine.h" +#include "KeyHandlerEditSession.h" +#include "SampleIMEBaseStructure.h" +#include "FileMapping.h" +#include "Compartment.h" +#include "define.h" + +class CCompositionProcessorEngine +{ +public: + CCompositionProcessorEngine(void); + ~CCompositionProcessorEngine(void); + + BOOL SetupLanguageProfile(LANGID langid, REFGUID guidLanguageProfile, _In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId, BOOL isSecureMode, BOOL isComLessMode); + + // Get language profile. + GUID GetLanguageProfile(LANGID *plangid) + { + *plangid = _langid; + return _guidProfile; + } + // Get locale + LCID GetLocale() + { + return MAKELCID(_langid, SORT_DEFAULT); + } + + BOOL IsVirtualKeyNeed(UINT uCode, _In_reads_(1) WCHAR *pwch, BOOL fComposing, CANDIDATE_MODE candidateMode, BOOL hasCandidateWithWildcard, _Out_opt_ _KEYSTROKE_STATE *pKeyState); + + BOOL AddVirtualKey(WCHAR wch); + void RemoveVirtualKey(DWORD_PTR dwIndex); + void PurgeVirtualKey(); + + DWORD_PTR GetVirtualKeyLength() { return _keystrokeBuffer.GetLength(); } + WCHAR GetVirtualKey(DWORD_PTR dwIndex); + + void GetReadingStrings(_Inout_ CSampleImeArray *pReadingStrings, _Out_ BOOL *pIsWildcardIncluded); + void GetCandidateList(_Inout_ CSampleImeArray *pCandidateList, BOOL isIncrementalWordSearch, BOOL isWildcardSearch); + void GetCandidateStringInConverted(CStringRange &searchString, _In_ CSampleImeArray *pCandidateList); + + // Preserved key handler + void OnPreservedKey(REFGUID rguid, _Out_ BOOL *pIsEaten, _In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId); + + // Punctuation + BOOL IsPunctuation(WCHAR wch); + WCHAR GetPunctuation(WCHAR wch); + + BOOL IsDoubleSingleByte(WCHAR wch); + BOOL IsWildcard() { return _isWildcard; } + BOOL IsDisableWildcardAtFirst() { return _isDisableWildcardAtFirst; } + BOOL IsWildcardChar(WCHAR wch) { return ((IsWildcardOneChar(wch) || IsWildcardAllChar(wch)) ? TRUE : FALSE); } + BOOL IsWildcardOneChar(WCHAR wch) { return (wch==L'?' ? TRUE : FALSE); } + BOOL IsWildcardAllChar(WCHAR wch) { return (wch==L'*' ? TRUE : FALSE); } + BOOL IsMakePhraseFromText() { return _hasMakePhraseFromText; } + BOOL IsKeystrokeSort() { return _isKeystrokeSort; } + + // Dictionary engine + BOOL IsDictionaryAvailable() { return (_pTableDictionaryEngine ? TRUE : FALSE); } + + // Language bar control + void SetLanguageBarStatus(DWORD status, BOOL isSet); + + void ConversionModeCompartmentUpdated(_In_ ITfThreadMgr *pThreadMgr); + + void ShowAllLanguageBarIcons(); + void HideAllLanguageBarIcons(); + + inline CCandidateRange *GetCandidateListIndexRange() { return &_candidateListIndexRange; } + inline UINT GetCandidateListPhraseModifier() { return _candidateListPhraseModifier; } + inline UINT GetCandidateWindowWidth() { return _candidateWndWidth; } + +private: + void InitKeyStrokeTable(); + BOOL InitLanguageBar(_In_ CLangBarItemButton *pLanguageBar, _In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId, REFGUID guidCompartment); + + struct _KEYSTROKE; + BOOL IsVirtualKeyKeystrokeComposition(UINT uCode, _Out_opt_ _KEYSTROKE_STATE *pKeyState, KEYSTROKE_FUNCTION function); + BOOL IsVirtualKeyKeystrokeCandidate(UINT uCode, _In_ _KEYSTROKE_STATE *pKeyState, CANDIDATE_MODE candidateMode, _Out_ BOOL *pfRetCode, _In_ CSampleImeArray<_KEYSTROKE> *pKeystrokeMetric); + BOOL IsKeystrokeRange(UINT uCode, _Out_ _KEYSTROKE_STATE *pKeyState, CANDIDATE_MODE candidateMode); + + void SetupKeystroke(); + void SetupPreserved(_In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId); + void SetupConfiguration(); + void SetupLanguageBar(_In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId, BOOL isSecureMode); + void SetKeystrokeTable(_Inout_ CSampleImeArray<_KEYSTROKE> *pKeystroke); + void SetupPunctuationPair(); + void CreateLanguageBarButton(DWORD dwEnable, GUID guidLangBar, _In_z_ LPCWSTR pwszDescriptionValue, _In_z_ LPCWSTR pwszTooltipValue, DWORD dwOnIconIndex, DWORD dwOffIconIndex, _Outptr_result_maybenull_ CLangBarItemButton **ppLangBarItemButton, BOOL isSecureMode); + void SetInitialCandidateListRange(); + void SetDefaultCandidateTextFont(); + void InitializeSampleIMECompartment(_In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId); + + class XPreservedKey; + void SetPreservedKey(const CLSID clsid, TF_PRESERVEDKEY & tfPreservedKey, _In_z_ LPCWSTR pwszDescription, _Out_ XPreservedKey *pXPreservedKey); + BOOL InitPreservedKey(_In_ XPreservedKey *pXPreservedKey, _In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId); + BOOL CheckShiftKeyOnly(_In_ CSampleImeArray *pTSFPreservedKeyTable); + + static HRESULT CompartmentCallback(_In_ void *pv, REFGUID guidCompartment); + void PrivateCompartmentsUpdated(_In_ ITfThreadMgr *pThreadMgr); + void KeyboardOpenCompartmentUpdated(_In_ ITfThreadMgr *pThreadMgr); + + + BOOL SetupDictionaryFile(); + CFile* GetDictionaryFile(); + +private: + struct _KEYSTROKE + { + UINT VirtualKey; + UINT Modifiers; + KEYSTROKE_FUNCTION Function; + + _KEYSTROKE() + { + VirtualKey = 0; + Modifiers = 0; + Function = FUNCTION_NONE; + } + }; + _KEYSTROKE _keystrokeTable[26]; + + CTableDictionaryEngine* _pTableDictionaryEngine; + CStringRange _keystrokeBuffer; + + BOOL _hasWildcardIncludedInKeystrokeBuffer; + + LANGID _langid; + GUID _guidProfile; + TfClientId _tfClientId; + + CSampleImeArray<_KEYSTROKE> _KeystrokeComposition; + CSampleImeArray<_KEYSTROKE> _KeystrokeCandidate; + CSampleImeArray<_KEYSTROKE> _KeystrokeCandidateWildcard; + CSampleImeArray<_KEYSTROKE> _KeystrokeCandidateSymbol; + CSampleImeArray<_KEYSTROKE> _KeystrokeSymbol; + + // Preserved key data + class XPreservedKey + { + public: + XPreservedKey(); + ~XPreservedKey(); + BOOL UninitPreservedKey(_In_ ITfThreadMgr *pThreadMgr); + + public: + CSampleImeArray TSFPreservedKeyTable; + GUID Guid; + LPCWSTR Description; + }; + + XPreservedKey _PreservedKey_IMEMode; + XPreservedKey _PreservedKey_DoubleSingleByte; + XPreservedKey _PreservedKey_Punctuation; + + // Punctuation data + CSampleImeArray _PunctuationPair; + CSampleImeArray _PunctuationNestPair; + + // Language bar data + CLangBarItemButton* _pLanguageBar_IMEMode; + CLangBarItemButton* _pLanguageBar_DoubleSingleByte; + CLangBarItemButton* _pLanguageBar_Punctuation; + + // Compartment + CCompartment* _pCompartmentConversion; + CCompartmentEventSink* _pCompartmentConversionEventSink; + CCompartmentEventSink* _pCompartmentKeyboardOpenEventSink; + CCompartmentEventSink* _pCompartmentDoubleSingleByteEventSink; + CCompartmentEventSink* _pCompartmentPunctuationEventSink; + + // Configuration data + BOOL _isWildcard : 1; + BOOL _isDisableWildcardAtFirst : 1; + BOOL _hasMakePhraseFromText : 1; + BOOL _isKeystrokeSort : 1; + BOOL _isComLessMode : 1; + CCandidateRange _candidateListIndexRange; + UINT _candidateListPhraseModifier; + UINT _candidateWndWidth; + + CFileMapping* _pDictionaryFile; + + static const int OUT_OF_FILE_INDEX = -1; +}; + diff --git a/Define.h b/Define.h new file mode 100644 index 00000000..2e7fe01a --- /dev/null +++ b/Define.h @@ -0,0 +1,48 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#pragma once +#include "resource.h" + +#define TEXTSERVICE_MODEL L"Apartment" +#define TEXTSERVICE_LANGID MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED) +#define TEXTSERVICE_ICON_INDEX -IDIS_SAMPLEIME +#define TEXTSERVICE_DIC L"SampleIMESimplifiedQuanPin.txt" + +#define IME_MODE_ON_ICON_INDEX IDI_IME_MODE_ON +#define IME_MODE_OFF_ICON_INDEX IDI_IME_MODE_OFF +#define IME_DOUBLE_ON_INDEX IDI_DOUBLE_SINGLE_BYTE_ON +#define IME_DOUBLE_OFF_INDEX IDI_DOUBLE_SINGLE_BYTE_OFF +#define IME_PUNCTUATION_ON_INDEX IDI_PUNCTUATION_ON +#define IME_PUNCTUATION_OFF_INDEX IDI_PUNCTUATION_OFF + +#define SAMPLEIME_FONT_DEFAULT L"Microsoft YaHei UI" + +//--------------------------------------------------------------------- +// defined Candidated Window +//--------------------------------------------------------------------- +#define CANDWND_ROW_WIDTH (30) +#define CANDWND_BORDER_COLOR (RGB(0x00, 0x00, 0x00)) +#define CANDWND_BORDER_WIDTH (2) +#define CANDWND_NUM_COLOR (RGB(0xB4, 0xB4, 0xB4)) +#define CANDWND_SELECTED_ITEM_COLOR (RGB(0xFF, 0xFF, 0xFF)) +#define CANDWND_SELECTED_BK_COLOR (RGB(0xA6, 0xA6, 0x00)) +#define CANDWND_ITEM_COLOR (RGB(0x00, 0x00, 0x00)) + +//--------------------------------------------------------------------- +// defined modifier +//--------------------------------------------------------------------- +#define _TF_MOD_ON_KEYUP_SHIFT_ONLY (0x00010000 | TF_MOD_ON_KEYUP) +#define _TF_MOD_ON_KEYUP_CONTROL_ONLY (0x00020000 | TF_MOD_ON_KEYUP) +#define _TF_MOD_ON_KEYUP_ALT_ONLY (0x00040000 | TF_MOD_ON_KEYUP) + +#define CAND_WIDTH (13) // * tmMaxCharWidth + +//--------------------------------------------------------------------- +// string length of CLSID +//--------------------------------------------------------------------- +#define CLSID_STRLEN (38) // strlen("{xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx}") \ No newline at end of file diff --git a/Dictionary/.gitattributes b/Dictionary/.gitattributes new file mode 100644 index 00000000..c7f878dc --- /dev/null +++ b/Dictionary/.gitattributes @@ -0,0 +1,3 @@ +# The *.txt files in this directory are UTF-16-encoded and should +# therefore be treated as pure binary data. +*.txt binary diff --git a/Dictionary/SampleIMESimplifiedQuanPin.txt b/Dictionary/SampleIMESimplifiedQuanPin.txt new file mode 100644 index 00000000..f73046fa Binary files /dev/null and b/Dictionary/SampleIMESimplifiedQuanPin.txt differ diff --git a/DictionaryParser.cpp b/DictionaryParser.cpp new file mode 100644 index 00000000..9c128691 --- /dev/null +++ b/DictionaryParser.cpp @@ -0,0 +1,222 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#include "Private.h" +#include "DictionaryParser.h" +#include "SampleIMEBaseStructure.h" + +//--------------------------------------------------------------------- +// +// ctor +// +//--------------------------------------------------------------------- + +CDictionaryParser::CDictionaryParser(LCID locale) +{ + _locale = locale; +} + +//--------------------------------------------------------------------- +// +// dtor +// +//--------------------------------------------------------------------- + +CDictionaryParser::~CDictionaryParser() +{ +} + +//--------------------------------------------------------------------- +// +// ParseLine +// +// dwBufLen - in character count +// +//--------------------------------------------------------------------- + +BOOL CDictionaryParser::ParseLine(_In_reads_(dwBufLen) LPCWSTR pwszBuffer, DWORD_PTR dwBufLen, _Out_ CParserStringRange *psrgKeyword, _Inout_opt_ CSampleImeArray *pValue) +{ + LPCWSTR pwszKeyWordDelimiter = nullptr; + pwszKeyWordDelimiter = GetToken(pwszBuffer, dwBufLen, Global::KeywordDelimiter, psrgKeyword); + if (!(pwszKeyWordDelimiter)) + { + return FALSE; // End of file + } + + dwBufLen -= (pwszKeyWordDelimiter - pwszBuffer); + pwszBuffer = pwszKeyWordDelimiter + 1; + dwBufLen--; + + // Get value. + if (pValue) + { + if (dwBufLen) + { + CParserStringRange* psrgValue = pValue->Append(); + if (!psrgValue) + { + return FALSE; + } + psrgValue->Set(pwszBuffer, dwBufLen); + RemoveWhiteSpaceFromBegin(psrgValue); + RemoveWhiteSpaceFromEnd(psrgValue); + RemoveStringDelimiter(psrgValue); + } + } + + return TRUE; +} + +//--------------------------------------------------------------------- +// +// GetToken +// +// dwBufLen - in character count +// +// return - pointer of delimiter which specified chDelimiter +// +//--------------------------------------------------------------------- +_Ret_maybenull_ +LPCWSTR CDictionaryParser::GetToken(_In_reads_(dwBufLen) LPCWSTR pwszBuffer, DWORD_PTR dwBufLen, _In_ const WCHAR chDelimiter, _Out_ CParserStringRange *psrgValue) +{ + WCHAR ch = '\0'; + + psrgValue->Set(pwszBuffer, dwBufLen); + + ch = *pwszBuffer; + while ((ch) && (ch != chDelimiter) && dwBufLen) + { + dwBufLen--; + pwszBuffer++; + + if (ch == Global::StringDelimiter) + { + while (*pwszBuffer && (*pwszBuffer != Global::StringDelimiter) && dwBufLen) + { + dwBufLen--; + pwszBuffer++; + } + if (*pwszBuffer && dwBufLen) + { + dwBufLen--; + pwszBuffer++; + } + else + { + return nullptr; + } + } + ch = *pwszBuffer; + } + + if (*pwszBuffer && dwBufLen) + { + LPCWSTR pwszStart = psrgValue->Get(); + + psrgValue->Set(pwszStart, pwszBuffer - pwszStart); + + RemoveWhiteSpaceFromBegin(psrgValue); + RemoveWhiteSpaceFromEnd(psrgValue); + RemoveStringDelimiter(psrgValue); + + return pwszBuffer; + } + + RemoveWhiteSpaceFromBegin(psrgValue); + RemoveWhiteSpaceFromEnd(psrgValue); + RemoveStringDelimiter(psrgValue); + + return nullptr; +} + +//--------------------------------------------------------------------- +// +// RemoveWhiteSpaceFromBegin +// RemoveWhiteSpaceFromEnd +// RemoveStringDelimiter +// +//--------------------------------------------------------------------- + +BOOL CDictionaryParser::RemoveWhiteSpaceFromBegin(_Inout_opt_ CStringRange *pString) +{ + DWORD_PTR dwIndexTrace = 0; // in char + + if (pString == nullptr) + { + return FALSE; + } + + if (SkipWhiteSpace(_locale, pString->Get(), pString->GetLength(), &dwIndexTrace) != S_OK) + { + return FALSE; + } + + pString->Set(pString->Get() + dwIndexTrace, pString->GetLength() - dwIndexTrace); + return TRUE; +} + +BOOL CDictionaryParser::RemoveWhiteSpaceFromEnd(_Inout_opt_ CStringRange *pString) +{ + if (pString == nullptr) + { + return FALSE; + } + + DWORD_PTR dwTotalBufLen = pString->GetLength(); + LPCWSTR pwszEnd = pString->Get() + dwTotalBufLen - 1; + + while (dwTotalBufLen && (IsSpace(_locale, *pwszEnd) || *pwszEnd == L'\r' || *pwszEnd == L'\n')) + { + pwszEnd--; + dwTotalBufLen--; + } + + pString->Set(pString->Get(), dwTotalBufLen); + return TRUE; +} + +BOOL CDictionaryParser::RemoveStringDelimiter(_Inout_opt_ CStringRange *pString) +{ + if (pString == nullptr) + { + return FALSE; + } + + if (pString->GetLength() >= 2) + { + if ((*pString->Get() == Global::StringDelimiter) && (*(pString->Get()+pString->GetLength()-1) == Global::StringDelimiter)) + { + pString->Set(pString->Get()+1, pString->GetLength()-2); + return TRUE; + } + } + + return FALSE; +} + +//--------------------------------------------------------------------- +// +// GetOneLine +// +// dwBufLen - in character count +// +//--------------------------------------------------------------------- + +DWORD_PTR CDictionaryParser::GetOneLine(_In_z_ LPCWSTR pwszBuffer, DWORD_PTR dwBufLen) +{ + DWORD_PTR dwIndexTrace = 0; // in char + + if (FAILED(FindChar(L'\r', pwszBuffer, dwBufLen, &dwIndexTrace))) + { + if (FAILED(FindChar(L'\0', pwszBuffer, dwBufLen, &dwIndexTrace))) + { + return dwBufLen; + } + } + + return dwIndexTrace; +} diff --git a/DictionaryParser.h b/DictionaryParser.h new file mode 100644 index 00000000..e943fc81 --- /dev/null +++ b/DictionaryParser.h @@ -0,0 +1,50 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + + +#pragma once + +#include "Globals.h" + +////////////////////////////////////////////////////////////////////// +// + +class CParserStringRange : public CStringRange +{ +public: + CParserStringRange() : CStringRange() + { + _fEscapeIncluded = FALSE; + } + + BOOL _fEscapeIncluded; // flag. This string range included escape sequence. +}; + +////////////////////////////////////////////////////////////////////// +// + +class CDictionaryParser +{ +public: + CDictionaryParser(LCID locale); + virtual ~CDictionaryParser(); + + BOOL ParseLine(_In_reads_(dwBufLen) LPCWSTR pwszBuffer, DWORD_PTR dwBufLen, _Out_ CParserStringRange *psrgKeyword, _Inout_opt_ CSampleImeArray *pValue = nullptr); + + // dwBufLen - in character count + _Ret_maybenull_ + LPCWSTR GetToken(_In_reads_(dwBufLen) LPCWSTR pwszBuffer, DWORD_PTR dwBufLen, _In_ const WCHAR chDelimiter, _Out_ CParserStringRange *srgKeyWord); + +protected: + BOOL RemoveWhiteSpaceFromBegin(_Inout_opt_ CStringRange *pString); + BOOL RemoveWhiteSpaceFromEnd(_Inout_opt_ CStringRange *pString); + BOOL RemoveStringDelimiter(_Inout_opt_ CStringRange *pString); + + DWORD_PTR GetOneLine(_In_z_ LPCWSTR pwszBuffer, DWORD_PTR dwBufLen); + + LCID _locale; // used for CompareString +}; diff --git a/DictionarySearch.cpp b/DictionarySearch.cpp new file mode 100644 index 00000000..a97a1389 --- /dev/null +++ b/DictionarySearch.cpp @@ -0,0 +1,250 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#include "Private.h" +#include "DictionarySearch.h" +#include "SampleIMEBaseStructure.h" + +//+--------------------------------------------------------------------------- +// +// ctor +// +//---------------------------------------------------------------------------- + +CDictionarySearch::CDictionarySearch(LCID locale, _In_ CFile *pFile, _In_ CStringRange *pSearchKeyCode) : CDictionaryParser(locale) +{ + _pFile = pFile; + _pSearchKeyCode = pSearchKeyCode; + _charIndex = 0; +} + +//+--------------------------------------------------------------------------- +// +// dtor +// +//---------------------------------------------------------------------------- + +CDictionarySearch::~CDictionarySearch() +{ +} + +//+--------------------------------------------------------------------------- +// +// FindPhrase +// +//---------------------------------------------------------------------------- + +BOOL CDictionarySearch::FindPhrase(_Out_ CDictionaryResult **ppdret) +{ + return FindWorker(FALSE, ppdret, FALSE); // NO WILDCARD +} + +//+--------------------------------------------------------------------------- +// +// FindPhraseForWildcard +// +//---------------------------------------------------------------------------- + +BOOL CDictionarySearch::FindPhraseForWildcard(_Out_ CDictionaryResult **ppdret) +{ + return FindWorker(FALSE, ppdret, TRUE); // Wildcard +} + +//+--------------------------------------------------------------------------- +// +// FindConvertedStringForWildcard +// +//---------------------------------------------------------------------------- + +BOOL CDictionarySearch::FindConvertedStringForWildcard(CDictionaryResult **ppdret) +{ + return FindWorker(TRUE, ppdret, TRUE); // Wildcard +} + +//+--------------------------------------------------------------------------- +// +// FindWorker +// +//---------------------------------------------------------------------------- + +BOOL CDictionarySearch::FindWorker(BOOL isTextSearch, _Out_ CDictionaryResult **ppdret, BOOL isWildcardSearch) +{ + DWORD_PTR dwTotalBufLen = GetBufferInWCharLength(); // in char + if (dwTotalBufLen == 0) + { + return FALSE; + } + + const WCHAR *pwch = GetBufferInWChar(); + DWORD_PTR indexTrace = 0; // in char + *ppdret = nullptr; + BOOL isFound = FALSE; + DWORD_PTR bufLenOneLine = 0; + +TryAgain: + bufLenOneLine = GetOneLine(&pwch[indexTrace], dwTotalBufLen); + if (bufLenOneLine == 0) + { + goto FindNextLine; + } + else + { + CParserStringRange keyword; + DWORD_PTR bufLen = 0; + LPWSTR pText = nullptr; + + if (!ParseLine(&pwch[indexTrace], bufLenOneLine, &keyword)) + { + return FALSE; // error + } + + if (!isTextSearch) + { + // Compare Dictionary key code and input key code + if (!isWildcardSearch) + { + if (CStringRange::Compare(_locale, &keyword, _pSearchKeyCode) != CSTR_EQUAL) + { + if (bufLen) + { + delete [] pText; + } + goto FindNextLine; + } + } + else + { + // Wildcard search + if (!CStringRange::WildcardCompare(_locale, _pSearchKeyCode, &keyword)) + { + if (bufLen) + { + delete [] pText; + } + goto FindNextLine; + } + } + } + else + { + // Compare Dictionary converted string and input string + CSampleImeArray convertedStrings; + if (!ParseLine(&pwch[indexTrace], bufLenOneLine, &keyword, &convertedStrings)) + { + if (bufLen) + { + delete [] pText; + } + return FALSE; + } + if (convertedStrings.Count() == 1) + { + CStringRange* pTempString = convertedStrings.GetAt(0); + + if (!isWildcardSearch) + { + if (CStringRange::Compare(_locale, pTempString, _pSearchKeyCode) != CSTR_EQUAL) + { + if (bufLen) + { + delete [] pText; + } + goto FindNextLine; + } + } + else + { + // Wildcard search + if (!CStringRange::WildcardCompare(_locale, _pSearchKeyCode, pTempString)) + { + if (bufLen) + { + delete [] pText; + } + goto FindNextLine; + } + } + } + else + { + if (bufLen) + { + delete [] pText; + } + goto FindNextLine; + } + } + + if (bufLen) + { + delete [] pText; + } + + // Prepare return's CDictionaryResult + *ppdret = new (std::nothrow) CDictionaryResult(); + if (!*ppdret) + { + return FALSE; + } + + CSampleImeArray valueStrings; + if (!ParseLine(&pwch[indexTrace], bufLenOneLine, &keyword, &valueStrings)) + { + if (*ppdret) + { + delete *ppdret; + *ppdret = nullptr; + } + return FALSE; + } + + (*ppdret)->_FindKeyCode = keyword; + (*ppdret)->_SearchKeyCode = *_pSearchKeyCode; + + for (UINT i = 0; i < valueStrings.Count(); i++) + { + CStringRange* findPhrase = (*ppdret)->_FindPhraseList.Append(); + if (findPhrase) + { + *findPhrase = *valueStrings.GetAt(i); + } + } + + // Seek to next line + isFound = TRUE; + } + +FindNextLine: + dwTotalBufLen -= bufLenOneLine; + if (dwTotalBufLen == 0) + { + indexTrace += bufLenOneLine; + _charIndex += indexTrace; + + if (!isFound && *ppdret) + { + delete *ppdret; + *ppdret = nullptr; + } + return (isFound ? TRUE : FALSE); // End of file + } + + indexTrace += bufLenOneLine; + if (pwch[indexTrace] == L'\r' || pwch[indexTrace] == L'\n' || pwch[indexTrace] == L'\0') + { + bufLenOneLine = 1; + goto FindNextLine; + } + + if (isFound) + { + _charIndex += indexTrace; + return TRUE; + } + + goto TryAgain; +} diff --git a/DictionarySearch.h b/DictionarySearch.h new file mode 100644 index 00000000..20ac3d03 --- /dev/null +++ b/DictionarySearch.h @@ -0,0 +1,76 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + + +#pragma once + +#include "File.h" +#include "DictionaryParser.h" +#include "SampleIMEBaseStructure.h" + +class CDictionaryResult; + +////////////////////////////////////////////////////////////////////// +// +// CDictionarySearch declaration. +// +////////////////////////////////////////////////////////////////////// + +class CDictionarySearch : CDictionaryParser +{ +public: + CDictionarySearch(LCID locale, _In_ CFile *pFile, _In_ CStringRange *pSearchKeyCode); + virtual ~CDictionarySearch(); + + BOOL FindPhrase(_Out_ CDictionaryResult **ppdret); + BOOL FindPhraseForWildcard(_Out_ CDictionaryResult **ppdret); + + BOOL FindConvertedStringForWildcard(CDictionaryResult **ppdret); + + CStringRange* _pSearchKeyCode; + + DWORD_PTR _charIndex; // in character. Always point start of line in dictionary file. + +private: + BOOL FindWorker(BOOL isTextSearch, _Out_ CDictionaryResult **ppdret, BOOL isWildcardSearch); + + DWORD_PTR GetBufferInWCharLength() + { + return (_pFile->GetFileSize() / sizeof(WCHAR)) - _charIndex; // in char count as a returned length. + } + + const WCHAR* GetBufferInWChar() + { + return _pFile->GetReadBufferPointer() + _charIndex; + } + + CFile* _pFile; +}; + +////////////////////////////////////////////////////////////////////// +// +// CDictionaryResult declaration. +// +////////////////////////////////////////////////////////////////////// + +class CDictionaryResult +{ +public: + CDictionaryResult() { } + virtual ~CDictionaryResult() { } + + CDictionaryResult& operator=(CDictionaryResult& dret) + { + _FindKeyCode = dret._FindKeyCode; + _FindPhraseList = dret._FindPhraseList; + return *this; + } + + CStringRange _SearchKeyCode; + CStringRange _FindKeyCode; + CSampleImeArray _FindPhraseList; +}; diff --git a/DisplayAttribute.cpp b/DisplayAttribute.cpp new file mode 100644 index 00000000..acc04dff --- /dev/null +++ b/DisplayAttribute.cpp @@ -0,0 +1,115 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#include "Private.h" +#include "globals.h" +#include "SampleIME.h" + +//+--------------------------------------------------------------------------- +// +// _ClearCompositionDisplayAttributes +// +//---------------------------------------------------------------------------- + +void CSampleIME::_ClearCompositionDisplayAttributes(TfEditCookie ec, _In_ ITfContext *pContext) +{ + ITfRange* pRangeComposition = nullptr; + ITfProperty* pDisplayAttributeProperty = nullptr; + + // get the compositon range. + if (FAILED(_pComposition->GetRange(&pRangeComposition))) + { + return; + } + + // get our the display attribute property + if (SUCCEEDED(pContext->GetProperty(GUID_PROP_ATTRIBUTE, &pDisplayAttributeProperty))) + { + // clear the value over the range + pDisplayAttributeProperty->Clear(ec, pRangeComposition); + + pDisplayAttributeProperty->Release(); + } + + pRangeComposition->Release(); +} + +//+--------------------------------------------------------------------------- +// +// _SetCompositionDisplayAttributes +// +//---------------------------------------------------------------------------- + +BOOL CSampleIME::_SetCompositionDisplayAttributes(TfEditCookie ec, _In_ ITfContext *pContext, TfGuidAtom gaDisplayAttribute) +{ + ITfRange* pRangeComposition = nullptr; + ITfProperty* pDisplayAttributeProperty = nullptr; + HRESULT hr = S_OK; + + // we need a range and the context it lives in + hr = _pComposition->GetRange(&pRangeComposition); + if (FAILED(hr)) + { + return FALSE; + } + + hr = E_FAIL; + + // get our the display attribute property + if (SUCCEEDED(pContext->GetProperty(GUID_PROP_ATTRIBUTE, &pDisplayAttributeProperty))) + { + VARIANT var; + // set the value over the range + // the application will use this guid atom to lookup the acutal rendering information + var.vt = VT_I4; // we're going to set a TfGuidAtom + var.lVal = gaDisplayAttribute; + + hr = pDisplayAttributeProperty->SetValue(ec, pRangeComposition, &var); + + pDisplayAttributeProperty->Release(); + } + + pRangeComposition->Release(); + return (hr == S_OK); +} + +//+--------------------------------------------------------------------------- +// +// _InitDisplayAttributeGuidAtom +// +// Because it's expensive to map our display attribute GUID to a TSF +// TfGuidAtom, we do it once when Activate is called. +//---------------------------------------------------------------------------- + +BOOL CSampleIME::_InitDisplayAttributeGuidAtom() +{ + ITfCategoryMgr* pCategoryMgr = nullptr; + HRESULT hr = CoCreateInstance(CLSID_TF_CategoryMgr, nullptr, CLSCTX_INPROC_SERVER, IID_ITfCategoryMgr, (void**)&pCategoryMgr); + + if (FAILED(hr)) + { + return FALSE; + } + + // register the display attribute for input text. + hr = pCategoryMgr->RegisterGUID(Global::SampleIMEGuidDisplayAttributeInput, &_gaDisplayAttributeInput); + if (FAILED(hr)) + { + goto Exit; + } + // register the display attribute for the converted text. + hr = pCategoryMgr->RegisterGUID(Global::SampleIMEGuidDisplayAttributeConverted, &_gaDisplayAttributeConverted); + if (FAILED(hr)) + { + goto Exit; + } + +Exit: + pCategoryMgr->Release(); + + return (hr == S_OK); +} diff --git a/DisplayAttributeInfo.cpp b/DisplayAttributeInfo.cpp new file mode 100644 index 00000000..d394594c --- /dev/null +++ b/DisplayAttributeInfo.cpp @@ -0,0 +1,227 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#include "Private.h" +#include "globals.h" +#include "DisplayAttributeInfo.h" +#include "TfInputProcessorProfile.h" + +//+--------------------------------------------------------------------------- +// +// The registry key and values +// +//---------------------------------------------------------------------------- + +// the registry values of the custmized display attributes +const WCHAR CDisplayAttributeInfoInput::_s_szValueName[] = L"DisplayAttributeInput"; +const WCHAR CDisplayAttributeInfoConverted::_s_szValueName[] = L"DisplayAttributeConverted"; + +// The descriptions +const WCHAR CDisplayAttributeInfoInput::_s_szDescription[] = L"Sample IME Text Service Display Attribute Input"; +const WCHAR CDisplayAttributeInfoConverted::_s_szDescription[] = L"Sample IME Text Service Display Attribute Converted"; + +//+--------------------------------------------------------------------------- +// +// DisplayAttribute +// +//---------------------------------------------------------------------------- + +const TF_DISPLAYATTRIBUTE CDisplayAttributeInfoInput::_s_DisplayAttribute = +{ + { TF_CT_COLORREF, RGB(0, 103,206) }, // text color + { TF_CT_NONE, 0 }, // background color (TF_CT_NONE => app default) + TF_LS_DOT, // underline style + FALSE, // underline boldness + { TF_CT_COLORREF, RGB(0, 103,206) }, // underline color + TF_ATTR_INPUT // attribute info +}; + +const TF_DISPLAYATTRIBUTE CDisplayAttributeInfoConverted::_s_DisplayAttribute = +{ + { TF_CT_COLORREF, RGB(255, 255, 255) }, // text color + { TF_CT_COLORREF, RGB( 0, 255, 255) }, // background color (TF_CT_NONE => app default) + TF_LS_NONE, // underline style + FALSE, // underline boldness + { TF_CT_NONE, 0 }, // underline color + TF_ATTR_TARGET_CONVERTED // attribute info +}; + +//+--------------------------------------------------------------------------- +// +// ctor +// +//---------------------------------------------------------------------------- + +CDisplayAttributeInfo::CDisplayAttributeInfo() +{ + DllAddRef(); + + _pguid = nullptr; + _pDisplayAttribute = nullptr; + _pValueName = nullptr; + + _refCount = 1; +} + +//+--------------------------------------------------------------------------- +// +// dtor +// +//---------------------------------------------------------------------------- + +CDisplayAttributeInfo::~CDisplayAttributeInfo() +{ + DllRelease(); +} + +//+--------------------------------------------------------------------------- +// +// QueryInterface +// +//---------------------------------------------------------------------------- + +STDAPI CDisplayAttributeInfo::QueryInterface(REFIID riid, _Outptr_ void **ppvObj) +{ + if (ppvObj == nullptr) + return E_INVALIDARG; + + *ppvObj = nullptr; + + if (IsEqualIID(riid, IID_IUnknown) || + IsEqualIID(riid, IID_ITfDisplayAttributeInfo)) + { + *ppvObj = (ITfDisplayAttributeInfo *)this; + } + + if (*ppvObj) + { + AddRef(); + return S_OK; + } + + return E_NOINTERFACE; +} + +//+--------------------------------------------------------------------------- +// +// AddRef +// +//---------------------------------------------------------------------------- + +ULONG CDisplayAttributeInfo::AddRef(void) +{ + return ++_refCount; +} + +//+--------------------------------------------------------------------------- +// +// Release +// +//---------------------------------------------------------------------------- + +ULONG CDisplayAttributeInfo::Release(void) +{ + LONG cr = --_refCount; + + assert(_refCount >= 0); + + if (_refCount == 0) + { + delete this; + } + + return cr; +} + +//+--------------------------------------------------------------------------- +// +// ITfDisplayAttributeInfo::GetGUID +// +//---------------------------------------------------------------------------- + +STDAPI CDisplayAttributeInfo::GetGUID(_Out_ GUID *pguid) +{ + if (pguid == nullptr) + return E_INVALIDARG; + + if (_pguid == nullptr) + return E_FAIL; + + *pguid = *_pguid; + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// ITfDisplayAttributeInfo::GetDescription +// +//---------------------------------------------------------------------------- + +STDAPI CDisplayAttributeInfo::GetDescription(_Out_ BSTR *pbstrDesc) +{ + BSTR tempDesc; + + if (pbstrDesc == nullptr) + { + return E_INVALIDARG; + } + + *pbstrDesc = nullptr; + + if ((tempDesc = SysAllocString(_pDescription)) == nullptr) + { + return E_OUTOFMEMORY; + } + + *pbstrDesc = tempDesc; + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// ITfDisplayAttributeInfo::GetAttributeInfo +// +//---------------------------------------------------------------------------- + +STDAPI CDisplayAttributeInfo::GetAttributeInfo(_Out_ TF_DISPLAYATTRIBUTE *ptfDisplayAttr) +{ + if (ptfDisplayAttr == nullptr) + { + return E_INVALIDARG; + } + + // return the default display attribute. + *ptfDisplayAttr = *_pDisplayAttribute; + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// ITfDisplayAttributeInfo::SetAttributeInfo +// +//---------------------------------------------------------------------------- + +STDAPI CDisplayAttributeInfo::SetAttributeInfo(_In_ const TF_DISPLAYATTRIBUTE *ptfDisplayAttr) +{ + ptfDisplayAttr; + + return E_NOTIMPL; +} + +//+--------------------------------------------------------------------------- +// +// ITfDisplayAttributeInfo::Reset +// +//---------------------------------------------------------------------------- + +STDAPI CDisplayAttributeInfo::Reset() +{ + return SetAttributeInfo(_pDisplayAttribute); +} diff --git a/DisplayAttributeInfo.h b/DisplayAttributeInfo.h new file mode 100644 index 00000000..441a11ca --- /dev/null +++ b/DisplayAttributeInfo.h @@ -0,0 +1,87 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + + +#pragma once + +//+--------------------------------------------------------------------------- +// +// CDisplayAttributeInfo class +// +//---------------------------------------------------------------------------- + +class CDisplayAttributeInfo : public ITfDisplayAttributeInfo +{ +public: + CDisplayAttributeInfo(); + ~CDisplayAttributeInfo(); + + // IUnknown + STDMETHODIMP QueryInterface(REFIID riid, _Outptr_ void **ppvObj); + STDMETHODIMP_(ULONG) AddRef(void); + STDMETHODIMP_(ULONG) Release(void); + + // ITfDisplayAttributeInfo + STDMETHODIMP GetGUID(_Out_ GUID *pguid); + STDMETHODIMP GetDescription(_Out_ BSTR *pbstrDesc); + STDMETHODIMP GetAttributeInfo(_Out_ TF_DISPLAYATTRIBUTE *pTSFDisplayAttr); + STDMETHODIMP SetAttributeInfo(_In_ const TF_DISPLAYATTRIBUTE *ptfDisplayAttr); + STDMETHODIMP Reset(); + +protected: + const GUID* _pguid; + const TF_DISPLAYATTRIBUTE* _pDisplayAttribute; + const WCHAR* _pDescription; + const WCHAR* _pValueName; + +private: + LONG _refCount; // COM ref count +}; + +//+--------------------------------------------------------------------------- +// +// CDisplayAttributeInfoInput class +// +//---------------------------------------------------------------------------- + +class CDisplayAttributeInfoInput : public CDisplayAttributeInfo +{ +public: + CDisplayAttributeInfoInput() + { + _pguid = &Global::SampleIMEGuidDisplayAttributeInput; + _pDisplayAttribute = &_s_DisplayAttribute; + _pDescription = _s_szDescription; + _pValueName = _s_szValueName; + } + + static const TF_DISPLAYATTRIBUTE _s_DisplayAttribute; + static const WCHAR _s_szDescription[]; + static const WCHAR _s_szValueName[]; +}; + +//+--------------------------------------------------------------------------- +// +// CDisplayAttributeInfoConverted class +// +//---------------------------------------------------------------------------- + +class CDisplayAttributeInfoConverted : public CDisplayAttributeInfo +{ +public: + CDisplayAttributeInfoConverted() + { + _pguid = &Global::SampleIMEGuidDisplayAttributeConverted; + _pDisplayAttribute = &_s_DisplayAttribute; + _pDescription = _s_szDescription; + _pValueName = _s_szValueName; + } + + static const TF_DISPLAYATTRIBUTE _s_DisplayAttribute; + static const WCHAR _s_szDescription[]; + static const WCHAR _s_szValueName[]; +}; diff --git a/DisplayAttributeProvider.cpp b/DisplayAttributeProvider.cpp new file mode 100644 index 00000000..81e266fb --- /dev/null +++ b/DisplayAttributeProvider.cpp @@ -0,0 +1,81 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#include "Private.h" +#include "globals.h" +#include "SampleIME.h" +#include "DisplayAttributeInfo.h" +#include "EnumDisplayAttributeInfo.h" + +//+--------------------------------------------------------------------------- +// +// ITfDisplayAttributeProvider::EnumDisplayAttributeInfo +// +//---------------------------------------------------------------------------- + +STDAPI CSampleIME::EnumDisplayAttributeInfo(__RPC__deref_out_opt IEnumTfDisplayAttributeInfo **ppEnum) +{ + CEnumDisplayAttributeInfo* pAttributeEnum = nullptr; + + if (ppEnum == nullptr) + { + return E_INVALIDARG; + } + + *ppEnum = nullptr; + + pAttributeEnum = new (std::nothrow) CEnumDisplayAttributeInfo(); + if (pAttributeEnum == nullptr) + { + return E_OUTOFMEMORY; + } + + *ppEnum = pAttributeEnum; + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// ITfDisplayAttributeProvider::GetDisplayAttributeInfo +// +//---------------------------------------------------------------------------- + +STDAPI CSampleIME::GetDisplayAttributeInfo(__RPC__in REFGUID guidInfo, __RPC__deref_out_opt ITfDisplayAttributeInfo **ppInfo) +{ + if (ppInfo == nullptr) + { + return E_INVALIDARG; + } + + *ppInfo = nullptr; + + // Which display attribute GUID? + if (IsEqualGUID(guidInfo, Global::SampleIMEGuidDisplayAttributeInput)) + { + *ppInfo = new (std::nothrow) CDisplayAttributeInfoInput(); + if ((*ppInfo) == nullptr) + { + return E_OUTOFMEMORY; + } + } + else if (IsEqualGUID(guidInfo, Global::SampleIMEGuidDisplayAttributeConverted)) + { + *ppInfo = new (std::nothrow) CDisplayAttributeInfoConverted(); + if ((*ppInfo) == nullptr) + { + return E_OUTOFMEMORY; + } + } + else + { + return E_INVALIDARG; + } + + + return S_OK; +} diff --git a/DisplayString.h b/DisplayString.h new file mode 100644 index 00000000..2036bf84 --- /dev/null +++ b/DisplayString.h @@ -0,0 +1,43 @@ +////////////////////////////////////////////////////////////////////// +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// +// DisplayString.h +// +// CDisplayString declaration. +// +////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "StringRange.h" +#include "PointerArray.h" + +class CDisplayString +{ +public: + CDisplayString() { } + ~CDisplayString() { } + + int Count() { } + // VOID SetDisplayString(int iIndex, WCHAR* pchText, USHORT cchMax, TF_DISPLAYATTRIBUTE tfDisplayAttribute) { } + // VOID GetDisplayString(int iIndex, WCHAR* pchText, USHORT cchMax, USHORT* pch, TF_DISPLAYATTRIBUTE tfDisplayAttribute) { } + VOID SetLogicalFont(LOGFONTW LogFont) { } + VOID GetLogicalFont(LOGFONTW* pLogFont) { } + +private: + //typedef struct _DISPLAY_STRING { + // CStringRange _StringRange; // Unicode string. + // // Length and MaximumLength is in character count. + // // Buffer doesn't add zero terminate. + // TF_DISPLAYATTRIBUTE _tfDisplayAttribute; // Display attribute for each array. + //} DISPLAY_STRING; + + // + // Array of DISPLAY_STRING + // + //CPointerArray _pDisplayString; + + // Logical font + LOGFONTW _logfont; +}; diff --git a/DllMain.cpp b/DllMain.cpp new file mode 100644 index 00000000..c3a48751 --- /dev/null +++ b/DllMain.cpp @@ -0,0 +1,54 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#include "Private.h" +#include "Globals.h" + +//+--------------------------------------------------------------------------- +// +// DllMain +// +//---------------------------------------------------------------------------- + +BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID pvReserved) +{ + pvReserved; + + switch (dwReason) + { + case DLL_PROCESS_ATTACH: + + Global::dllInstanceHandle = hInstance; + + if (!InitializeCriticalSectionAndSpinCount(&Global::CS, 0)) + { + return FALSE; + } + + if (!Global::RegisterWindowClass()) { + return FALSE; + } + + break; + + case DLL_PROCESS_DETACH: + + DeleteCriticalSection(&Global::CS); + + break; + + case DLL_THREAD_ATTACH: + + break; + + case DLL_THREAD_DETACH: + + break; + } + + return TRUE; +} diff --git a/EditSession.cpp b/EditSession.cpp new file mode 100644 index 00000000..0e8830b5 --- /dev/null +++ b/EditSession.cpp @@ -0,0 +1,99 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#include "Private.h" +#include "EditSession.h" +#include "SampleIME.h" + +//+--------------------------------------------------------------------------- +// +// ctor +// +//---------------------------------------------------------------------------- + +CEditSessionBase::CEditSessionBase(_In_ CSampleIME *pTextService, _In_ ITfContext *pContext) +{ + _refCount = 1; + _pContext = pContext; + _pContext->AddRef(); + + _pTextService = pTextService; + _pTextService->AddRef(); +} + +//+--------------------------------------------------------------------------- +// +// dtor +// +//---------------------------------------------------------------------------- + +CEditSessionBase::~CEditSessionBase() +{ + _pContext->Release(); + _pTextService->Release(); +} + +//+--------------------------------------------------------------------------- +// +// QueryInterface +// +//---------------------------------------------------------------------------- + +STDAPI CEditSessionBase::QueryInterface(REFIID riid, _Outptr_ void **ppvObj) +{ + if (ppvObj == nullptr) + { + return E_INVALIDARG; + } + + *ppvObj = nullptr; + + if (IsEqualIID(riid, IID_IUnknown) || + IsEqualIID(riid, IID_ITfEditSession)) + { + *ppvObj = (ITfLangBarItemButton *)this; + } + + if (*ppvObj) + { + AddRef(); + return S_OK; + } + + return E_NOINTERFACE; +} + +//+--------------------------------------------------------------------------- +// +// AddRef +// +//---------------------------------------------------------------------------- + +STDAPI_(ULONG) CEditSessionBase::AddRef(void) +{ + return ++_refCount; +} + +//+--------------------------------------------------------------------------- +// +// Release +// +//---------------------------------------------------------------------------- + +STDAPI_(ULONG) CEditSessionBase::Release(void) +{ + LONG cr = --_refCount; + + assert(_refCount >= 0); + + if (_refCount == 0) + { + delete this; + } + + return cr; +} diff --git a/EditSession.h b/EditSession.h new file mode 100644 index 00000000..23c0fd9c --- /dev/null +++ b/EditSession.h @@ -0,0 +1,33 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + + +#pragma once + +class CSampleIME; + +class CEditSessionBase : public ITfEditSession +{ +public: + CEditSessionBase(_In_ CSampleIME *pTextService, _In_ ITfContext *pContext); + virtual ~CEditSessionBase(); + + // IUnknown + STDMETHODIMP QueryInterface(REFIID riid, _Outptr_ void **ppvObj); + STDMETHODIMP_(ULONG) AddRef(void); + STDMETHODIMP_(ULONG) Release(void); + + // ITfEditSession + virtual STDMETHODIMP DoEditSession(TfEditCookie ec) = 0; + +protected: + ITfContext *_pContext; + CSampleIME *_pTextService; + +private: + LONG _refCount; // COM ref count +}; diff --git a/EndComposition.cpp b/EndComposition.cpp new file mode 100644 index 00000000..1f037cdb --- /dev/null +++ b/EndComposition.cpp @@ -0,0 +1,98 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#include "Private.h" +#include "Globals.h" +#include "EditSession.h" +#include "SampleIME.h" + +////////////////////////////////////////////////////////////////////// +// +// ITfEditSession +// CEditSessionBase +// CEndCompositionEditSession class +// +////////////////////////////////////////////////////////////////////// + +//+--------------------------------------------------------------------------- +// +// CEndCompositionEditSession +// +//---------------------------------------------------------------------------- + +class CEndCompositionEditSession : public CEditSessionBase +{ +public: + CEndCompositionEditSession(_In_ CSampleIME *pTextService, _In_ ITfContext *pContext) : CEditSessionBase(pTextService, pContext) + { + } + + // ITfEditSession + STDMETHODIMP DoEditSession(TfEditCookie ec) + { + _pTextService->_TerminateComposition(ec, _pContext, TRUE); + return S_OK; + } + +}; + +////////////////////////////////////////////////////////////////////// +// +// CSampleIME class +// +////////////////////////////////////////////////////////////////////// + +//+--------------------------------------------------------------------------- +// +// _TerminateComposition +// +//---------------------------------------------------------------------------- + +void CSampleIME::_TerminateComposition(TfEditCookie ec, _In_ ITfContext *pContext, BOOL isCalledFromDeactivate) +{ + isCalledFromDeactivate; + + if (_pComposition != nullptr) + { + // remove the display attribute from the composition range. + _ClearCompositionDisplayAttributes(ec, pContext); + + if (FAILED(_pComposition->EndComposition(ec))) + { + // if we fail to EndComposition, then we need to close the reverse reading window. + _DeleteCandidateList(TRUE, pContext); + } + + _pComposition->Release(); + _pComposition = nullptr; + + if (_pContext) + { + _pContext->Release(); + _pContext = nullptr; + } + } +} + +//+--------------------------------------------------------------------------- +// +// _EndComposition +// +//---------------------------------------------------------------------------- + +void CSampleIME::_EndComposition(_In_opt_ ITfContext *pContext) +{ + CEndCompositionEditSession *pEditSession = new (std::nothrow) CEndCompositionEditSession(this, pContext); + HRESULT hr = S_OK; + + if (nullptr != pEditSession) + { + pContext->RequestEditSession(_tfClientId, pEditSession, TF_ES_ASYNCDONTCARE | TF_ES_READWRITE, &hr); + pEditSession->Release(); + } +} + diff --git a/EnumDisplayAttributeInfo.cpp b/EnumDisplayAttributeInfo.cpp new file mode 100644 index 00000000..d3b5915b --- /dev/null +++ b/EnumDisplayAttributeInfo.cpp @@ -0,0 +1,226 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#include "Private.h" +#include "globals.h" +#include "SampleIME.h" +#include "DisplayAttributeInfo.h" +#include "EnumDisplayAttributeInfo.h" + +//+--------------------------------------------------------------------------- +// +// ctor +// +//---------------------------------------------------------------------------- + +CEnumDisplayAttributeInfo::CEnumDisplayAttributeInfo() +{ + DllAddRef(); + + _index = 0; + _refCount = 1; +} + +//+--------------------------------------------------------------------------- +// +// dtor +// +//---------------------------------------------------------------------------- + +CEnumDisplayAttributeInfo::~CEnumDisplayAttributeInfo() +{ + DllRelease(); +} + +//+--------------------------------------------------------------------------- +// +// QueryInterface +// +//---------------------------------------------------------------------------- + +STDAPI CEnumDisplayAttributeInfo::QueryInterface(REFIID riid, _Outptr_ void **ppvObj) +{ + if (ppvObj == nullptr) + return E_INVALIDARG; + + *ppvObj = nullptr; + + if (IsEqualIID(riid, IID_IUnknown) || + IsEqualIID(riid, IID_IEnumTfDisplayAttributeInfo)) + { + *ppvObj = (IEnumTfDisplayAttributeInfo *)this; + } + + if (*ppvObj) + { + AddRef(); + return S_OK; + } + + return E_NOINTERFACE; +} + + +//+--------------------------------------------------------------------------- +// +// AddRef +// +//---------------------------------------------------------------------------- + +STDAPI_(ULONG) CEnumDisplayAttributeInfo::AddRef() +{ + return ++_refCount; +} + +//+--------------------------------------------------------------------------- +// +// Release +// +//---------------------------------------------------------------------------- + +STDAPI_(ULONG) CEnumDisplayAttributeInfo::Release() +{ + LONG cr = --_refCount; + + assert(_refCount >= 0); + + if (_refCount == 0) + { + delete this; + } + + return cr; +} + +//+--------------------------------------------------------------------------- +// +// IEnumTfDisplayAttributeInfo::Clone +// +// Returns a copy of the object. +//---------------------------------------------------------------------------- + +STDAPI CEnumDisplayAttributeInfo::Clone(_Out_ IEnumTfDisplayAttributeInfo **ppEnum) +{ + CEnumDisplayAttributeInfo* pClone = nullptr; + + if (ppEnum == nullptr) + { + return E_INVALIDARG; + } + + *ppEnum = nullptr; + + pClone = new (std::nothrow) CEnumDisplayAttributeInfo(); + if ((pClone) == nullptr) + { + return E_OUTOFMEMORY; + } + + // the clone should match this object's state + pClone->_index = _index; + + *ppEnum = pClone; + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// IEnumTfDisplayAttributeInfo::Next +// +// Returns an array of display attribute info objects supported by this service. +//---------------------------------------------------------------------------- + +const int MAX_DISPLAY_ATTRIBUTE_INFO = 2; + +STDAPI CEnumDisplayAttributeInfo::Next(ULONG ulCount, __RPC__out_ecount_part(ulCount, *pcFetched) ITfDisplayAttributeInfo **rgInfo, __RPC__out ULONG *pcFetched) +{ + ULONG fetched; + + fetched = 0; + + if (ulCount == 0) + { + return S_OK; + } + if (rgInfo == nullptr) + { + return E_INVALIDARG; + } + *rgInfo = nullptr; + + while (fetched < ulCount) + { + ITfDisplayAttributeInfo* pDisplayAttributeInfo = nullptr; + + if (_index == 0) + { + pDisplayAttributeInfo = new (std::nothrow) CDisplayAttributeInfoInput(); + if ((pDisplayAttributeInfo) == nullptr) + { + return E_OUTOFMEMORY; + } + } + else if (_index == 1) + { + pDisplayAttributeInfo = new (std::nothrow) CDisplayAttributeInfoConverted(); + if ((pDisplayAttributeInfo) == nullptr) + { + return E_OUTOFMEMORY; + } + + } + else + { + break; + } + + *rgInfo = pDisplayAttributeInfo; + rgInfo++; + fetched++; + _index++; + } + + if (pcFetched != nullptr) + { + // technically this is only legal if ulCount == 1, but we won't check + *pcFetched = fetched; + } + + return (fetched == ulCount) ? S_OK : S_FALSE; +} + +//+--------------------------------------------------------------------------- +// +// IEnumTfDisplayAttributeInfo::Reset +// +// Resets the enumeration. +//---------------------------------------------------------------------------- + +STDAPI CEnumDisplayAttributeInfo::Reset() +{ + _index = 0; + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// IEnumTfDisplayAttributeInfo::Skip +// +// Skips past objects in the enumeration. +//---------------------------------------------------------------------------- + +STDAPI CEnumDisplayAttributeInfo::Skip(ULONG ulCount) +{ + if ((ulCount + _index) > MAX_DISPLAY_ATTRIBUTE_INFO || (ulCount + _index) < ulCount) + { + _index = MAX_DISPLAY_ATTRIBUTE_INFO; + return S_FALSE; + } + _index += ulCount; + return S_OK; +} diff --git a/EnumDisplayAttributeInfo.h b/EnumDisplayAttributeInfo.h new file mode 100644 index 00000000..fafc2040 --- /dev/null +++ b/EnumDisplayAttributeInfo.h @@ -0,0 +1,36 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#pragma once + +//+--------------------------------------------------------------------------- +// +// CEnumDisplayAttributeInfo +// +//---------------------------------------------------------------------------- + +class CEnumDisplayAttributeInfo : public IEnumTfDisplayAttributeInfo +{ +public: + CEnumDisplayAttributeInfo(); + ~CEnumDisplayAttributeInfo(); + + // IUnknown + STDMETHODIMP QueryInterface(REFIID riid, _Outptr_ void **ppvObj); + STDMETHODIMP_(ULONG) AddRef(void); + STDMETHODIMP_(ULONG) Release(void); + + // IEnumTfDisplayAttributeInfo + STDMETHODIMP Clone(_Out_ IEnumTfDisplayAttributeInfo **ppEnum); + STDMETHODIMP Next(ULONG ulCount, __RPC__out_ecount_part(ulCount, *pcFetched) ITfDisplayAttributeInfo **rgInfo, __RPC__out ULONG *pcFetched); + STDMETHODIMP Reset(); + STDMETHODIMP Skip(ULONG ulCount); + +private: + LONG _index; // next display attribute to enum + LONG _refCount; // COM ref count +}; diff --git a/EnumTfCandidates.cpp b/EnumTfCandidates.cpp new file mode 100644 index 00000000..f0270881 --- /dev/null +++ b/EnumTfCandidates.cpp @@ -0,0 +1,146 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#include "private.h" +#include "EnumTfCandidates.h" + +HRESULT CEnumTfCandidates::CreateInstance(_Out_ CEnumTfCandidates **ppobj, _In_ const CSampleImeArray &rgelm, UINT currentNum) +{ + if (ppobj == nullptr) + { + return E_INVALIDARG; + } + *ppobj = nullptr; + + *ppobj = new (std::nothrow) CEnumTfCandidates(rgelm, currentNum); + if (*ppobj == nullptr) + { + return E_OUTOFMEMORY; + } + + return S_OK; +} + +HRESULT CEnumTfCandidates::CreateInstance(REFIID riid, _Out_ void **ppvObj, _In_ const CSampleImeArray &rgelm, UINT currentNum) +{ + if (ppvObj == nullptr) + { + return E_POINTER; + } + *ppvObj = nullptr; + + *ppvObj = new (std::nothrow) CEnumTfCandidates(rgelm, currentNum); + if (*ppvObj == nullptr) + { + return E_OUTOFMEMORY; + } + + return ((CEnumTfCandidates*)(*ppvObj))->QueryInterface(riid, ppvObj); +} + +CEnumTfCandidates::CEnumTfCandidates(_In_ const CSampleImeArray &rgelm, UINT currentNum) +{ + _refCount = 0; + _rgelm = rgelm; + _currentCandidateStrIndex = currentNum; +} + +CEnumTfCandidates::~CEnumTfCandidates() +{ +} + +// +// IUnknown methods +// +STDMETHODIMP CEnumTfCandidates::QueryInterface(REFIID riid, _Outptr_ void **ppvObj) +{ + if (ppvObj == nullptr) + { + return E_POINTER; + } + *ppvObj = nullptr; + + if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, __uuidof(IEnumTfCandidates))) + { + *ppvObj = (IEnumTfCandidates*)this; + } + + if (*ppvObj == nullptr) + { + return E_NOINTERFACE; + } + + AddRef(); + return S_OK; +} + +STDMETHODIMP_(ULONG) CEnumTfCandidates::AddRef() +{ + return (ULONG)InterlockedIncrement(&_refCount); +} + +STDMETHODIMP_(ULONG) CEnumTfCandidates::Release() +{ + ULONG cRef = (ULONG)InterlockedDecrement(&_refCount); + if (0 < cRef) + { + return cRef; + } + + delete this; + return 0; +} + + +// +// IEnumTfCandidates methods +// +STDMETHODIMP CEnumTfCandidates::Next(ULONG ulCount, _Out_ ITfCandidateString **ppObj, _Out_ ULONG *pcFetched) +{ + ULONG fetched = 0; + if (ppObj == nullptr) + { + return E_INVALIDARG; + } + *ppObj = nullptr; + + while ((fetched < ulCount) && (_currentCandidateStrIndex < _rgelm.Count())) + { + *ppObj = *_rgelm.GetAt(_currentCandidateStrIndex); + _currentCandidateStrIndex++; + fetched++; + } + + if (pcFetched) + { + *pcFetched = fetched; + } + + return (fetched == ulCount) ? S_OK : S_FALSE; +} + +STDMETHODIMP CEnumTfCandidates::Skip(ULONG ulCount) +{ + while ((0 < ulCount) && (_currentCandidateStrIndex < _rgelm.Count())) + { + _currentCandidateStrIndex++; + ulCount--; + } + + return (0 < ulCount) ? S_FALSE : S_OK; +} + +STDMETHODIMP CEnumTfCandidates::Reset() +{ + _currentCandidateStrIndex = 0; + return S_OK; +} + +STDMETHODIMP CEnumTfCandidates::Clone(_Out_ IEnumTfCandidates **ppEnum) +{ + return CreateInstance(__uuidof(IEnumTfCandidates), (void**)ppEnum, _rgelm, _currentCandidateStrIndex); +} \ No newline at end of file diff --git a/EnumTfCandidates.h b/EnumTfCandidates.h new file mode 100644 index 00000000..5a9d6ddf --- /dev/null +++ b/EnumTfCandidates.h @@ -0,0 +1,40 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#pragma once + +#include "Globals.h" + +class CEnumTfCandidates : public IEnumTfCandidates +{ +protected: + // constructor/destructor + CEnumTfCandidates(_In_ const CSampleImeArray &rgelm, UINT currentNum); + + virtual ~CEnumTfCandidates(void); + +public: + // create instance + static HRESULT CreateInstance(_Out_ CEnumTfCandidates **ppobj, _In_ const CSampleImeArray &rgelm, UINT currentNum = 0); + static HRESULT CreateInstance(REFIID riid, _Out_ void **ppvObj, _In_ const CSampleImeArray &rgelm, UINT currentNum = 0); + + // IUnknown methods + virtual STDMETHODIMP QueryInterface(REFIID riid, _Outptr_ void **ppvObj); + virtual STDMETHODIMP_(ULONG) AddRef(void); + virtual STDMETHODIMP_(ULONG) Release(void); + + // IEnumTfCandidates methods + virtual STDMETHODIMP Next(ULONG ulCount, _Out_ ITfCandidateString **ppObj, _Out_ ULONG *pcFetched); + virtual STDMETHODIMP Skip(ULONG ulCount); + virtual STDMETHODIMP Reset(); + virtual STDMETHODIMP Clone(_Out_ IEnumTfCandidates **ppEnum); + +protected: + LONG _refCount; + CSampleImeArray _rgelm; + UINT _currentCandidateStrIndex; +}; diff --git a/File.cpp b/File.cpp new file mode 100644 index 00000000..59f1d137 --- /dev/null +++ b/File.cpp @@ -0,0 +1,254 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#ifndef UNICODE +#define UNICODE +#endif // !UNICODE + +#ifndef _UNICODE +#define _UNICODE +#endif // !UNICODE + +#include "Private.h" +#include "File.h" +#include "SampleIMEBaseStructure.h" + +//--------------------------------------------------------------------- +// +// ctor +// +//--------------------------------------------------------------------- + +CFile::CFile(UINT codePage) +{ + _codePage = codePage; + _fileHandle = nullptr; + _pReadBuffer = nullptr; + _fileSize = 0; + _filePosPointer = 0; + _pFileName = nullptr; +} + +//--------------------------------------------------------------------- +// +// dtor +// +//--------------------------------------------------------------------- + +CFile::~CFile() +{ + if (_pReadBuffer) + { + delete[] _pReadBuffer; + _pReadBuffer = nullptr; + } + if (_fileHandle) + { + CloseHandle(_fileHandle); + _fileHandle = nullptr; + } + if (_pFileName) + { + delete[] _pFileName; + _pFileName = nullptr; + } +} + +//--------------------------------------------------------------------- +// +// CreateFile +// +//--------------------------------------------------------------------- + +BOOL CFile::CreateFile(_In_ PCWSTR pFileName, DWORD desiredAccess, DWORD creationDisposition, DWORD sharedMode, + _Inout_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD flagsAndAttributes, + _Inout_opt_ HANDLE templateFileHandle) +{ + size_t fullPathLen = wcslen(pFileName); + if (!_pFileName) + { + _pFileName = new (std::nothrow) WCHAR[fullPathLen + 1]; + } + if (!_pFileName) + { + return FALSE; + } + + StringCchCopyN(_pFileName, fullPathLen + 1, pFileName, fullPathLen); + + _fileHandle = ::CreateFile(pFileName, desiredAccess, sharedMode, lpSecurityAttributes, creationDisposition, + flagsAndAttributes, templateFileHandle); + + if (_fileHandle == INVALID_HANDLE_VALUE) + { + return FALSE; + } + + _fileSize = ::GetFileSize(_fileHandle, NULL); + + return TRUE; +} + +//--------------------------------------------------------------------- +// +// SetupReadBuffer +// +//--------------------------------------------------------------------- + +BOOL CFile::SetupReadBuffer() +{ + const WCHAR *pWideBuffer = nullptr; + + _pReadBuffer = (const WCHAR *)new (std::nothrow) BYTE[_fileSize]; + if (!_pReadBuffer) + { + return FALSE; + } + + DWORD dwNumberOfByteRead = 0; + if (!ReadFile(_fileHandle, (LPVOID)_pReadBuffer, (DWORD)_fileSize, &dwNumberOfByteRead, NULL)) + { + delete[] _pReadBuffer; + _pReadBuffer = nullptr; + return FALSE; + } + + if (!IsTextUnicode(_pReadBuffer, dwNumberOfByteRead, NULL)) + { + // This is ASCII file. + // Read file with Unicode conversion. + int wideLength = 0; + + wideLength = MultiByteToWideChar(_codePage, 0, (LPCSTR)_pReadBuffer, dwNumberOfByteRead, NULL, 0); + if (wideLength <= 0) + { + delete[] _pReadBuffer; + _pReadBuffer = nullptr; + return FALSE; + } + + pWideBuffer = new (std::nothrow) WCHAR[wideLength]; + if (!pWideBuffer) + { + delete[] _pReadBuffer; + _pReadBuffer = nullptr; + return FALSE; + } + + wideLength = + MultiByteToWideChar(_codePage, 0, (LPCSTR)_pReadBuffer, (DWORD)_fileSize, (LPWSTR)pWideBuffer, wideLength); + if (wideLength <= 0) + { + delete[] pWideBuffer; + delete[] _pReadBuffer; + _pReadBuffer = nullptr; + return FALSE; + } + + _fileSize = wideLength * sizeof(WCHAR); + delete[] _pReadBuffer; + _pReadBuffer = pWideBuffer; + } + else if (_fileSize > sizeof(WCHAR)) + { + // Read file in allocated buffer + pWideBuffer = new (std::nothrow) WCHAR[_fileSize / sizeof(WCHAR) - 1]; + if (!pWideBuffer) + { + delete[] _pReadBuffer; + _pReadBuffer = nullptr; + return FALSE; + } + + // skip unicode byte-order signature + SetFilePointer(_fileHandle, sizeof(WCHAR), NULL, FILE_BEGIN); + + if (!ReadFile(_fileHandle, (LPVOID)pWideBuffer, (DWORD)(_fileSize - sizeof(WCHAR)), &dwNumberOfByteRead, NULL)) + { + delete[] pWideBuffer; + delete[] _pReadBuffer; + _pReadBuffer = nullptr; + return FALSE; + } + + _fileSize -= sizeof(WCHAR); + delete[] _pReadBuffer; + _pReadBuffer = pWideBuffer; + } + else + { + return FALSE; + } + + return TRUE; +} + +//+--------------------------------------------------------------------------- +// +// IsEndOfFile +// +//---------------------------------------------------------------------------- + +BOOL CFile::IsEndOfFile() +{ + return _fileSize == _filePosPointer ? TRUE : FALSE; +} + +//+--------------------------------------------------------------------------- +// +// NextLine +// +//---------------------------------------------------------------------------- + +VOID CFile::NextLine() +{ + DWORD_PTR totalBufLen = GetBufferInWCharLength(); + if (totalBufLen == 0) + { + goto SetEOF; + } + const WCHAR *pwch = GetBufferInWChar(); + + DWORD_PTR indexTrace = 0; // in char + + if (FindChar(L'\r', pwch, totalBufLen, &indexTrace) != S_OK) + { + goto SetEOF; + } + if (indexTrace >= DWORD_MAX - 1) + { + goto SetEOF; + } + + indexTrace++; // skip CR + totalBufLen -= indexTrace; + if (totalBufLen == 0) + { + goto SetEOF; + } + + if (pwch[indexTrace] != L'\n') + { + _filePosPointer += (indexTrace * sizeof(WCHAR)); + return; + } + + indexTrace++; + totalBufLen--; + if (totalBufLen == 0) + { + goto SetEOF; + } + + _filePosPointer += (indexTrace * sizeof(WCHAR)); + + return; + +SetEOF: + _filePosPointer = _fileSize; + return; +} diff --git a/File.h b/File.h new file mode 100644 index 00000000..97b73c3f --- /dev/null +++ b/File.h @@ -0,0 +1,70 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#pragma once + +// #include "Private.h" + +class CFile +{ +public: + CFile(UINT codePage = CP_ACP); + virtual ~CFile(); + + BOOL CreateFile(_In_ PCWSTR pFileName, DWORD desiredAccess, DWORD creationDisposition, + DWORD sharedMode = 0, _Inout_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes = nullptr, DWORD flagsAndAttributes = 0, _Inout_opt_ HANDLE templateFileHandle = nullptr); + + BOOL IsEndOfFile(); + VOID NextLine(); + + _Ret_maybenull_ + const WCHAR *GetReadBufferPointer() + { + if (!_pReadBuffer) + { + if (!SetupReadBuffer()) + { + return nullptr; + } + } + return _pReadBuffer; + } + + DWORD_PTR GetFileSize() { return _fileSize;} + + LPCWSTR GetFileName() { return _pFileName;} + +protected: + virtual BOOL SetupReadBuffer(); + +protected: + const WCHAR* _pReadBuffer; // read buffer memory. + DWORD_PTR _fileSize; // in byte. + HANDLE _fileHandle; // file handle for CreateFile + +private: + DWORD_PTR GetBufferInWCharLength() + { + if (_filePosPointer == 0 && _fileSize > 0) + { + // skip Unicode byte order mark + GetReadBufferPointer(); + } + return(_fileSize - _filePosPointer) / sizeof(WCHAR); // in char count as a returned length. + } + + const WCHAR *GetBufferInWChar() + { + const WCHAR *pwch = GetReadBufferPointer(); + return(const WCHAR*)((BYTE*)pwch + _filePosPointer); + } + +private: + UINT _codePage; // used for MultiByteToWideChar + DWORD_PTR _filePosPointer; // in byte. Always point start of line. + LPWSTR _pFileName; +}; diff --git a/FileMapping.cpp b/FileMapping.cpp new file mode 100644 index 00000000..fe9c1793 --- /dev/null +++ b/FileMapping.cpp @@ -0,0 +1,87 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#include "Private.h" +#include "FileMapping.h" +#include "Globals.h" + +//--------------------------------------------------------------------- +// +// ctor +// +//--------------------------------------------------------------------- + +CFileMapping::CFileMapping() : CFile() +{ + _fileMappingHandle = nullptr; + _pMapBuffer = nullptr; +} + +//--------------------------------------------------------------------- +// +// dtor +// +//--------------------------------------------------------------------- + +CFileMapping::~CFileMapping() +{ + if (_pMapBuffer) + { + UnmapViewOfFile(_pMapBuffer); + _pMapBuffer = nullptr; + _pReadBuffer = nullptr; + } + if (_fileMappingHandle) + { + CloseHandle(_fileMappingHandle); + _fileMappingHandle = nullptr; + } +} + +//--------------------------------------------------------------------- +// +// SetupReadBuffer +// +//--------------------------------------------------------------------- + +BOOL CFileMapping::SetupReadBuffer() +{ + if (_fileSize > sizeof(WCHAR)) + { + // + // Read file in file mapping + // + _fileMappingHandle = CreateFileMapping(_fileHandle, NULL, PAGE_READONLY, 0, 0, NULL); + if (_fileMappingHandle) + { + _pMapBuffer = (const WCHAR *)MapViewOfFile(_fileMappingHandle, FILE_MAP_READ, 0, 0, 0); + if (_pMapBuffer) + { + if (IsTextUnicode(_pMapBuffer, (int)_fileSize, NULL)) + { + _pReadBuffer = (WCHAR*)_pMapBuffer; + + // skip Unicode byte order mark + if (*((WCHAR*)_pMapBuffer) == Global::UnicodeByteOrderMark) + { + _pReadBuffer++; + _fileSize--; + } + return TRUE; + } + + UnmapViewOfFile(_pReadBuffer); + _pReadBuffer = nullptr; + } + + CloseHandle(_fileMappingHandle); + _fileMappingHandle = nullptr; + } + } + + return FALSE; +} diff --git a/FileMapping.h b/FileMapping.h new file mode 100644 index 00000000..c5e3ea5b --- /dev/null +++ b/FileMapping.h @@ -0,0 +1,46 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#pragma once + +#include "File.h" + +class CFileMapping : public CFile +{ +public: + CFileMapping(); + virtual ~CFileMapping(); + + BOOL CreateFile(_In_ PCWSTR pFileName, DWORD desiredAccess, DWORD creationDisposition, + DWORD sharedMode = 0, _Inout_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes = nullptr, DWORD flagsAndAttributes = 0, _Inout_opt_ HANDLE templateFileHandle = nullptr) + + { + return CFile::CreateFile(pFileName, desiredAccess, creationDisposition, + sharedMode, lpSecurityAttributes, flagsAndAttributes, templateFileHandle); + } + + BOOL IsEndOfFile() + { + return CFile::IsEndOfFile(); + } + VOID NextLine() + { + CFile::NextLine(); + } + + const WCHAR *GetReadBufferPointer() { return CFile::GetReadBufferPointer(); } + DWORD_PTR GetFileSize() { return CFile::GetFileSize(); } + + LPCWSTR GetFileName() { return CFile::GetFileName(); } + +protected: + BOOL SetupReadBuffer(); + +private: + HANDLE _fileMappingHandle; // file handle for CreateFileMapping + const VOID *_pMapBuffer; // read buffer memory. +}; diff --git a/FunctionProviderSink.cpp b/FunctionProviderSink.cpp new file mode 100644 index 00000000..d3e68a32 --- /dev/null +++ b/FunctionProviderSink.cpp @@ -0,0 +1,55 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#include "Private.h" +#include "SampleIME.h" +#include "SearchCandidateProvider.h" + +//+--------------------------------------------------------------------------- +// +// _InitFunctionProviderSink +// +//---------------------------------------------------------------------------- + +BOOL CSampleIME::_InitFunctionProviderSink() +{ + ITfSourceSingle* pSourceSingle = nullptr; + BOOL ret = FALSE; + if (SUCCEEDED(_pThreadMgr->QueryInterface(IID_ITfSourceSingle, (void **)&pSourceSingle))) + { + IUnknown* punk = nullptr; + if (SUCCEEDED(QueryInterface(IID_IUnknown, (void **)&punk))) + { + if (SUCCEEDED(pSourceSingle->AdviseSingleSink(_tfClientId, IID_ITfFunctionProvider, punk))) + { + if (SUCCEEDED(CSearchCandidateProvider::CreateInstance(&_pITfFnSearchCandidateProvider, (ITfTextInputProcessorEx*)this))) + { + ret = TRUE; + } + } + punk->Release(); + } + pSourceSingle->Release(); + } + return ret; +} + +//+--------------------------------------------------------------------------- +// +// _UninitFunctionProviderSink +// +//---------------------------------------------------------------------------- + +void CSampleIME::_UninitFunctionProviderSink() +{ + ITfSourceSingle* pSourceSingle = nullptr; + if (SUCCEEDED(_pThreadMgr->QueryInterface(IID_ITfSourceSingle, (void **)&pSourceSingle))) + { + pSourceSingle->UnadviseSingleSink(_tfClientId, IID_ITfFunctionProvider); + pSourceSingle->Release(); + } +} diff --git a/GetTextExtentEditSession.cpp b/GetTextExtentEditSession.cpp new file mode 100644 index 00000000..cf0733f6 --- /dev/null +++ b/GetTextExtentEditSession.cpp @@ -0,0 +1,43 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#include "Private.h" +#include "EditSession.h" +#include "GetTextExtentEditSession.h" +#include "TfTextLayoutSink.h" + +//+--------------------------------------------------------------------------- +// +// ctor +// +//---------------------------------------------------------------------------- + +CGetTextExtentEditSession::CGetTextExtentEditSession(_In_ CSampleIME *pTextService, _In_ ITfContext *pContext, _In_ ITfContextView *pContextView, _In_ ITfRange *pRangeComposition, _In_ CTfTextLayoutSink *pTfTextLayoutSink) : CEditSessionBase(pTextService, pContext) +{ + _pContextView = pContextView; + _pRangeComposition = pRangeComposition; + _pTfTextLayoutSink = pTfTextLayoutSink; +} + +//+--------------------------------------------------------------------------- +// +// ITfEditSession::DoEditSession +// +//---------------------------------------------------------------------------- + +STDAPI CGetTextExtentEditSession::DoEditSession(TfEditCookie ec) +{ + RECT rc = {0, 0, 0, 0}; + BOOL isClipped = TRUE; + + if (SUCCEEDED(_pContextView->GetTextExt(ec, _pRangeComposition, &rc, &isClipped))) + { + _pTfTextLayoutSink->_LayoutChangeNotification(&rc); + } + + return S_OK; +} diff --git a/GetTextExtentEditSession.h b/GetTextExtentEditSession.h new file mode 100644 index 00000000..3a94a0a9 --- /dev/null +++ b/GetTextExtentEditSession.h @@ -0,0 +1,41 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#pragma once + +#include "EditSession.h" + +class CSampleIME; +class CTfTextLayoutSink; + +////////////////////////////////////////////////////////////////////// +// +// ITfEditSession +// CEditSessionBase +// CGetTextExtentEditSession class +// +////////////////////////////////////////////////////////////////////// + +//+--------------------------------------------------------------------------- +// +// CGetTextExtentEditSession +// +//---------------------------------------------------------------------------- + +class CGetTextExtentEditSession : public CEditSessionBase +{ +public: + CGetTextExtentEditSession(_In_ CSampleIME *pTextService, _In_ ITfContext *pContext, _In_ ITfContextView *pContextView, _In_ ITfRange *pRangeComposition, _In_ CTfTextLayoutSink *pTextLayoutSink); + + // ITfEditSession + STDMETHODIMP DoEditSession(TfEditCookie ec); + +private: + ITfContextView* _pContextView; + ITfRange* _pRangeComposition; + CTfTextLayoutSink* _pTfTextLayoutSink; +}; diff --git a/Globals.cpp b/Globals.cpp new file mode 100644 index 00000000..16ed6203 --- /dev/null +++ b/Globals.cpp @@ -0,0 +1,460 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#include "Private.h" +#include "resource.h" +#include "BaseWindow.h" +#include "define.h" +#include "SampleIMEBaseStructure.h" + +namespace Global { +HINSTANCE dllInstanceHandle; + +LONG dllRefCount = -1; + +CRITICAL_SECTION CS; +HFONT defaultlFontHandle; // Global font object we use everywhere + +//--------------------------------------------------------------------- +// SampleIME CLSID +//--------------------------------------------------------------------- +// {D2291A80-84D8-4641-9AB2-BDD1472C846B} +extern const CLSID SampleIMECLSID = { + 0xd2291a80, + 0x84d8, + 0x4641, + { 0x9a, 0xb2, 0xbd, 0xd1, 0x47, 0x2c, 0x84, 0x6b } +}; + +//--------------------------------------------------------------------- +// Profile GUID +//--------------------------------------------------------------------- +// {83955C0E-2C09-47a5-BCF3-F2B98E11EE8B} +extern const GUID SampleIMEGuidProfile = { + 0x83955c0e, + 0x2c09, + 0x47a5, + { 0xbc, 0xf3, 0xf2, 0xb9, 0x8e, 0x11, 0xee, 0x8b } +}; + +//--------------------------------------------------------------------- +// PreserveKey GUID +//--------------------------------------------------------------------- +// {4B62B54B-F828-43B5-9095-A96DF9CBDF38} +extern const GUID SampleIMEGuidImeModePreserveKey = { + 0x4b62b54b, + 0xf828, + 0x43b5, + { 0x90, 0x95, 0xa9, 0x6d, 0xf9, 0xcb, 0xdf, 0x38 } +}; + +// {5A08D6C4-4563-4E46-8DDB-65E75C4E73A3} +extern const GUID SampleIMEGuidDoubleSingleBytePreserveKey = { + 0x5a08d6c4, + 0x4563, + 0x4e46, + { 0x8d, 0xdb, 0x65, 0xe7, 0x5c, 0x4e, 0x73, 0xa3 } +}; + +// {175F062E-B961-4AED-A3DF-59F78A02862D} +extern const GUID SampleIMEGuidPunctuationPreserveKey = { + 0x175f062e, + 0xb961, + 0x4aed, + { 0xa3, 0xdf, 0x59, 0xf7, 0x8a, 0x2, 0x86, 0x2d } +}; + +//--------------------------------------------------------------------- +// Compartments +//--------------------------------------------------------------------- +// {101011C5-CF72-4F0C-A515-153019593F10} +extern const GUID SampleIMEGuidCompartmentDoubleSingleByte = { + 0x101011c5, + 0xcf72, + 0x4f0c, + { 0xa5, 0x15, 0x15, 0x30, 0x19, 0x59, 0x3f, 0x10 } +}; + +// {DD321BCC-A7F8-4561-9B61-9B3508C9BA97} +extern const GUID SampleIMEGuidCompartmentPunctuation = { + 0xdd321bcc, + 0xa7f8, + 0x4561, + { 0x9b, 0x61, 0x9b, 0x35, 0x8, 0xc9, 0xba, 0x97 } +}; + + +//--------------------------------------------------------------------- +// LanguageBars +//--------------------------------------------------------------------- + +// {89BE500C-9462-4070-9DB0-B467BB051327} +extern const GUID SampleIMEGuidLangBarIMEMode = { + 0x89be500c, + 0x9462, + 0x4070, + { 0x9d, 0xb0, 0xb4, 0x67, 0xbb, 0x5, 0x13, 0x27 } +}; + +// {6A11D9DE-46DB-455B-A257-2EB615746BF4} +extern const GUID SampleIMEGuidLangBarDoubleSingleByte = { + 0x6a11d9de, + 0x46db, + 0x455b, + { 0xa2, 0x57, 0x2e, 0xb6, 0x15, 0x74, 0x6b, 0xf4 } +}; + +// {F29C731A-A51E-49FB-8A3C-EE51752912E2} +extern const GUID SampleIMEGuidLangBarPunctuation = { + 0xf29c731a, + 0xa51e, + 0x49fb, + { 0x8a, 0x3c, 0xee, 0x51, 0x75, 0x29, 0x12, 0xe2 } +}; + +// {4C802E2C-8140-4436-A5E5-F7C544EBC9CD} +extern const GUID SampleIMEGuidDisplayAttributeInput = { + 0x4c802e2c, + 0x8140, + 0x4436, + { 0xa5, 0xe5, 0xf7, 0xc5, 0x44, 0xeb, 0xc9, 0xcd } +}; + +// {9A1CC683-F2A7-4701-9C6E-2DA69A5CD474} +extern const GUID SampleIMEGuidDisplayAttributeConverted = { + 0x9a1cc683, + 0xf2a7, + 0x4701, + { 0x9c, 0x6e, 0x2d, 0xa6, 0x9a, 0x5c, 0xd4, 0x74 } +}; + + +//--------------------------------------------------------------------- +// UI element +//--------------------------------------------------------------------- + +// {84B0749F-8DE7-4732-907A-3BCB150A01A8} +extern const GUID SampleIMEGuidCandUIElement = { + 0x84b0749f, + 0x8de7, + 0x4732, + { 0x90, 0x7a, 0x3b, 0xcb, 0x15, 0xa, 0x1, 0xa8 } +}; + +//--------------------------------------------------------------------- +// Unicode byte order mark +//--------------------------------------------------------------------- +extern const WCHAR UnicodeByteOrderMark = 0xFEFF; + +//--------------------------------------------------------------------- +// dictionary table delimiter +//--------------------------------------------------------------------- +extern const WCHAR KeywordDelimiter = L'='; +extern const WCHAR StringDelimiter = L'\"'; + +//--------------------------------------------------------------------- +// defined item in setting file table [PreservedKey] section +//--------------------------------------------------------------------- +extern const WCHAR ImeModeDescription[] = L"Chinese/English input (Shift)"; +extern const int ImeModeOnIcoIndex = IME_MODE_ON_ICON_INDEX; +extern const int ImeModeOffIcoIndex = IME_MODE_OFF_ICON_INDEX; + +extern const WCHAR DoubleSingleByteDescription[] = L"Double/Single byte (Shift+Space)"; +extern const int DoubleSingleByteOnIcoIndex = IME_DOUBLE_ON_INDEX; +extern const int DoubleSingleByteOffIcoIndex = IME_DOUBLE_OFF_INDEX; + +extern const WCHAR PunctuationDescription[] = L"Chinese/English punctuation (Ctrl+.)"; +extern const int PunctuationOnIcoIndex = IME_PUNCTUATION_ON_INDEX; +extern const int PunctuationOffIcoIndex = IME_PUNCTUATION_OFF_INDEX; + +//--------------------------------------------------------------------- +// defined item in setting file table [LanguageBar] section +//--------------------------------------------------------------------- +extern const WCHAR LangbarImeModeDescription[] = L"Conversion mode"; +extern const WCHAR LangbarDoubleSingleByteDescription[] = L"Character width"; +extern const WCHAR LangbarPunctuationDescription[] = L"Punctuation"; + +//--------------------------------------------------------------------- +// windows class / titile / atom +//--------------------------------------------------------------------- +extern const WCHAR CandidateClassName[] = L"SampleIME.CandidateWindow"; +ATOM AtomCandidateWindow; + +extern const WCHAR ShadowClassName[] = L"SampleIME.ShadowWindow"; +ATOM AtomShadowWindow; + +extern const WCHAR ScrollBarClassName[] = L"SampleIME.ScrollBarWindow"; +ATOM AtomScrollBarWindow; + +BOOL RegisterWindowClass() +{ + if (!CBaseWindow::_InitWindowClass(CandidateClassName, &AtomCandidateWindow)) + { + return FALSE; + } + if (!CBaseWindow::_InitWindowClass(ShadowClassName, &AtomShadowWindow)) + { + return FALSE; + } + if (!CBaseWindow::_InitWindowClass(ScrollBarClassName, &AtomScrollBarWindow)) + { + return FALSE; + } + return TRUE; +} + +//--------------------------------------------------------------------- +// defined full width characters for Double/Single byte conversion +//--------------------------------------------------------------------- +extern const WCHAR FullWidthCharTable[] = { + // ! " # $ % & ' ( ) * + , - . / + 0x3000, 0xFF01, 0xFF02, 0xFF03, 0xFF04, 0xFF05, 0xFF06, 0xFF07, 0xFF08, 0xFF09, 0xFF0A, 0xFF0B, 0xFF0C, 0xFF0D, 0xFF0E, 0xFF0F, + // 0 1 2 3 4 5 6 7 8 9 : ; < = > ? + 0xFF10, 0xFF11, 0xFF12, 0xFF13, 0xFF14, 0xFF15, 0xFF16, 0xFF17, 0xFF18, 0xFF19, 0xFF1A, 0xFF1B, 0xFF1C, 0xFF1D, 0xFF1E, 0xFF1F, + // @ A B C D E F G H I J K L M N 0 + 0xFF20, 0xFF21, 0xFF22, 0xFF23, 0xFF24, 0xFF25, 0xFF26, 0xFF27, 0xFF28, 0xFF29, 0xFF2A, 0xFF2B, 0xFF2C, 0xFF2D, 0xFF2E, 0xFF2F, + // P Q R S T U V W X Y Z [ \ ] ^ _ + 0xFF30, 0xFF31, 0xFF32, 0xFF33, 0xFF34, 0xFF35, 0xFF36, 0xFF37, 0xFF38, 0xFF39, 0xFF3A, 0xFF3B, 0xFF3C, 0xFF3D, 0xFF3E, 0xFF3F, + // ' a b c d e f g h i j k l m n o + 0xFF40, 0xFF41, 0xFF42, 0xFF43, 0xFF44, 0xFF45, 0xFF46, 0xFF47, 0xFF48, 0xFF49, 0xFF4A, 0xFF4B, 0xFF4C, 0xFF4D, 0xFF4E, 0xFF4F, + // p q r s t u v w x y z { | } ~ + 0xFF50, 0xFF51, 0xFF52, 0xFF53, 0xFF54, 0xFF55, 0xFF56, 0xFF57, 0xFF58, 0xFF59, 0xFF5A, 0xFF5B, 0xFF5C, 0xFF5D, 0xFF5E +}; + +//--------------------------------------------------------------------- +// defined punctuation characters +//--------------------------------------------------------------------- +extern const struct _PUNCTUATION PunctuationTable[14] = { + {L'!', 0xFF01}, + {L'$', 0xFFE5}, + {L'&', 0x2014}, + {L'(', 0xFF08}, + {L')', 0xFF09}, + {L',', 0xFF0C}, + {L'.', 0x3002}, + {L':', 0xFF1A}, + {L';', 0xFF1B}, + {L'?', 0xFF1F}, + {L'@', 0x00B7}, + {L'\\', 0x3001}, + {L'^', 0x2026}, + {L'_', 0x2014} +}; + +//+--------------------------------------------------------------------------- +// +// CheckModifiers +// +//---------------------------------------------------------------------------- + +#define TF_MOD_ALLALT (TF_MOD_RALT | TF_MOD_LALT | TF_MOD_ALT) +#define TF_MOD_ALLCONTROL (TF_MOD_RCONTROL | TF_MOD_LCONTROL | TF_MOD_CONTROL) +#define TF_MOD_ALLSHIFT (TF_MOD_RSHIFT | TF_MOD_LSHIFT | TF_MOD_SHIFT) +#define TF_MOD_RLALT (TF_MOD_RALT | TF_MOD_LALT) +#define TF_MOD_RLCONTROL (TF_MOD_RCONTROL | TF_MOD_LCONTROL) +#define TF_MOD_RLSHIFT (TF_MOD_RSHIFT | TF_MOD_LSHIFT) + +#define CheckMod(m0, m1, mod) \ + if (m1 & TF_MOD_ ## mod ##) \ +{ \ + if (!(m0 & TF_MOD_ ## mod ##)) \ +{ \ + return FALSE; \ +} \ +} \ + else \ +{ \ + if ((m1 ^ m0) & TF_MOD_RL ## mod ##) \ +{ \ + return FALSE; \ +} \ +} \ + + + +BOOL CheckModifiers(UINT modCurrent, UINT mod) +{ + mod &= ~TF_MOD_ON_KEYUP; + + if (mod & TF_MOD_IGNORE_ALL_MODIFIER) + { + return TRUE; + } + + if (modCurrent == mod) + { + return TRUE; + } + + if (modCurrent && !mod) + { + return FALSE; + } + + CheckMod(modCurrent, mod, ALT); + CheckMod(modCurrent, mod, SHIFT); + CheckMod(modCurrent, mod, CONTROL); + + return TRUE; +} + +//+--------------------------------------------------------------------------- +// +// UpdateModifiers +// +// wParam - virtual-key code +// lParam - [0-15] Repeat count +// [16-23] Scan code +// [24] Extended key +// [25-28] Reserved +// [29] Context code +// [30] Previous key state +// [31] Transition state +//---------------------------------------------------------------------------- + +USHORT ModifiersValue = 0; +BOOL IsShiftKeyDownOnly = FALSE; +BOOL IsControlKeyDownOnly = FALSE; +BOOL IsAltKeyDownOnly = FALSE; + +BOOL UpdateModifiers(WPARAM wParam, LPARAM lParam) +{ + // high-order bit : key down + // low-order bit : toggled + SHORT sksMenu = GetKeyState(VK_MENU); + SHORT sksCtrl = GetKeyState(VK_CONTROL); + SHORT sksShft = GetKeyState(VK_SHIFT); + + switch (wParam & 0xff) + { + case VK_MENU: + // is VK_MENU down? + if (sksMenu & 0x8000) + { + // is extended key? + if (lParam & 0x01000000) + { + ModifiersValue |= (TF_MOD_RALT | TF_MOD_ALT); + } + else + { + ModifiersValue |= (TF_MOD_LALT | TF_MOD_ALT); + } + + // is previous key state up? + if (!(lParam & 0x40000000)) + { + // is VK_CONTROL and VK_SHIFT up? + if (!(sksCtrl & 0x8000) && !(sksShft & 0x8000)) + { + IsAltKeyDownOnly = TRUE; + } + else + { + IsShiftKeyDownOnly = FALSE; + IsControlKeyDownOnly = FALSE; + IsAltKeyDownOnly = FALSE; + } + } + } + break; + + case VK_CONTROL: + // is VK_CONTROL down? + if (sksCtrl & 0x8000) + { + // is extended key? + if (lParam & 0x01000000) + { + ModifiersValue |= (TF_MOD_RCONTROL | TF_MOD_CONTROL); + } + else + { + ModifiersValue |= (TF_MOD_LCONTROL | TF_MOD_CONTROL); + } + + // is previous key state up? + if (!(lParam & 0x40000000)) + { + // is VK_SHIFT and VK_MENU up? + if (!(sksShft & 0x8000) && !(sksMenu & 0x8000)) + { + IsControlKeyDownOnly = TRUE; + } + else + { + IsShiftKeyDownOnly = FALSE; + IsControlKeyDownOnly = FALSE; + IsAltKeyDownOnly = FALSE; + } + } + } + break; + + case VK_SHIFT: + // is VK_SHIFT down? + if (sksShft & 0x8000) + { + // is scan code 0x36(right shift)? + if (((lParam >> 16) & 0x00ff) == 0x36) + { + ModifiersValue |= (TF_MOD_RSHIFT | TF_MOD_SHIFT); + } + else + { + ModifiersValue |= (TF_MOD_LSHIFT | TF_MOD_SHIFT); + } + + // is previous key state up? + if (!(lParam & 0x40000000)) + { + // is VK_MENU and VK_CONTROL up? + if (!(sksMenu & 0x8000) && !(sksCtrl & 0x8000)) + { + IsShiftKeyDownOnly = TRUE; + } + else + { + IsShiftKeyDownOnly = FALSE; + IsControlKeyDownOnly = FALSE; + IsAltKeyDownOnly = FALSE; + } + } + } + break; + + default: + IsShiftKeyDownOnly = FALSE; + IsControlKeyDownOnly = FALSE; + IsAltKeyDownOnly = FALSE; + break; + } + + if (!(sksMenu & 0x8000)) + { + ModifiersValue &= ~TF_MOD_ALLALT; + } + if (!(sksCtrl & 0x8000)) + { + ModifiersValue &= ~TF_MOD_ALLCONTROL; + } + if (!(sksShft & 0x8000)) + { + ModifiersValue &= ~TF_MOD_ALLSHIFT; + } + + return TRUE; +} + +//--------------------------------------------------------------------- +// override CompareElements +//--------------------------------------------------------------------- +BOOL CompareElements(LCID locale, const CStringRange* pElement1, const CStringRange* pElement2) +{ + return (CStringRange::Compare(locale, (CStringRange*)pElement1, (CStringRange*)pElement2) == CSTR_EQUAL) ? TRUE : FALSE; +} +} \ No newline at end of file diff --git a/Globals.h b/Globals.h new file mode 100644 index 00000000..0996e6d3 --- /dev/null +++ b/Globals.h @@ -0,0 +1,162 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#pragma once + +#include "private.h" +#include "define.h" +#include "SampleIMEBaseStructure.h" + +void DllAddRef(); +void DllRelease(); + + +namespace Global { +//--------------------------------------------------------------------- +// inline +//--------------------------------------------------------------------- + +inline void SafeRelease(_In_ IUnknown *punk) +{ + if (punk != nullptr) + { + punk->Release(); + } +} + +inline void QuickVariantInit(_Inout_ VARIANT *pvar) +{ + pvar->vt = VT_EMPTY; +} + +inline void QuickVariantClear(_Inout_ VARIANT *pvar) +{ + switch (pvar->vt) + { + // some ovbious VTs that don't need to call VariantClear. + case VT_EMPTY: + case VT_NULL: + case VT_I2: + case VT_I4: + case VT_R4: + case VT_R8: + case VT_CY: + case VT_DATE: + case VT_I1: + case VT_UI1: + case VT_UI2: + case VT_UI4: + case VT_I8: + case VT_UI8: + case VT_INT: + case VT_UINT: + case VT_BOOL: + break; + + // Call release for VT_UNKNOWN. + case VT_UNKNOWN: + SafeRelease(pvar->punkVal); + break; + + default: + // we call OleAut32 for other VTs. + VariantClear(pvar); + break; + } + pvar->vt = VT_EMPTY; +} + +//+--------------------------------------------------------------------------- +// +// IsTooSimilar +// +// Return TRUE if the colors cr1 and cr2 are so similar that they +// are hard to distinguish. Used for deciding to use reverse video +// selection instead of system selection colors. +// +//---------------------------------------------------------------------------- + +inline BOOL IsTooSimilar(COLORREF cr1, COLORREF cr2) +{ + if ((cr1 | cr2) & 0xFF000000) // One color and/or the other isn't RGB, so algorithm doesn't apply + { + return FALSE; + } + + LONG DeltaR = abs(GetRValue(cr1) - GetRValue(cr2)); + LONG DeltaG = abs(GetGValue(cr1) - GetGValue(cr2)); + LONG DeltaB = abs(GetBValue(cr1) - GetBValue(cr2)); + + return DeltaR + DeltaG + DeltaB < 80; +} + +//--------------------------------------------------------------------- +// extern +//--------------------------------------------------------------------- +extern HINSTANCE dllInstanceHandle; + +extern ATOM AtomCandidateWindow; +extern ATOM AtomShadowWindow; +extern ATOM AtomScrollBarWindow; + +BOOL RegisterWindowClass(); + +extern LONG dllRefCount; + +extern CRITICAL_SECTION CS; +extern HFONT defaultlFontHandle; // Global font object we use everywhere + +extern const CLSID SampleIMECLSID; +extern const CLSID SampleIMEGuidProfile; +extern const CLSID SampleIMEGuidImeModePreserveKey; +extern const CLSID SampleIMEGuidDoubleSingleBytePreserveKey; +extern const CLSID SampleIMEGuidPunctuationPreserveKey; + +LRESULT CALLBACK ThreadKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam); +BOOL CheckModifiers(UINT uModCurrent, UINT uMod); +BOOL UpdateModifiers(WPARAM wParam, LPARAM lParam); + +extern USHORT ModifiersValue; +extern BOOL IsShiftKeyDownOnly; +extern BOOL IsControlKeyDownOnly; +extern BOOL IsAltKeyDownOnly; + +extern const GUID SampleIMEGuidCompartmentDoubleSingleByte; +extern const GUID SampleIMEGuidCompartmentPunctuation; + +extern const WCHAR FullWidthCharTable[]; +extern const struct _PUNCTUATION PunctuationTable[14]; + +extern const GUID SampleIMEGuidLangBarIMEMode; +extern const GUID SampleIMEGuidLangBarDoubleSingleByte; +extern const GUID SampleIMEGuidLangBarPunctuation; + +extern const GUID SampleIMEGuidDisplayAttributeInput; +extern const GUID SampleIMEGuidDisplayAttributeConverted; + +extern const GUID SampleIMEGuidCandUIElement; + +extern const WCHAR UnicodeByteOrderMark; +extern const WCHAR KeywordDelimiter; +extern const WCHAR StringDelimiter; + +extern const WCHAR ImeModeDescription[]; +extern const int ImeModeOnIcoIndex; +extern const int ImeModeOffIcoIndex; + +extern const WCHAR DoubleSingleByteDescription[]; +extern const int DoubleSingleByteOnIcoIndex; +extern const int DoubleSingleByteOffIcoIndex; + +extern const WCHAR PunctuationDescription[]; +extern const int PunctuationOnIcoIndex; +extern const int PunctuationOffIcoIndex; + +extern const WCHAR LangbarImeModeDescription[]; +extern const WCHAR LangbarDoubleSingleByteDescription[]; +extern const WCHAR LangbarPunctuationDescription[]; +} \ No newline at end of file diff --git a/KeyEventSink.cpp b/KeyEventSink.cpp new file mode 100644 index 00000000..41475912 --- /dev/null +++ b/KeyEventSink.cpp @@ -0,0 +1,457 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#include "Private.h" +#include "Globals.h" +#include "SampleIME.h" +#include "CandidateListUIPresenter.h" +#include "CompositionProcessorEngine.h" +#include "KeyHandlerEditSession.h" +#include "Compartment.h" + +// 0xF003, 0xF004 are the keys that the touch keyboard sends for next/previous +#define THIRDPARTY_NEXTPAGE static_cast(0xF003) +#define THIRDPARTY_PREVPAGE static_cast(0xF004) + +// Because the code mostly works with VKeys, here map a WCHAR back to a VKKey for certain +// vkeys that the IME handles specially +__inline UINT VKeyFromVKPacketAndWchar(UINT vk, WCHAR wch) +{ + UINT vkRet = vk; + if (LOWORD(vk) == VK_PACKET) + { + if (wch == L' ') + { + vkRet = VK_SPACE; + } + else if ((wch >= L'0') && (wch <= L'9')) + { + vkRet = static_cast(wch); + } + else if ((wch >= L'a') && (wch <= L'z')) + { + vkRet = (UINT)(L'A') + ((UINT)(L'z') - static_cast(wch)); + } + else if ((wch >= L'A') && (wch <= L'Z')) + { + vkRet = static_cast(wch); + } + else if (wch == THIRDPARTY_NEXTPAGE) + { + vkRet = VK_NEXT; + } + else if (wch == THIRDPARTY_PREVPAGE) + { + vkRet = VK_PRIOR; + } + } + return vkRet; +} + +//+--------------------------------------------------------------------------- +// +// _IsKeyEaten +// +//---------------------------------------------------------------------------- + +BOOL CSampleIME::_IsKeyEaten(_In_ ITfContext *pContext, UINT codeIn, _Out_ UINT *pCodeOut, _Out_writes_(1) WCHAR *pwch, _Out_opt_ _KEYSTROKE_STATE *pKeyState) +{ + pContext; + + *pCodeOut = codeIn; + + BOOL isOpen = FALSE; + CCompartment CompartmentKeyboardOpen(_pThreadMgr, _tfClientId, GUID_COMPARTMENT_KEYBOARD_OPENCLOSE); + CompartmentKeyboardOpen._GetCompartmentBOOL(isOpen); + + BOOL isDoubleSingleByte = FALSE; + CCompartment CompartmentDoubleSingleByte(_pThreadMgr, _tfClientId, Global::SampleIMEGuidCompartmentDoubleSingleByte); + CompartmentDoubleSingleByte._GetCompartmentBOOL(isDoubleSingleByte); + + BOOL isPunctuation = FALSE; + CCompartment CompartmentPunctuation(_pThreadMgr, _tfClientId, Global::SampleIMEGuidCompartmentPunctuation); + CompartmentPunctuation._GetCompartmentBOOL(isPunctuation); + + if (pKeyState) + { + pKeyState->Category = CATEGORY_NONE; + pKeyState->Function = FUNCTION_NONE; + } + if (pwch) + { + *pwch = L'\0'; + } + + // if the keyboard is disabled, we don't eat keys. + if (_IsKeyboardDisabled()) + { + return FALSE; + } + + // + // Map virtual key to character code + // + BOOL isTouchKeyboardSpecialKeys = FALSE; + WCHAR wch = ConvertVKey(codeIn); + *pCodeOut = VKeyFromVKPacketAndWchar(codeIn, wch); + if ((wch == THIRDPARTY_NEXTPAGE) || (wch == THIRDPARTY_PREVPAGE)) + { + // We always eat the above softkeyboard special keys + isTouchKeyboardSpecialKeys = TRUE; + if (pwch) + { + *pwch = wch; + } + } + + // if the keyboard is closed, we don't eat keys, with the exception of the touch keyboard specials keys + if (!isOpen && !isDoubleSingleByte && !isPunctuation) + { + return isTouchKeyboardSpecialKeys; + } + + if (pwch) + { + *pwch = wch; + } + + // + // Get composition engine + // + CCompositionProcessorEngine *pCompositionProcessorEngine; + pCompositionProcessorEngine = _pCompositionProcessorEngine; + + if (isOpen) + { + // + // The candidate or phrase list handles the keys through ITfKeyEventSink. + // + // eat only keys that CKeyHandlerEditSession can handles. + // + if (pCompositionProcessorEngine->IsVirtualKeyNeed(*pCodeOut, pwch, _IsComposing(), _candidateMode, _isCandidateWithWildcard, pKeyState)) + { + return TRUE; + } + } + + // + // Punctuation + // + if (pCompositionProcessorEngine->IsPunctuation(wch)) + { + if ((_candidateMode == CANDIDATE_NONE) && isPunctuation) + { + if (pKeyState) + { + pKeyState->Category = CATEGORY_COMPOSING; + pKeyState->Function = FUNCTION_PUNCTUATION; + } + return TRUE; + } + } + + // + // Double/Single byte + // + if (isDoubleSingleByte && pCompositionProcessorEngine->IsDoubleSingleByte(wch)) + { + if (_candidateMode == CANDIDATE_NONE) + { + if (pKeyState) + { + pKeyState->Category = CATEGORY_COMPOSING; + pKeyState->Function = FUNCTION_DOUBLE_SINGLE_BYTE; + } + return TRUE; + } + } + + return isTouchKeyboardSpecialKeys; +} + +//+--------------------------------------------------------------------------- +// +// ConvertVKey +// +//---------------------------------------------------------------------------- + +WCHAR CSampleIME::ConvertVKey(UINT code) +{ + // + // Map virtual key to scan code + // + UINT scanCode = 0; + scanCode = MapVirtualKey(code, 0); + + // + // Keyboard state + // + BYTE abKbdState[256] = {'\0'}; + if (!GetKeyboardState(abKbdState)) + { + return 0; + } + + // + // Map virtual key to character code + // + WCHAR wch = '\0'; + if (ToUnicode(code, scanCode, abKbdState, &wch, 1, 0) == 1) + { + return wch; + } + + return 0; +} + +//+--------------------------------------------------------------------------- +// +// _IsKeyboardDisabled +// +//---------------------------------------------------------------------------- + +BOOL CSampleIME::_IsKeyboardDisabled() +{ + ITfDocumentMgr* pDocMgrFocus = nullptr; + ITfContext* pContext = nullptr; + BOOL isDisabled = FALSE; + + if ((_pThreadMgr->GetFocus(&pDocMgrFocus) != S_OK) || + (pDocMgrFocus == nullptr)) + { + // if there is no focus document manager object, the keyboard + // is disabled. + isDisabled = TRUE; + } + else if ((pDocMgrFocus->GetTop(&pContext) != S_OK) || + (pContext == nullptr)) + { + // if there is no context object, the keyboard is disabled. + isDisabled = TRUE; + } + else + { + CCompartment CompartmentKeyboardDisabled(_pThreadMgr, _tfClientId, GUID_COMPARTMENT_KEYBOARD_DISABLED); + CompartmentKeyboardDisabled._GetCompartmentBOOL(isDisabled); + + CCompartment CompartmentEmptyContext(_pThreadMgr, _tfClientId, GUID_COMPARTMENT_EMPTYCONTEXT); + CompartmentEmptyContext._GetCompartmentBOOL(isDisabled); + } + + if (pContext) + { + pContext->Release(); + } + + if (pDocMgrFocus) + { + pDocMgrFocus->Release(); + } + + return isDisabled; +} + +//+--------------------------------------------------------------------------- +// +// ITfKeyEventSink::OnSetFocus +// +// Called by the system whenever this service gets the keystroke device focus. +//---------------------------------------------------------------------------- + +STDAPI CSampleIME::OnSetFocus(BOOL fForeground) +{ + fForeground; + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// ITfKeyEventSink::OnTestKeyDown +// +// Called by the system to query this service wants a potential keystroke. +//---------------------------------------------------------------------------- + +STDAPI CSampleIME::OnTestKeyDown(ITfContext *pContext, WPARAM wParam, LPARAM lParam, BOOL *pIsEaten) +{ + Global::UpdateModifiers(wParam, lParam); + + _KEYSTROKE_STATE KeystrokeState; + WCHAR wch = '\0'; + UINT code = 0; + *pIsEaten = _IsKeyEaten(pContext, (UINT)wParam, &code, &wch, &KeystrokeState); + + if (KeystrokeState.Category == CATEGORY_INVOKE_COMPOSITION_EDIT_SESSION) + { + // + // Invoke key handler edit session + // + KeystrokeState.Category = CATEGORY_COMPOSING; + + _InvokeKeyHandler(pContext, code, wch, (DWORD)lParam, KeystrokeState); + } + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// ITfKeyEventSink::OnKeyDown +// +// Called by the system to offer this service a keystroke. If *pIsEaten == TRUE +// on exit, the application will not handle the keystroke. +//---------------------------------------------------------------------------- + +STDAPI CSampleIME::OnKeyDown(ITfContext *pContext, WPARAM wParam, LPARAM lParam, BOOL *pIsEaten) +{ + Global::UpdateModifiers(wParam, lParam); + + _KEYSTROKE_STATE KeystrokeState; + WCHAR wch = '\0'; + UINT code = 0; + + *pIsEaten = _IsKeyEaten(pContext, (UINT)wParam, &code, &wch, &KeystrokeState); + + if (*pIsEaten) + { + bool needInvokeKeyHandler = true; + // + // Invoke key handler edit session + // + if (code == VK_ESCAPE) + { + KeystrokeState.Category = CATEGORY_COMPOSING; + } + + // Always eat THIRDPARTY_NEXTPAGE and THIRDPARTY_PREVPAGE keys, but don't always process them. + if ((wch == THIRDPARTY_NEXTPAGE) || (wch == THIRDPARTY_PREVPAGE)) + { + needInvokeKeyHandler = !((KeystrokeState.Category == CATEGORY_NONE) && (KeystrokeState.Function == FUNCTION_NONE)); + } + + if (needInvokeKeyHandler) + { + _InvokeKeyHandler(pContext, code, wch, (DWORD)lParam, KeystrokeState); + } + } + else if (KeystrokeState.Category == CATEGORY_INVOKE_COMPOSITION_EDIT_SESSION) + { + // Invoke key handler edit session + KeystrokeState.Category = CATEGORY_COMPOSING; + _InvokeKeyHandler(pContext, code, wch, (DWORD)lParam, KeystrokeState); + } + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// ITfKeyEventSink::OnTestKeyUp +// +// Called by the system to query this service wants a potential keystroke. +//---------------------------------------------------------------------------- + +STDAPI CSampleIME::OnTestKeyUp(ITfContext *pContext, WPARAM wParam, LPARAM lParam, BOOL *pIsEaten) +{ + if (pIsEaten == nullptr) + { + return E_INVALIDARG; + } + + Global::UpdateModifiers(wParam, lParam); + + WCHAR wch = '\0'; + UINT code = 0; + + *pIsEaten = _IsKeyEaten(pContext, (UINT)wParam, &code, &wch, NULL); + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// ITfKeyEventSink::OnKeyUp +// +// Called by the system to offer this service a keystroke. If *pIsEaten == TRUE +// on exit, the application will not handle the keystroke. +//---------------------------------------------------------------------------- + +STDAPI CSampleIME::OnKeyUp(ITfContext *pContext, WPARAM wParam, LPARAM lParam, BOOL *pIsEaten) +{ + Global::UpdateModifiers(wParam, lParam); + + WCHAR wch = '\0'; + UINT code = 0; + + *pIsEaten = _IsKeyEaten(pContext, (UINT)wParam, &code, &wch, NULL); + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// ITfKeyEventSink::OnPreservedKey +// +// Called when a hotkey (registered by us, or by the system) is typed. +//---------------------------------------------------------------------------- + +STDAPI CSampleIME::OnPreservedKey(ITfContext *pContext, REFGUID rguid, BOOL *pIsEaten) +{ + pContext; + + CCompositionProcessorEngine *pCompositionProcessorEngine; + pCompositionProcessorEngine = _pCompositionProcessorEngine; + + pCompositionProcessorEngine->OnPreservedKey(rguid, pIsEaten, _GetThreadMgr(), _GetClientId()); + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// _InitKeyEventSink +// +// Advise a keystroke sink. +//---------------------------------------------------------------------------- + +BOOL CSampleIME::_InitKeyEventSink() +{ + ITfKeystrokeMgr* pKeystrokeMgr = nullptr; + HRESULT hr = S_OK; + + if (FAILED(_pThreadMgr->QueryInterface(IID_ITfKeystrokeMgr, (void **)&pKeystrokeMgr))) + { + return FALSE; + } + + hr = pKeystrokeMgr->AdviseKeyEventSink(_tfClientId, (ITfKeyEventSink *)this, TRUE); + + pKeystrokeMgr->Release(); + + return (hr == S_OK); +} + +//+--------------------------------------------------------------------------- +// +// _UninitKeyEventSink +// +// Unadvise a keystroke sink. Assumes we have advised one already. +//---------------------------------------------------------------------------- + +void CSampleIME::_UninitKeyEventSink() +{ + ITfKeystrokeMgr* pKeystrokeMgr = nullptr; + + if (FAILED(_pThreadMgr->QueryInterface(IID_ITfKeystrokeMgr, (void **)&pKeystrokeMgr))) + { + return; + } + + pKeystrokeMgr->UnadviseKeyEventSink(_tfClientId); + + pKeystrokeMgr->Release(); +} diff --git a/KeyHandler.cpp b/KeyHandler.cpp new file mode 100644 index 00000000..ae47bb80 --- /dev/null +++ b/KeyHandler.cpp @@ -0,0 +1,635 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#include "Private.h" +#include "Globals.h" +#include "EditSession.h" +#include "SampleIME.h" +#include "CandidateListUIPresenter.h" +#include "CompositionProcessorEngine.h" + +////////////////////////////////////////////////////////////////////// +// +// CSampleIME class +// +////////////////////////////////////////////////////////////////////// + +//+--------------------------------------------------------------------------- +// +// _IsRangeCovered +// +// Returns TRUE if pRangeTest is entirely contained within pRangeCover. +// +//---------------------------------------------------------------------------- + +BOOL CSampleIME::_IsRangeCovered(TfEditCookie ec, _In_ ITfRange *pRangeTest, _In_ ITfRange *pRangeCover) +{ + LONG lResult = 0;; + + if (FAILED(pRangeCover->CompareStart(ec, pRangeTest, TF_ANCHOR_START, &lResult)) + || (lResult > 0)) + { + return FALSE; + } + + if (FAILED(pRangeCover->CompareEnd(ec, pRangeTest, TF_ANCHOR_END, &lResult)) + || (lResult < 0)) + { + return FALSE; + } + + return TRUE; +} + +//+--------------------------------------------------------------------------- +// +// _DeleteCandidateList +// +//---------------------------------------------------------------------------- + +VOID CSampleIME::_DeleteCandidateList(BOOL isForce, _In_opt_ ITfContext *pContext) +{ + isForce;pContext; + + CCompositionProcessorEngine* pCompositionProcessorEngine = nullptr; + pCompositionProcessorEngine = _pCompositionProcessorEngine; + pCompositionProcessorEngine->PurgeVirtualKey(); + + if (_pCandidateListUIPresenter) + { + _pCandidateListUIPresenter->_EndCandidateList(); + + _candidateMode = CANDIDATE_NONE; + _isCandidateWithWildcard = FALSE; + } +} + +//+--------------------------------------------------------------------------- +// +// _HandleComplete +// +//---------------------------------------------------------------------------- + +HRESULT CSampleIME::_HandleComplete(TfEditCookie ec, _In_ ITfContext *pContext) +{ + _DeleteCandidateList(FALSE, pContext); + + // just terminate the composition + _TerminateComposition(ec, pContext); + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// _HandleCancel +// +//---------------------------------------------------------------------------- + +HRESULT CSampleIME::_HandleCancel(TfEditCookie ec, _In_ ITfContext *pContext) +{ + _RemoveDummyCompositionForComposing(ec, _pComposition); + + _DeleteCandidateList(FALSE, pContext); + + _TerminateComposition(ec, pContext); + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// _HandleCompositionInput +// +// If the keystroke happens within a composition, eat the key and return S_OK. +// +//---------------------------------------------------------------------------- + +HRESULT CSampleIME::_HandleCompositionInput(TfEditCookie ec, _In_ ITfContext *pContext, WCHAR wch) +{ + ITfRange* pRangeComposition = nullptr; + TF_SELECTION tfSelection; + ULONG fetched = 0; + BOOL isCovered = TRUE; + + CCompositionProcessorEngine* pCompositionProcessorEngine = nullptr; + pCompositionProcessorEngine = _pCompositionProcessorEngine; + + if ((_pCandidateListUIPresenter != nullptr) && (_candidateMode != CANDIDATE_INCREMENTAL)) + { + _HandleCompositionFinalize(ec, pContext, FALSE); + } + + // Start the new (std::nothrow) compositon if there is no composition. + if (!_IsComposing()) + { + _StartComposition(pContext); + } + + // first, test where a keystroke would go in the document if we did an insert + if (pContext->GetSelection(ec, TF_DEFAULT_SELECTION, 1, &tfSelection, &fetched) != S_OK || fetched != 1) + { + return S_FALSE; + } + + // is the insertion point covered by a composition? + if (SUCCEEDED(_pComposition->GetRange(&pRangeComposition))) + { + isCovered = _IsRangeCovered(ec, tfSelection.range, pRangeComposition); + + pRangeComposition->Release(); + + if (!isCovered) + { + goto Exit; + } + } + + // Add virtual key to composition processor engine + pCompositionProcessorEngine->AddVirtualKey(wch); + + _HandleCompositionInputWorker(pCompositionProcessorEngine, ec, pContext); + +Exit: + tfSelection.range->Release(); + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// _HandleCompositionInputWorker +// +// If the keystroke happens within a composition, eat the key and return S_OK. +// +//---------------------------------------------------------------------------- + +HRESULT CSampleIME::_HandleCompositionInputWorker(_In_ CCompositionProcessorEngine *pCompositionProcessorEngine, TfEditCookie ec, _In_ ITfContext *pContext) +{ + HRESULT hr = S_OK; + CSampleImeArray readingStrings; + BOOL isWildcardIncluded = TRUE; + + // + // Get reading string from composition processor engine + // + pCompositionProcessorEngine->GetReadingStrings(&readingStrings, &isWildcardIncluded); + + for (UINT index = 0; index < readingStrings.Count(); index++) + { + hr = _AddComposingAndChar(ec, pContext, readingStrings.GetAt(index)); + if (FAILED(hr)) + { + return hr; + } + } + + // + // Get candidate string from composition processor engine + // + CSampleImeArray candidateList; + + pCompositionProcessorEngine->GetCandidateList(&candidateList, TRUE, FALSE); + + if ((candidateList.Count())) + { + hr = _CreateAndStartCandidate(pCompositionProcessorEngine, ec, pContext); + if (SUCCEEDED(hr)) + { + _pCandidateListUIPresenter->_ClearList(); + _pCandidateListUIPresenter->_SetText(&candidateList, TRUE); + } + } + else if (_pCandidateListUIPresenter) + { + _pCandidateListUIPresenter->_ClearList(); + } + else if (readingStrings.Count() && isWildcardIncluded) + { + hr = _CreateAndStartCandidate(pCompositionProcessorEngine, ec, pContext); + if (SUCCEEDED(hr)) + { + _pCandidateListUIPresenter->_ClearList(); + } + } + return hr; +} +//+--------------------------------------------------------------------------- +// +// _CreateAndStartCandidate +// +//---------------------------------------------------------------------------- + +HRESULT CSampleIME::_CreateAndStartCandidate(_In_ CCompositionProcessorEngine *pCompositionProcessorEngine, TfEditCookie ec, _In_ ITfContext *pContext) +{ + HRESULT hr = S_OK; + + if (((_candidateMode == CANDIDATE_PHRASE) && (_pCandidateListUIPresenter)) + || ((_candidateMode == CANDIDATE_NONE) && (_pCandidateListUIPresenter))) + { + // Recreate candidate list + _pCandidateListUIPresenter->_EndCandidateList(); + delete _pCandidateListUIPresenter; + _pCandidateListUIPresenter = nullptr; + + _candidateMode = CANDIDATE_NONE; + _isCandidateWithWildcard = FALSE; + } + + if (_pCandidateListUIPresenter == nullptr) + { + _pCandidateListUIPresenter = new (std::nothrow) CCandidateListUIPresenter(this, Global::AtomCandidateWindow, + CATEGORY_CANDIDATE, + pCompositionProcessorEngine->GetCandidateListIndexRange(), + FALSE); + if (!_pCandidateListUIPresenter) + { + return E_OUTOFMEMORY; + } + + _candidateMode = CANDIDATE_INCREMENTAL; + _isCandidateWithWildcard = FALSE; + + // we don't cache the document manager object. So get it from pContext. + ITfDocumentMgr* pDocumentMgr = nullptr; + if (SUCCEEDED(pContext->GetDocumentMgr(&pDocumentMgr))) + { + // get the composition range. + ITfRange* pRange = nullptr; + if (SUCCEEDED(_pComposition->GetRange(&pRange))) + { + hr = _pCandidateListUIPresenter->_StartCandidateList(_tfClientId, pDocumentMgr, pContext, ec, pRange, pCompositionProcessorEngine->GetCandidateWindowWidth()); + pRange->Release(); + } + pDocumentMgr->Release(); + } + } + + return hr; +} + +//+--------------------------------------------------------------------------- +// +// _HandleCompositionFinalize +// +//---------------------------------------------------------------------------- + +HRESULT CSampleIME::_HandleCompositionFinalize(TfEditCookie ec, _In_ ITfContext *pContext, BOOL isCandidateList) +{ + HRESULT hr = S_OK; + + if (isCandidateList && _pCandidateListUIPresenter) + { + // Finalize selected candidate string from CCandidateListUIPresenter + DWORD_PTR candidateLen = 0; + const WCHAR *pCandidateString = nullptr; + + candidateLen = _pCandidateListUIPresenter->_GetSelectedCandidateString(&pCandidateString); + + CStringRange candidateString; + candidateString.Set(pCandidateString, candidateLen); + + if (candidateLen) + { + // Finalize character + hr = _AddCharAndFinalize(ec, pContext, &candidateString); + if (FAILED(hr)) + { + return hr; + } + } + } + else + { + // Finalize current text store strings + if (_IsComposing()) + { + ULONG fetched = 0; + TF_SELECTION tfSelection; + + if (FAILED(pContext->GetSelection(ec, TF_DEFAULT_SELECTION, 1, &tfSelection, &fetched)) || fetched != 1) + { + return S_FALSE; + } + + ITfRange* pRangeComposition = nullptr; + if (SUCCEEDED(_pComposition->GetRange(&pRangeComposition))) + { + if (_IsRangeCovered(ec, tfSelection.range, pRangeComposition)) + { + _EndComposition(pContext); + } + + pRangeComposition->Release(); + } + + tfSelection.range->Release(); + } + } + + _HandleCancel(ec, pContext); + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// _HandleCompositionConvert +// +//---------------------------------------------------------------------------- + +HRESULT CSampleIME::_HandleCompositionConvert(TfEditCookie ec, _In_ ITfContext *pContext, BOOL isWildcardSearch) +{ + HRESULT hr = S_OK; + + CSampleImeArray candidateList; + + // + // Get candidate string from composition processor engine + // + CCompositionProcessorEngine* pCompositionProcessorEngine = nullptr; + pCompositionProcessorEngine = _pCompositionProcessorEngine; + pCompositionProcessorEngine->GetCandidateList(&candidateList, FALSE, isWildcardSearch); + + // If there is no candlidate listin the current reading string, we don't do anything. Just wait for + // next char to be ready for the conversion with it. + int nCount = candidateList.Count(); + if (nCount) + { + if (_pCandidateListUIPresenter) + { + _pCandidateListUIPresenter->_EndCandidateList(); + delete _pCandidateListUIPresenter; + _pCandidateListUIPresenter = nullptr; + + _candidateMode = CANDIDATE_NONE; + _isCandidateWithWildcard = FALSE; + } + + // + // create an instance of the candidate list class. + // + if (_pCandidateListUIPresenter == nullptr) + { + _pCandidateListUIPresenter = new (std::nothrow) CCandidateListUIPresenter(this, Global::AtomCandidateWindow, + CATEGORY_CANDIDATE, + pCompositionProcessorEngine->GetCandidateListIndexRange(), + FALSE); + if (!_pCandidateListUIPresenter) + { + return E_OUTOFMEMORY; + } + + _candidateMode = CANDIDATE_ORIGINAL; + } + + _isCandidateWithWildcard = isWildcardSearch; + + // we don't cache the document manager object. So get it from pContext. + ITfDocumentMgr* pDocumentMgr = nullptr; + if (SUCCEEDED(pContext->GetDocumentMgr(&pDocumentMgr))) + { + // get the composition range. + ITfRange* pRange = nullptr; + if (SUCCEEDED(_pComposition->GetRange(&pRange))) + { + hr = _pCandidateListUIPresenter->_StartCandidateList(_tfClientId, pDocumentMgr, pContext, ec, pRange, pCompositionProcessorEngine->GetCandidateWindowWidth()); + pRange->Release(); + } + pDocumentMgr->Release(); + } + if (SUCCEEDED(hr)) + { + _pCandidateListUIPresenter->_SetText(&candidateList, FALSE); + } + } + + return hr; +} + +//+--------------------------------------------------------------------------- +// +// _HandleCompositionBackspace +// +//---------------------------------------------------------------------------- + +HRESULT CSampleIME::_HandleCompositionBackspace(TfEditCookie ec, _In_ ITfContext *pContext) +{ + ITfRange* pRangeComposition = nullptr; + TF_SELECTION tfSelection; + ULONG fetched = 0; + BOOL isCovered = TRUE; + + // Start the new (std::nothrow) compositon if there is no composition. + if (!_IsComposing()) + { + return S_OK; + } + + // first, test where a keystroke would go in the document if we did an insert + if (FAILED(pContext->GetSelection(ec, TF_DEFAULT_SELECTION, 1, &tfSelection, &fetched)) || fetched != 1) + { + return S_FALSE; + } + + // is the insertion point covered by a composition? + if (SUCCEEDED(_pComposition->GetRange(&pRangeComposition))) + { + isCovered = _IsRangeCovered(ec, tfSelection.range, pRangeComposition); + + pRangeComposition->Release(); + + if (!isCovered) + { + goto Exit; + } + } + + // + // Add virtual key to composition processor engine + // + CCompositionProcessorEngine* pCompositionProcessorEngine = nullptr; + pCompositionProcessorEngine = _pCompositionProcessorEngine; + + DWORD_PTR vKeyLen = pCompositionProcessorEngine->GetVirtualKeyLength(); + + if (vKeyLen) + { + pCompositionProcessorEngine->RemoveVirtualKey(vKeyLen - 1); + + if (pCompositionProcessorEngine->GetVirtualKeyLength()) + { + _HandleCompositionInputWorker(pCompositionProcessorEngine, ec, pContext); + } + else + { + _HandleCancel(ec, pContext); + } + } + +Exit: + tfSelection.range->Release(); + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// _HandleCompositionArrowKey +// +// Update the selection within a composition. +// +//---------------------------------------------------------------------------- + +HRESULT CSampleIME::_HandleCompositionArrowKey(TfEditCookie ec, _In_ ITfContext *pContext, KEYSTROKE_FUNCTION keyFunction) +{ + ITfRange* pRangeComposition = nullptr; + TF_SELECTION tfSelection; + ULONG fetched = 0; + + // get the selection + if (FAILED(pContext->GetSelection(ec, TF_DEFAULT_SELECTION, 1, &tfSelection, &fetched)) + || fetched != 1) + { + // no selection, eat the keystroke + return S_OK; + } + + // get the composition range + if (FAILED(_pComposition->GetRange(&pRangeComposition))) + { + goto Exit; + } + + // For incremental candidate list + if (_pCandidateListUIPresenter) + { + _pCandidateListUIPresenter->AdviseUIChangedByArrowKey(keyFunction); + } + + pContext->SetSelection(ec, 1, &tfSelection); + + pRangeComposition->Release(); + +Exit: + tfSelection.range->Release(); + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// _HandleCompositionPunctuation +// +//---------------------------------------------------------------------------- + +HRESULT CSampleIME::_HandleCompositionPunctuation(TfEditCookie ec, _In_ ITfContext *pContext, WCHAR wch) +{ + HRESULT hr = S_OK; + + if (_candidateMode != CANDIDATE_NONE && _pCandidateListUIPresenter) + { + DWORD_PTR candidateLen = 0; + const WCHAR* pCandidateString = nullptr; + + candidateLen = _pCandidateListUIPresenter->_GetSelectedCandidateString(&pCandidateString); + + CStringRange candidateString; + candidateString.Set(pCandidateString, candidateLen); + + if (candidateLen) + { + _AddComposingAndChar(ec, pContext, &candidateString); + } + } + // + // Get punctuation char from composition processor engine + // + CCompositionProcessorEngine* pCompositionProcessorEngine = nullptr; + pCompositionProcessorEngine = _pCompositionProcessorEngine; + + WCHAR punctuation = pCompositionProcessorEngine->GetPunctuation(wch); + + CStringRange punctuationString; + punctuationString.Set(&punctuation, 1); + + // Finalize character + hr = _AddCharAndFinalize(ec, pContext, &punctuationString); + if (FAILED(hr)) + { + return hr; + } + + _HandleCancel(ec, pContext); + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// _HandleCompositionDoubleSingleByte +// +//---------------------------------------------------------------------------- + +HRESULT CSampleIME::_HandleCompositionDoubleSingleByte(TfEditCookie ec, _In_ ITfContext *pContext, WCHAR wch) +{ + HRESULT hr = S_OK; + + WCHAR fullWidth = Global::FullWidthCharTable[wch - 0x20]; + + CStringRange fullWidthString; + fullWidthString.Set(&fullWidth, 1); + + // Finalize character + hr = _AddCharAndFinalize(ec, pContext, &fullWidthString); + if (FAILED(hr)) + { + return hr; + } + + _HandleCancel(ec, pContext); + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// _InvokeKeyHandler +// +// This text service is interested in handling keystrokes to demonstrate the +// use the compositions. Some apps will cancel compositions if they receive +// keystrokes while a compositions is ongoing. +// +// param +// [in] uCode - virtual key code of WM_KEYDOWN wParam +// [in] dwFlags - WM_KEYDOWN lParam +// [in] dwKeyFunction - Function regarding virtual key +//---------------------------------------------------------------------------- + +HRESULT CSampleIME::_InvokeKeyHandler(_In_ ITfContext *pContext, UINT code, WCHAR wch, DWORD flags, _KEYSTROKE_STATE keyState) +{ + flags; + + CKeyHandlerEditSession* pEditSession = nullptr; + HRESULT hr = E_FAIL; + + // we'll insert a char ourselves in place of this keystroke + pEditSession = new (std::nothrow) CKeyHandlerEditSession(this, pContext, code, wch, keyState); + if (pEditSession == nullptr) + { + goto Exit; + } + + // + // Call CKeyHandlerEditSession::DoEditSession(). + // + // Do not specify TF_ES_SYNC so edit session is not invoked on WinWord + // + hr = pContext->RequestEditSession(_tfClientId, pEditSession, TF_ES_ASYNCDONTCARE | TF_ES_READWRITE, &hr); + + pEditSession->Release(); + +Exit: + return hr; +} diff --git a/KeyHandlerEditSession.cpp b/KeyHandlerEditSession.cpp new file mode 100644 index 00000000..4d17c420 --- /dev/null +++ b/KeyHandlerEditSession.cpp @@ -0,0 +1,46 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#include "Private.h" +#include "KeyHandlerEditSession.h" +#include "EditSession.h" +#include "SampleIME.h" +#include "CompositionProcessorEngine.h" +#include "KeyStateCategory.h" + +////////////////////////////////////////////////////////////////////// +// +// ITfEditSession +// CEditSessionBase +// CKeyHandlerEditSession class +// +////////////////////////////////////////////////////////////////////// + +//+--------------------------------------------------------------------------- +// +// CKeyHandlerEditSession::DoEditSession +// +//---------------------------------------------------------------------------- + +STDAPI CKeyHandlerEditSession::DoEditSession(TfEditCookie ec) +{ + HRESULT hResult = S_OK; + + CKeyStateCategoryFactory* pKeyStateCategoryFactory = CKeyStateCategoryFactory::Instance(); + CKeyStateCategory* pKeyStateCategory = pKeyStateCategoryFactory->MakeKeyStateCategory(_KeyState.Category, _pTextService); + + if (pKeyStateCategory) + { + KeyHandlerEditSessionDTO keyHandlerEditSessioDTO(ec, _pContext, _uCode,_wch, _KeyState.Function); + hResult = pKeyStateCategory->KeyStateHandler(_KeyState.Function, keyHandlerEditSessioDTO); + + pKeyStateCategory->Release(); + pKeyStateCategoryFactory->Release(); + } + + return hResult; +} diff --git a/KeyHandlerEditSession.h b/KeyHandlerEditSession.h new file mode 100644 index 00000000..4d92baa3 --- /dev/null +++ b/KeyHandlerEditSession.h @@ -0,0 +1,30 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#pragma once + +#include "EditSession.h" +#include "Globals.h" + +class CKeyHandlerEditSession : public CEditSessionBase +{ +public: + CKeyHandlerEditSession(CSampleIME *pTextService, ITfContext *pContext, UINT uCode, WCHAR wch, _KEYSTROKE_STATE keyState) : CEditSessionBase(pTextService, pContext) + { + _uCode = uCode; + _wch = wch; + _KeyState = keyState; + } + + // ITfEditSession + STDMETHODIMP DoEditSession(TfEditCookie ec); + +private: + UINT _uCode; // virtual key code + WCHAR _wch; // character code + _KEYSTROKE_STATE _KeyState; // key function regarding virtual key +}; diff --git a/KeyStateCategory.cpp b/KeyStateCategory.cpp new file mode 100644 index 00000000..b9deb55b --- /dev/null +++ b/KeyStateCategory.cpp @@ -0,0 +1,370 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#include "KeyStateCategory.h" + +CKeyStateCategoryFactory* CKeyStateCategoryFactory::_instance; + +CKeyStateCategoryFactory::CKeyStateCategoryFactory() +{ + _instance = nullptr; +} + +CKeyStateCategoryFactory* CKeyStateCategoryFactory::Instance() +{ + if (nullptr == _instance) + { + _instance = new (std::nothrow) CKeyStateCategoryFactory(); + } + + return _instance; +} + +CKeyStateCategory* CKeyStateCategoryFactory::MakeKeyStateCategory(KEYSTROKE_CATEGORY keyCategory, _In_ CSampleIME *pTextService) +{ + CKeyStateCategory* pKeyState = nullptr; + + switch (keyCategory) + { + case CATEGORY_NONE: + pKeyState = new (std::nothrow) CKeyStateNull(pTextService); + break; + + case CATEGORY_COMPOSING: + pKeyState = new (std::nothrow) CKeyStateComposing(pTextService); + break; + + case CATEGORY_CANDIDATE: + pKeyState = new (std::nothrow) CKeyStateCandidate(pTextService); + break; + + case CATEGORY_PHRASE: + pKeyState = new (std::nothrow) CKeyStatePhrase(pTextService); + break; + + default: + pKeyState = new (std::nothrow) CKeyStateNull(pTextService); + break; + } + return pKeyState; +} + +void CKeyStateCategoryFactory::Release() +{ + if (_instance) + { + delete _instance; + _instance = nullptr; + } +} + +/* +class CKeyStateCategory +*/ +CKeyStateCategory::CKeyStateCategory(_In_ CSampleIME *pTextService) +{ + _pTextService = pTextService; +} + +CKeyStateCategory::~CKeyStateCategory(void) +{ +} + +HRESULT CKeyStateCategory::KeyStateHandler(KEYSTROKE_FUNCTION function, KeyHandlerEditSessionDTO dto) +{ + switch(function) + { + case FUNCTION_INPUT: + return HandleKeyInput(dto); + + case FUNCTION_FINALIZE_TEXTSTORE_AND_INPUT: + return HandleKeyFinalizeTextStoreAndInput(dto); + + case FUNCTION_FINALIZE_TEXTSTORE: + return HandleKeyFinalizeTextStore(dto); + + case FUNCTION_FINALIZE_CANDIDATELIST_AND_INPUT: + return HandleKeyFinalizeCandidatelistAndInput(dto); + + case FUNCTION_FINALIZE_CANDIDATELIST: + return HandleKeyFinalizeCandidatelist(dto); + + case FUNCTION_CONVERT: + return HandleKeyConvert(dto); + + case FUNCTION_CONVERT_WILDCARD: + return HandleKeyConvertWildCard(dto); + + case FUNCTION_CANCEL: + return HandleKeyCancel(dto); + + case FUNCTION_BACKSPACE: + return HandleKeyBackspace(dto); + + case FUNCTION_MOVE_LEFT: + case FUNCTION_MOVE_RIGHT: + return HandleKeyArrow(dto); + + case FUNCTION_MOVE_UP: + case FUNCTION_MOVE_DOWN: + case FUNCTION_MOVE_PAGE_UP: + case FUNCTION_MOVE_PAGE_DOWN: + case FUNCTION_MOVE_PAGE_TOP: + case FUNCTION_MOVE_PAGE_BOTTOM: + return HandleKeyArrow(dto); + + case FUNCTION_DOUBLE_SINGLE_BYTE: + return HandleKeyDoubleSingleByte(dto); + + case FUNCTION_PUNCTUATION: + return HandleKeyPunctuation(dto); + + case FUNCTION_SELECT_BY_NUMBER: + return HandleKeySelectByNumber(dto); + + } + return E_INVALIDARG; +} + +void CKeyStateCategory::Release() +{ + delete this; +} + +// _HandleCompositionInput +HRESULT CKeyStateCategory::HandleKeyInput(KeyHandlerEditSessionDTO dto) +{ + dto; + return E_NOTIMPL; +} + +// HandleKeyFinalizeTextStore +HRESULT CKeyStateCategory::HandleKeyFinalizeTextStore(KeyHandlerEditSessionDTO dto) +{ + dto; + return E_NOTIMPL; +} +// HandleKeyCompositionFinalizeTextStoreAndInput +HRESULT CKeyStateCategory::HandleKeyFinalizeTextStoreAndInput(KeyHandlerEditSessionDTO dto) +{ + dto; + return E_NOTIMPL; +} + +// HandleKeyCompositionFinalizeCandidatelistAndInput +HRESULT CKeyStateCategory::HandleKeyFinalizeCandidatelistAndInput(KeyHandlerEditSessionDTO dto) +{ + dto; + return E_NOTIMPL; +} + +// HandleKeyCompositionFinalizeCandidatelist +HRESULT CKeyStateCategory::HandleKeyFinalizeCandidatelist(KeyHandlerEditSessionDTO dto) +{ + dto; + return E_NOTIMPL; +} + +// HandleKeyConvert +HRESULT CKeyStateCategory::HandleKeyConvert(KeyHandlerEditSessionDTO dto) +{ + dto; + return E_NOTIMPL; +} + +// HandleKeyConvertWildCard +HRESULT CKeyStateCategory::HandleKeyConvertWildCard(KeyHandlerEditSessionDTO dto) +{ + dto; + return E_NOTIMPL; +} + +//_HandleCancel +HRESULT CKeyStateCategory::HandleKeyCancel(KeyHandlerEditSessionDTO dto) +{ + dto; + return E_NOTIMPL; +} + +//_HandleCompositionBackspace +HRESULT CKeyStateCategory::HandleKeyBackspace(KeyHandlerEditSessionDTO dto) +{ + dto; + return E_NOTIMPL; +} + +//_HandleCompositionArrowKey +HRESULT CKeyStateCategory::HandleKeyArrow(KeyHandlerEditSessionDTO dto) +{ + dto; + return E_NOTIMPL; +} + +//_HandleCompositionDoubleSingleByte +HRESULT CKeyStateCategory::HandleKeyDoubleSingleByte(KeyHandlerEditSessionDTO dto) +{ + dto; + return E_NOTIMPL; +} + +//_HandleCompositionPunctuation +HRESULT CKeyStateCategory::HandleKeyPunctuation(KeyHandlerEditSessionDTO dto) +{ + dto; + return E_NOTIMPL; +} + +HRESULT CKeyStateCategory::HandleKeySelectByNumber(KeyHandlerEditSessionDTO dto) +{ + dto; + return E_NOTIMPL; +} + +/* +class CKeyStateComposing +*/ +CKeyStateComposing::CKeyStateComposing(_In_ CSampleIME *pTextService) : CKeyStateCategory(pTextService) +{ +} + +HRESULT CKeyStateComposing::HandleKeyInput(KeyHandlerEditSessionDTO dto) +{ + return _pTextService->_HandleCompositionInput(dto.ec, dto.pContext, dto.wch); +} + +HRESULT CKeyStateComposing::HandleKeyFinalizeTextStoreAndInput(KeyHandlerEditSessionDTO dto) +{ + _pTextService->_HandleCompositionFinalize(dto.ec, dto.pContext, FALSE); + return _pTextService->_HandleCompositionInput(dto.ec, dto.pContext, dto.wch); +} + +HRESULT CKeyStateComposing::HandleKeyFinalizeTextStore(KeyHandlerEditSessionDTO dto) +{ + return _pTextService->_HandleCompositionFinalize(dto.ec, dto.pContext, FALSE); +} + +HRESULT CKeyStateComposing::HandleKeyFinalizeCandidatelistAndInput(KeyHandlerEditSessionDTO dto) +{ + _pTextService->_HandleCompositionFinalize(dto.ec, dto.pContext, TRUE); + return _pTextService->_HandleCompositionInput(dto.ec, dto.pContext, dto.wch); +} + +HRESULT CKeyStateComposing::HandleKeyFinalizeCandidatelist(KeyHandlerEditSessionDTO dto) +{ + return _pTextService->_HandleCompositionFinalize(dto.ec, dto.pContext, TRUE); +} + +HRESULT CKeyStateComposing::HandleKeyConvert(KeyHandlerEditSessionDTO dto) +{ + return _pTextService->_HandleCompositionConvert(dto.ec, dto.pContext, FALSE); +} + +HRESULT CKeyStateComposing::HandleKeyConvertWildCard(KeyHandlerEditSessionDTO dto) +{ + return _pTextService->_HandleCompositionConvert(dto.ec, dto.pContext, TRUE); +} + +HRESULT CKeyStateComposing::HandleKeyCancel(KeyHandlerEditSessionDTO dto) +{ + return _pTextService->_HandleCancel(dto.ec, dto.pContext); +} + +HRESULT CKeyStateComposing::HandleKeyBackspace(KeyHandlerEditSessionDTO dto) +{ + return _pTextService->_HandleCompositionBackspace(dto.ec, dto.pContext); +} + +HRESULT CKeyStateComposing::HandleKeyArrow(KeyHandlerEditSessionDTO dto) +{ + return _pTextService->_HandleCompositionArrowKey(dto.ec, dto.pContext, dto.arrowKey); +} + +HRESULT CKeyStateComposing::HandleKeyDoubleSingleByte(KeyHandlerEditSessionDTO dto) +{ + return _pTextService->_HandleCompositionDoubleSingleByte(dto.ec, dto.pContext, dto.wch); +} + +HRESULT CKeyStateComposing::HandleKeyPunctuation(KeyHandlerEditSessionDTO dto) +{ + return _pTextService->_HandleCompositionPunctuation(dto.ec, dto.pContext, dto.wch); +} + +/* +class CKeyStateCandidate +*/ +CKeyStateCandidate::CKeyStateCandidate(_In_ CSampleIME *pTextService) : CKeyStateCategory(pTextService) +{ +} + +// _HandleCandidateInput +HRESULT CKeyStateCandidate::HandleKeyFinalizeCandidatelist(KeyHandlerEditSessionDTO dto) +{ + return _pTextService->_HandleCandidateFinalize(dto.ec, dto.pContext); +} + +// HandleKeyFinalizeCandidatelistAndInput +HRESULT CKeyStateCandidate::HandleKeyFinalizeCandidatelistAndInput(KeyHandlerEditSessionDTO dto) +{ + _pTextService->_HandleCandidateFinalize(dto.ec, dto.pContext); + return _pTextService->_HandleCompositionInput(dto.ec, dto.pContext, dto.wch); +} + +//_HandleCandidateConvert +HRESULT CKeyStateCandidate::HandleKeyConvert(KeyHandlerEditSessionDTO dto) +{ + return _pTextService->_HandleCandidateConvert(dto.ec, dto.pContext); +} + +//_HandleCancel +HRESULT CKeyStateCandidate::HandleKeyCancel(KeyHandlerEditSessionDTO dto) +{ + return _pTextService->_HandleCancel(dto.ec, dto.pContext); +} + +//_HandleCandidateArrowKey +HRESULT CKeyStateCandidate::HandleKeyArrow(KeyHandlerEditSessionDTO dto) +{ + return _pTextService->_HandleCandidateArrowKey(dto.ec, dto.pContext, dto.arrowKey); +} + +//_HandleCandidateSelectByNumber +HRESULT CKeyStateCandidate::HandleKeySelectByNumber(KeyHandlerEditSessionDTO dto) +{ + return _pTextService->_HandleCandidateSelectByNumber(dto.ec, dto.pContext, dto.code); +} + +/* +class CKeyStatePhrase +*/ + +CKeyStatePhrase::CKeyStatePhrase(_In_ CSampleIME *pTextService) : CKeyStateCategory(pTextService) +{ +} + +//HandleKeyFinalizeCandidatelist +HRESULT CKeyStatePhrase::HandleKeyFinalizeCandidatelist(KeyHandlerEditSessionDTO dto) +{ + return _pTextService->_HandlePhraseFinalize(dto.ec, dto.pContext); +} + +//HandleKeyCancel +HRESULT CKeyStatePhrase::HandleKeyCancel(KeyHandlerEditSessionDTO dto) +{ + return _pTextService->_HandleCancel(dto.ec, dto.pContext); +} + +//HandleKeyArrow +HRESULT CKeyStatePhrase::HandleKeyArrow(KeyHandlerEditSessionDTO dto) +{ + return _pTextService->_HandlePhraseArrowKey(dto.ec, dto.pContext, dto.arrowKey); +} + +//HandleKeySelectByNumber +HRESULT CKeyStatePhrase::HandleKeySelectByNumber(KeyHandlerEditSessionDTO dto) +{ + return _pTextService->_HandlePhraseSelectByNumber(dto.ec, dto.pContext, dto.code); +} \ No newline at end of file diff --git a/KeyStateCategory.h b/KeyStateCategory.h new file mode 100644 index 00000000..36b256a4 --- /dev/null +++ b/KeyStateCategory.h @@ -0,0 +1,236 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#pragma once +#include "Globals.h" +#include "Private.h" +#include "SampleIME.h" + +class CKeyStateCategory; + +class CKeyStateCategoryFactory +{ +public: + static CKeyStateCategoryFactory* Instance(); + CKeyStateCategory* MakeKeyStateCategory(KEYSTROKE_CATEGORY keyCategory, _In_ CSampleIME *pTextService); + void Release(); + +protected: + CKeyStateCategoryFactory(); + +private: + static CKeyStateCategoryFactory* _instance; + +}; + +typedef struct KeyHandlerEditSessionDTO +{ + KeyHandlerEditSessionDTO::KeyHandlerEditSessionDTO(TfEditCookie tFEC, _In_ ITfContext *pTfContext, UINT virualCode, WCHAR inputChar, KEYSTROKE_FUNCTION arrowKeyFunction) + { + ec = tFEC; + pContext = pTfContext; + code = virualCode; + wch = inputChar; + arrowKey = arrowKeyFunction; + } + + TfEditCookie ec; + ITfContext* pContext; + UINT code; + WCHAR wch; + KEYSTROKE_FUNCTION arrowKey; +}KeyHandlerEditSessionDTO; + +class CKeyStateCategory +{ +public: + CKeyStateCategory(_In_ CSampleIME *pTextService); + +protected: + ~CKeyStateCategory(void); + +public: + HRESULT KeyStateHandler(KEYSTROKE_FUNCTION function, KeyHandlerEditSessionDTO dto); + void Release(void); + +protected: + // HandleKeyInput + virtual HRESULT HandleKeyInput(KeyHandlerEditSessionDTO dto); + + // HandleKeyFinalizeTextStoreAndInput + virtual HRESULT HandleKeyFinalizeTextStoreAndInput(KeyHandlerEditSessionDTO dto); + + // HandleKeyFinalizeTextStore + virtual HRESULT HandleKeyFinalizeTextStore(KeyHandlerEditSessionDTO dto); + + // HandleKeyFinalizeCandidatelistAndInput + virtual HRESULT HandleKeyFinalizeCandidatelistAndInput(KeyHandlerEditSessionDTO dto); + + // HandleKeyFinalizeCandidatelist + virtual HRESULT HandleKeyFinalizeCandidatelist(KeyHandlerEditSessionDTO dto); + + // HandleKeyConvert + virtual HRESULT HandleKeyConvert(KeyHandlerEditSessionDTO dto); + + // HandleKeyConvertWild + virtual HRESULT HandleKeyConvertWildCard(KeyHandlerEditSessionDTO dto); + + // HandleKeyCancel + virtual HRESULT HandleKeyCancel(KeyHandlerEditSessionDTO dto); + + // HandleKeyBackspace + virtual HRESULT HandleKeyBackspace(KeyHandlerEditSessionDTO dto); + + // HandleKeyArrow + virtual HRESULT HandleKeyArrow(KeyHandlerEditSessionDTO dto); + + // HandleKeyDoubleSingleByte + virtual HRESULT HandleKeyDoubleSingleByte(KeyHandlerEditSessionDTO dto); + + // HandleKeyPunctuation + virtual HRESULT HandleKeyPunctuation(KeyHandlerEditSessionDTO dto); + + // HandleKeySelectByNumber + virtual HRESULT HandleKeySelectByNumber(KeyHandlerEditSessionDTO dto); + +protected: + CSampleIME* _pTextService; +}; + +class CKeyStateComposing : public CKeyStateCategory +{ +public: + CKeyStateComposing(_In_ CSampleIME *pTextService); + +protected: + // _HandleCompositionInput + HRESULT HandleKeyInput(KeyHandlerEditSessionDTO dto); + + // HandleKeyCompositionFinalizeTextStoreAndInput + HRESULT HandleKeyFinalizeTextStoreAndInput(KeyHandlerEditSessionDTO dto); + + // HandleKeyFinalizeTextStore + HRESULT HandleKeyFinalizeTextStore(KeyHandlerEditSessionDTO dto); + + // HandleKeyCompositionFinalizeCandidatelistAndInput + HRESULT HandleKeyFinalizeCandidatelistAndInput(KeyHandlerEditSessionDTO dto); + + // HandleKeyCompositionFinalizeCandidatelist + HRESULT HandleKeyFinalizeCandidatelist(KeyHandlerEditSessionDTO dto); + + // HandleCompositionConvert + HRESULT HandleKeyConvert(KeyHandlerEditSessionDTO dto); + + // HandleKeyCompositionConvertWildCard + HRESULT HandleKeyConvertWildCard(KeyHandlerEditSessionDTO dto); + + // HandleCancel + HRESULT HandleKeyCancel(KeyHandlerEditSessionDTO dto); + + // HandleCompositionBackspace + HRESULT HandleKeyBackspace(KeyHandlerEditSessionDTO dto); + + // HandleArrowKey + HRESULT HandleKeyArrow(KeyHandlerEditSessionDTO dto); + + // HandleKeyDoubleSingleByte + HRESULT HandleKeyDoubleSingleByte(KeyHandlerEditSessionDTO dto); + + // HandleKeyCompositionPunctuation + HRESULT HandleKeyPunctuation(KeyHandlerEditSessionDTO dto); +}; + +class CKeyStateCandidate : public CKeyStateCategory +{ +public: + CKeyStateCandidate(_In_ CSampleIME *pTextService); + +protected: + // HandleKeyFinalizeCandidatelist + HRESULT HandleKeyFinalizeCandidatelist(KeyHandlerEditSessionDTO dto); + + // HandleKeyFinalizeCandidatelistAndInput + HRESULT HandleKeyFinalizeCandidatelistAndInput(KeyHandlerEditSessionDTO dto); + + //_HandleCandidateConvert + HRESULT HandleKeyConvert(KeyHandlerEditSessionDTO dto); + + //_HandleCancel + HRESULT HandleKeyCancel(KeyHandlerEditSessionDTO dto); + + //_HandleCandidateArrowKey + HRESULT HandleKeyArrow(KeyHandlerEditSessionDTO dto); + + //_HandleCandidateSelectByNumber + HRESULT HandleKeySelectByNumber(KeyHandlerEditSessionDTO dto); +}; + +class CKeyStatePhrase : public CKeyStateCategory +{ +public: + CKeyStatePhrase(_In_ CSampleIME *pTextService); + +protected: + //_HandleCancel + HRESULT HandleKeyFinalizeCandidatelist(KeyHandlerEditSessionDTO dto); + + //_HandleCancel + HRESULT HandleKeyCancel(KeyHandlerEditSessionDTO dto); + + //_HandlePhraseArrowKey + HRESULT HandleKeyArrow(KeyHandlerEditSessionDTO dto); + + //_HandlePhraseSelectByNumber + HRESULT HandleKeySelectByNumber(KeyHandlerEditSessionDTO dto); +}; + +//degeneration class +class CKeyStateNull : public CKeyStateCategory +{ +public: + CKeyStateNull(_In_ CSampleIME *pTextService) : CKeyStateCategory(pTextService) {}; + +protected: + // _HandleNullInput + HRESULT HandleKeyInput(KeyHandlerEditSessionDTO dto) { return __super::HandleKeyInput(dto); }; + + // HandleKeyNullFinalizeTextStoreAndInput + HRESULT HandleKeyFinalizeTextStoreAndInput(KeyHandlerEditSessionDTO dto) { return __super::HandleKeyFinalizeTextStoreAndInput(dto); }; + + // HandleKeyFinalizeTextStore + HRESULT HandleKeyFinalizeTextStore(KeyHandlerEditSessionDTO dto) { return __super::HandleKeyFinalizeTextStore(dto); }; + + // HandleKeyNullFinalizeCandidatelistAndInput + HRESULT HandleKeyFinalizeCandidatelistAndInput(KeyHandlerEditSessionDTO dto) { return __super::HandleKeyFinalizeCandidatelistAndInput(dto); }; + + // HandleKeyNullFinalizeCandidatelist + HRESULT HandleKeyFinalizeCandidatelist(KeyHandlerEditSessionDTO dto) { return __super::HandleKeyFinalizeCandidatelist(dto); }; + + //_HandleNullConvert + HRESULT HandleKeyConvert(KeyHandlerEditSessionDTO dto) { return __super::HandleKeyConvert(dto); }; + + //_HandleNullCancel + HRESULT HandleKeyCancel(KeyHandlerEditSessionDTO dto) { return __super::HandleKeyCancel(dto); }; + + // HandleKeyNullConvertWild + HRESULT HandleKeyConvertWildCard(KeyHandlerEditSessionDTO dto) { return __super::HandleKeyConvertWildCard(dto); }; + + //_HandleNullBackspace + HRESULT HandleKeyBackspace(KeyHandlerEditSessionDTO dto) { return __super::HandleKeyBackspace(dto); }; + + //_HandleNullArrowKey + HRESULT HandleKeyArrow(KeyHandlerEditSessionDTO dto) { return __super::HandleKeyArrow(dto); }; + + // HandleKeyDoubleSingleByte + HRESULT HandleKeyDoubleSingleByte(KeyHandlerEditSessionDTO dto) { return __super::HandleKeyDoubleSingleByte(dto); }; + + // HandleKeyPunctuation + HRESULT HandleKeyPunctuation(KeyHandlerEditSessionDTO dto) { return __super::HandleKeyPunctuation(dto); }; + + //_HandleNullCandidateSelectByNumber + HRESULT HandleKeySelectByNumber(KeyHandlerEditSessionDTO dto) { return __super::HandleKeySelectByNumber(dto); }; +}; \ No newline at end of file diff --git a/LanguageBar.cpp b/LanguageBar.cpp new file mode 100644 index 00000000..1dcc4f37 --- /dev/null +++ b/LanguageBar.cpp @@ -0,0 +1,662 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#include "Private.h" +#include "SampleIME.h" +#include "CompositionProcessorEngine.h" +#include "LanguageBar.h" +#include "Globals.h" +#include "Compartment.h" + +//+--------------------------------------------------------------------------- +// +// CSampleIME::_UpdateLanguageBarOnSetFocus +// +//---------------------------------------------------------------------------- + +void CSampleIME::_UpdateLanguageBarOnSetFocus(_In_ ITfDocumentMgr *pDocMgrFocus) +{ + BOOL needDisableButtons = FALSE; + + if (!pDocMgrFocus) + { + needDisableButtons = TRUE; + } + else + { + IEnumTfContexts* pEnumContext = nullptr; + + if (FAILED(pDocMgrFocus->EnumContexts(&pEnumContext)) || !pEnumContext) + { + needDisableButtons = TRUE; + } + else + { + ULONG fetched = 0; + ITfContext* pContext = nullptr; + + if (FAILED(pEnumContext->Next(1, &pContext, &fetched)) || fetched != 1) + { + needDisableButtons = TRUE; + } + + if (!pContext) + { + // context is not associated + needDisableButtons = TRUE; + } + else + { + pContext->Release(); + } + } + + if (pEnumContext) + { + pEnumContext->Release(); + } + } + + CCompositionProcessorEngine* pCompositionProcessorEngine = nullptr; + pCompositionProcessorEngine = _pCompositionProcessorEngine; + + pCompositionProcessorEngine->SetLanguageBarStatus(TF_LBI_STATUS_DISABLED, needDisableButtons); +} + +//+--------------------------------------------------------------------------- +// +// CCompositionProcessorEngine::SetLanguageBarStatus +// +//---------------------------------------------------------------------------- + +VOID CCompositionProcessorEngine::SetLanguageBarStatus(DWORD status, BOOL isSet) +{ + if (_pLanguageBar_IMEMode) { + _pLanguageBar_IMEMode->SetStatus(status, isSet); + } + if (_pLanguageBar_DoubleSingleByte) { + _pLanguageBar_DoubleSingleByte->SetStatus(status, isSet); + } + if (_pLanguageBar_Punctuation) { + _pLanguageBar_Punctuation->SetStatus(status, isSet); + } +} + +//+--------------------------------------------------------------------------- +// +// CLangBarItemButton::ctor +// +//---------------------------------------------------------------------------- + +CLangBarItemButton::CLangBarItemButton(REFGUID guidLangBar, LPCWSTR description, LPCWSTR tooltip, DWORD onIconIndex, DWORD offIconIndex, BOOL isSecureMode) +{ + DWORD bufLen = 0; + + DllAddRef(); + + // initialize TF_LANGBARITEMINFO structure. + _tfLangBarItemInfo.clsidService = Global::SampleIMECLSID; // This LangBarItem belongs to this TextService. + _tfLangBarItemInfo.guidItem = guidLangBar; // GUID of this LangBarItem. + _tfLangBarItemInfo.dwStyle = (TF_LBI_STYLE_BTN_BUTTON | TF_LBI_STYLE_SHOWNINTRAY); // This LangBar is a button type. + _tfLangBarItemInfo.ulSort = 0; // The position of this LangBar Item is not specified. + StringCchCopy(_tfLangBarItemInfo.szDescription, ARRAYSIZE(_tfLangBarItemInfo.szDescription), description); // Set the description of this LangBar Item. + + // Initialize the sink pointer to NULL. + _pLangBarItemSink = nullptr; + + // Initialize ICON index and file name. + _onIconIndex = onIconIndex; + _offIconIndex = offIconIndex; + + // Initialize compartment. + _pCompartment = nullptr; + _pCompartmentEventSink = nullptr; + + _isAddedToLanguageBar = FALSE; + _isSecureMode = isSecureMode; + _status = 0; + + _refCount = 1; + + // Initialize Tooltip + _pTooltipText = nullptr; + if (tooltip) + { + size_t len = 0; + if (StringCchLength(tooltip, STRSAFE_MAX_CCH, &len) != S_OK) + { + len = 0; + } + bufLen = static_cast(len) + 1; + _pTooltipText = (LPCWSTR) new (std::nothrow) WCHAR[ bufLen ]; + if (_pTooltipText) + { + StringCchCopy((LPWSTR)_pTooltipText, bufLen, tooltip); + } + } +} + +//+--------------------------------------------------------------------------- +// +// CLangBarItemButton::dtor +// +//---------------------------------------------------------------------------- + +CLangBarItemButton::~CLangBarItemButton() +{ + DllRelease(); + CleanUp(); +} + +//+--------------------------------------------------------------------------- +// +// CLangBarItemButton::CleanUp +// +//---------------------------------------------------------------------------- + +void CLangBarItemButton::CleanUp() +{ + if (_pTooltipText) + { + delete [] _pTooltipText; + _pTooltipText = nullptr; + } + + ITfThreadMgr* pThreadMgr = nullptr; + HRESULT hr = CoCreateInstance(CLSID_TF_ThreadMgr, + NULL, + CLSCTX_INPROC_SERVER, + IID_ITfThreadMgr, + (void**)&pThreadMgr); + if (SUCCEEDED(hr)) + { + _UnregisterCompartment(pThreadMgr); + + _RemoveItem(pThreadMgr); + pThreadMgr->Release(); + pThreadMgr = nullptr; + } + + if (_pCompartment) + { + delete _pCompartment; + _pCompartment = nullptr; + } + + if (_pCompartmentEventSink) + { + delete _pCompartmentEventSink; + _pCompartmentEventSink = nullptr; + } +} + +//+--------------------------------------------------------------------------- +// +// CLangBarItemButton::QueryInterface +// +//---------------------------------------------------------------------------- + +STDAPI CLangBarItemButton::QueryInterface(REFIID riid, _Outptr_ void **ppvObj) +{ + if (ppvObj == nullptr) + { + return E_INVALIDARG; + } + + *ppvObj = nullptr; + + if (IsEqualIID(riid, IID_IUnknown) || + IsEqualIID(riid, IID_ITfLangBarItem) || + IsEqualIID(riid, IID_ITfLangBarItemButton)) + { + *ppvObj = (ITfLangBarItemButton *)this; + } + else if (IsEqualIID(riid, IID_ITfSource)) + { + *ppvObj = (ITfSource *)this; + } + + if (*ppvObj) + { + AddRef(); + return S_OK; + } + + return E_NOINTERFACE; +} + + +//+--------------------------------------------------------------------------- +// +// CLangBarItemButton::AddRef +// +//---------------------------------------------------------------------------- + +STDAPI_(ULONG) CLangBarItemButton::AddRef() +{ + return ++_refCount; +} + +//+--------------------------------------------------------------------------- +// +// CLangBarItemButton::Release +// +//---------------------------------------------------------------------------- + +STDAPI_(ULONG) CLangBarItemButton::Release() +{ + LONG cr = --_refCount; + + assert(_refCount >= 0); + + if (_refCount == 0) + { + delete this; + } + + return cr; +} + +//+--------------------------------------------------------------------------- +// +// GetInfo +// +//---------------------------------------------------------------------------- + +STDAPI CLangBarItemButton::GetInfo(_Out_ TF_LANGBARITEMINFO *pInfo) +{ + _tfLangBarItemInfo.dwStyle |= TF_LBI_STYLE_SHOWNINTRAY; + *pInfo = _tfLangBarItemInfo; + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// GetStatus +// +//---------------------------------------------------------------------------- + +STDAPI CLangBarItemButton::GetStatus(_Out_ DWORD *pdwStatus) +{ + if (pdwStatus == nullptr) + { + E_INVALIDARG; + } + + *pdwStatus = _status; + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// SetStatus +// +//---------------------------------------------------------------------------- + +void CLangBarItemButton::SetStatus(DWORD dwStatus, BOOL fSet) +{ + BOOL isChange = FALSE; + + if (fSet) + { + if (!(_status & dwStatus)) + { + _status |= dwStatus; + isChange = TRUE; + } + } + else + { + if (_status & dwStatus) + { + _status &= ~dwStatus; + isChange = TRUE; + } + } + + if (isChange && _pLangBarItemSink) + { + _pLangBarItemSink->OnUpdate(TF_LBI_STATUS | TF_LBI_ICON); + } + + return; +} + +//+--------------------------------------------------------------------------- +// +// Show +// +//---------------------------------------------------------------------------- + +STDAPI CLangBarItemButton::Show(BOOL fShow) +{ + fShow; + if (_pLangBarItemSink) + { + _pLangBarItemSink->OnUpdate(TF_LBI_STATUS); + } + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// GetTooltipString +// +//---------------------------------------------------------------------------- + +STDAPI CLangBarItemButton::GetTooltipString(_Out_ BSTR *pbstrToolTip) +{ + *pbstrToolTip = SysAllocString(_pTooltipText); + + return (*pbstrToolTip == nullptr) ? E_OUTOFMEMORY : S_OK; +} + +//+--------------------------------------------------------------------------- +// +// OnClick +// +//---------------------------------------------------------------------------- + +STDAPI CLangBarItemButton::OnClick(TfLBIClick click, POINT pt, _In_ const RECT *prcArea) +{ + click;pt; + prcArea; + + BOOL isOn = FALSE; + + _pCompartment->_GetCompartmentBOOL(isOn); + _pCompartment->_SetCompartmentBOOL(isOn ? FALSE : TRUE); + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// InitMenu +// +//---------------------------------------------------------------------------- + +STDAPI CLangBarItemButton::InitMenu(_In_ ITfMenu *pMenu) +{ + pMenu; + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// OnMenuSelect +// +//---------------------------------------------------------------------------- + +STDAPI CLangBarItemButton::OnMenuSelect(UINT wID) +{ + wID; + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// GetIcon +// +//---------------------------------------------------------------------------- + +STDAPI CLangBarItemButton::GetIcon(_Out_ HICON *phIcon) +{ + BOOL isOn = FALSE; + + if (!_pCompartment) + { + return E_FAIL; + } + if (!phIcon) + { + return E_FAIL; + } + *phIcon = nullptr; + + _pCompartment->_GetCompartmentBOOL(isOn); + + DWORD status = 0; + GetStatus(&status); + + // If IME is working on the UAC mode, the size of ICON should be 24 x 24. + int desiredSize = 16; + if (_isSecureMode) // detect UAC mode + { + desiredSize = _isSecureMode ? 24 : 16; + } + + if (isOn && !(status & TF_LBI_STATUS_DISABLED)) + { + if (Global::dllInstanceHandle) + { + *phIcon = reinterpret_cast(LoadImage(Global::dllInstanceHandle, MAKEINTRESOURCE(_onIconIndex), IMAGE_ICON, desiredSize, desiredSize, 0)); + } + } + else + { + if (Global::dllInstanceHandle) + { + *phIcon = reinterpret_cast(LoadImage(Global::dllInstanceHandle, MAKEINTRESOURCE(_offIconIndex), IMAGE_ICON, desiredSize, desiredSize, 0)); + } + } + + return (*phIcon != NULL) ? S_OK : E_FAIL; +} + +//+--------------------------------------------------------------------------- +// +// GetText +// +//---------------------------------------------------------------------------- + +STDAPI CLangBarItemButton::GetText(_Out_ BSTR *pbstrText) +{ + *pbstrText = SysAllocString(_tfLangBarItemInfo.szDescription); + + return (*pbstrText == nullptr) ? E_OUTOFMEMORY : S_OK; +} + +//+--------------------------------------------------------------------------- +// +// AdviseSink +// +//---------------------------------------------------------------------------- + +STDAPI CLangBarItemButton::AdviseSink(__RPC__in REFIID riid, __RPC__in_opt IUnknown *punk, __RPC__out DWORD *pdwCookie) +{ + // We allow only ITfLangBarItemSink interface. + if (!IsEqualIID(IID_ITfLangBarItemSink, riid)) + { + return CONNECT_E_CANNOTCONNECT; + } + + // We support only one sink once. + if (_pLangBarItemSink != nullptr) + { + return CONNECT_E_ADVISELIMIT; + } + + // Query the ITfLangBarItemSink interface and store it into _pLangBarItemSink. + if (punk == nullptr) + { + return E_INVALIDARG; + } + if (punk->QueryInterface(IID_ITfLangBarItemSink, (void **)&_pLangBarItemSink) != S_OK) + { + _pLangBarItemSink = nullptr; + return E_NOINTERFACE; + } + + // return our cookie. + *pdwCookie = _cookie; + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// UnadviseSink +// +//---------------------------------------------------------------------------- + +STDAPI CLangBarItemButton::UnadviseSink(DWORD dwCookie) +{ + // Check the given cookie. + if (dwCookie != _cookie) + { + return CONNECT_E_NOCONNECTION; + } + + // If there is nno connected sink, we just fail. + if (_pLangBarItemSink == nullptr) + { + return CONNECT_E_NOCONNECTION; + } + + _pLangBarItemSink->Release(); + _pLangBarItemSink = nullptr; + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// _AddItem +// +//---------------------------------------------------------------------------- + +HRESULT CLangBarItemButton::_AddItem(_In_ ITfThreadMgr *pThreadMgr) +{ + HRESULT hr = S_OK; + ITfLangBarItemMgr* pLangBarItemMgr = nullptr; + + if (_isAddedToLanguageBar) + { + return S_OK; + } + + hr = pThreadMgr->QueryInterface(IID_ITfLangBarItemMgr, (void **)&pLangBarItemMgr); + if (SUCCEEDED(hr)) + { + hr = pLangBarItemMgr->AddItem(this); + if (SUCCEEDED(hr)) + { + _isAddedToLanguageBar = TRUE; + } + pLangBarItemMgr->Release(); + } + + return hr; +} + +//+--------------------------------------------------------------------------- +// +// _RemoveItem +// +//---------------------------------------------------------------------------- + +HRESULT CLangBarItemButton::_RemoveItem(_In_ ITfThreadMgr *pThreadMgr) +{ + HRESULT hr = S_OK; + ITfLangBarItemMgr* pLangBarItemMgr = nullptr; + + if (!_isAddedToLanguageBar) + { + return S_OK; + } + + hr = pThreadMgr->QueryInterface(IID_ITfLangBarItemMgr, (void **)&pLangBarItemMgr); + if (SUCCEEDED(hr)) + { + hr = pLangBarItemMgr->RemoveItem(this); + if (SUCCEEDED(hr)) + { + _isAddedToLanguageBar = FALSE; + } + pLangBarItemMgr->Release(); + } + + return hr; +} + +//+--------------------------------------------------------------------------- +// +// _RegisterCompartment +// +//---------------------------------------------------------------------------- + +BOOL CLangBarItemButton::_RegisterCompartment(_In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId, REFGUID guidCompartment) +{ + _pCompartment = new (std::nothrow) CCompartment(pThreadMgr, tfClientId, guidCompartment); + if (_pCompartment) + { + // Advice ITfCompartmentEventSink + _pCompartmentEventSink = new (std::nothrow) CCompartmentEventSink(_CompartmentCallback, this); + if (_pCompartmentEventSink) + { + _pCompartmentEventSink->_Advise(pThreadMgr, guidCompartment); + } + else + { + delete _pCompartment; + _pCompartment = nullptr; + } + } + + return _pCompartment ? TRUE : FALSE; +} + +//+--------------------------------------------------------------------------- +// +// _UnregisterCompartment +// +//---------------------------------------------------------------------------- + +BOOL CLangBarItemButton::_UnregisterCompartment(_In_ ITfThreadMgr *pThreadMgr) +{ + pThreadMgr; + if (_pCompartment) + { + // Unadvice ITfCompartmentEventSink + if (_pCompartmentEventSink) + { + _pCompartmentEventSink->_Unadvise(); + } + + // clear ITfCompartment + _pCompartment->_ClearCompartment(); + } + + return TRUE; +} + +//+--------------------------------------------------------------------------- +// +// _CompartmentCallback +// +//---------------------------------------------------------------------------- + +// static +HRESULT CLangBarItemButton::_CompartmentCallback(_In_ void *pv, REFGUID guidCompartment) +{ + CLangBarItemButton* fakeThis = (CLangBarItemButton*)pv; + + GUID guid = GUID_NULL; + fakeThis->_pCompartment->_GetGUID(&guid); + + if (IsEqualGUID(guid, guidCompartment)) + { + if (fakeThis->_pLangBarItemSink) + { + fakeThis->_pLangBarItemSink->OnUpdate(TF_LBI_STATUS | TF_LBI_ICON); + } + } + + return S_OK; +} diff --git a/LanguageBar.h b/LanguageBar.h new file mode 100644 index 00000000..491cf0c6 --- /dev/null +++ b/LanguageBar.h @@ -0,0 +1,74 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#pragma once + +class CCompartment; +class CCompartmentEventSink; + +class CLangBarItemButton : public ITfLangBarItemButton, + public ITfSource +{ +public: + CLangBarItemButton(REFGUID guidLangBar, LPCWSTR description, LPCWSTR tooltip, DWORD onIconIndex, DWORD offIconIndex, BOOL isSecureMode); + ~CLangBarItemButton(); + + // IUnknown + STDMETHODIMP QueryInterface(REFIID riid, _Outptr_ void **ppvObj); + STDMETHODIMP_(ULONG) AddRef(void); + STDMETHODIMP_(ULONG) Release(void); + + // ITfLangBarItem + STDMETHODIMP GetInfo(_Out_ TF_LANGBARITEMINFO *pInfo); + STDMETHODIMP GetStatus(_Out_ DWORD *pdwStatus); + STDMETHODIMP Show(BOOL fShow); + STDMETHODIMP GetTooltipString(_Out_ BSTR *pbstrToolTip); + + // ITfLangBarItemButton + STDMETHODIMP OnClick(TfLBIClick click, POINT pt, _In_ const RECT *prcArea); + STDMETHODIMP InitMenu(_In_ ITfMenu *pMenu); + STDMETHODIMP OnMenuSelect(UINT wID); + STDMETHODIMP GetIcon(_Out_ HICON *phIcon); + STDMETHODIMP GetText(_Out_ BSTR *pbstrText); + + // ITfSource + STDMETHODIMP AdviseSink(__RPC__in REFIID riid, __RPC__in_opt IUnknown *punk, __RPC__out DWORD *pdwCookie); + STDMETHODIMP UnadviseSink(DWORD dwCookie); + + // Add/Remove languagebar item + HRESULT _AddItem(_In_ ITfThreadMgr *pThreadMgr); + HRESULT _RemoveItem(_In_ ITfThreadMgr *pThreadMgr); + + // Register compartment for button On/Off switch + BOOL _RegisterCompartment(_In_ ITfThreadMgr *pThreadMgr, TfClientId tfClientId, REFGUID guidCompartment); + BOOL _UnregisterCompartment(_In_ ITfThreadMgr *pThreadMgr); + + void CleanUp(); + + void SetStatus(DWORD dwStatus, BOOL fSet); + +private: + ITfLangBarItemSink* _pLangBarItemSink; + + TF_LANGBARITEMINFO _tfLangBarItemInfo; + LPCWSTR _pTooltipText; + DWORD _onIconIndex; + DWORD _offIconIndex; + + BOOL _isAddedToLanguageBar; + BOOL _isSecureMode; + DWORD _status; + + CCompartment* _pCompartment; + CCompartmentEventSink* _pCompartmentEventSink; + static HRESULT _CompartmentCallback(_In_ void *pv, REFGUID guidCompartment); + + // The cookie for the sink to CLangBarItemButton. + static const DWORD _cookie = 0; + + LONG _refCount; +}; diff --git a/Private.h b/Private.h new file mode 100644 index 00000000..3ddfecb5 --- /dev/null +++ b/Private.h @@ -0,0 +1,22 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#pragma once + +#include "stdafx.h" +#include "sal.h" + +#include +#include +#include + +#include +#include + +#include "initguid.h" +#include "msctf.h" +#include "ctffunc.h" diff --git a/README.md b/README.md new file mode 100644 index 00000000..b3c02a7e --- /dev/null +++ b/README.md @@ -0,0 +1,13 @@ +how to install ime + +``` +regsvr32 "C:\Program Files\SampleIME\SampleIME.dll" +regsvr32 "C:\Program Files (x86)\SampleIME\SampleIME.dll" +``` + +how to uninstall ime + +```shell +regsvr32 /u "C:\Program Files\SampleIME\SampleIME.dll" +regsvr32 /u "C:\Program Files (x86)\SampleIME\SampleIME.dll" +``` diff --git a/RegKey.cpp b/RegKey.cpp new file mode 100644 index 00000000..63f387ce --- /dev/null +++ b/RegKey.cpp @@ -0,0 +1,278 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#include "Private.h" +#include "RegKey.h" + +//--------------------------------------------------------------------- +// +// ctor +// +//--------------------------------------------------------------------- + +CRegKey::CRegKey() +{ + _keyHandle = nullptr; +} + +//--------------------------------------------------------------------- +// +// dtor +// +//--------------------------------------------------------------------- + +CRegKey::~CRegKey() +{ + Close(); +} + + +//--------------------------------------------------------------------- +// +// operator +// +//--------------------------------------------------------------------- + +HKEY CRegKey::GetHKEY() +{ + return _keyHandle; +} + +//--------------------------------------------------------------------- +// +// Create +// +//--------------------------------------------------------------------- + +LONG CRegKey::Create(_In_ HKEY hKeyPresent, _In_ LPCWSTR pwszKeyName, _In_reads_opt_(255) LPWSTR pwszClass, DWORD dwOptions, REGSAM samDesired, _Inout_ LPSECURITY_ATTRIBUTES lpSecAttr, _Out_opt_ LPDWORD lpdwDisposition) +{ + DWORD disposition = 0; + HKEY keyHandle = nullptr; + + LONG res = RegCreateKeyEx(hKeyPresent, pwszKeyName, 0, + pwszClass, dwOptions, samDesired, lpSecAttr, &keyHandle, &disposition); + + if (lpdwDisposition != nullptr) + { + *lpdwDisposition = disposition; + } + + if (res == ERROR_SUCCESS) + { + Close(); + _keyHandle = keyHandle; + } + + return res; +} + +//--------------------------------------------------------------------- +// +// Open +// +//--------------------------------------------------------------------- + +LONG CRegKey::Open(_In_ HKEY hKeyParent, _In_ LPCWSTR pwszKeyName, REGSAM samDesired) +{ + HKEY keyHandle = nullptr; + + LONG res = RegOpenKeyEx(hKeyParent, pwszKeyName, 0, samDesired, &keyHandle); + if (res == ERROR_SUCCESS) + { + Close(); + _keyHandle = keyHandle; + } + return res; +} + +//--------------------------------------------------------------------- +// +// Close +// +//--------------------------------------------------------------------- + +LONG CRegKey::Close() +{ + LONG res = ERROR_SUCCESS; + if (_keyHandle) + { + res = RegCloseKey(_keyHandle); + _keyHandle = nullptr; + } + return res; +} + +//--------------------------------------------------------------------- +// +// DeleteSubKey +// RecurseDeleteKey +// +//--------------------------------------------------------------------- + +LONG CRegKey::DeleteSubKey(_In_ LPCWSTR pwszSubKey) +{ + return RegDeleteKey(_keyHandle, pwszSubKey); +} + +LONG CRegKey::RecurseDeleteKey(_In_ LPCWSTR pwszSubKey) +{ + CRegKey key; + LONG res = key.Open(_keyHandle, pwszSubKey, KEY_READ | KEY_WRITE); + if (res != ERROR_SUCCESS) + { + return res; + } + FILETIME time; + WCHAR subKeyName[256] = {'\0'}; + DWORD subKeyNameSize = ARRAYSIZE(subKeyName); + + while (RegEnumKeyEx(key.GetHKEY(), 0, subKeyName, &subKeyNameSize, NULL, NULL, NULL, &time) == ERROR_SUCCESS) + { + subKeyName[ARRAYSIZE(subKeyName)-1] = L'\0'; + res = key.RecurseDeleteKey(subKeyName); + if (res != ERROR_SUCCESS) + { + return res; + } + subKeyNameSize = ARRAYSIZE(subKeyName); + } + + key.Close(); + + return DeleteSubKey(pwszSubKey); +} + +//--------------------------------------------------------------------- +// +// DeleteValue +// +//--------------------------------------------------------------------- + +LONG CRegKey::DeleteValue(_In_ LPCWSTR pwszValue) +{ + return RegDeleteValue(_keyHandle, pwszValue); +} + +//--------------------------------------------------------------------- +// +// QueryStingValue +// SetStringValue +// +//--------------------------------------------------------------------- + +LONG CRegKey::QueryStringValue(_In_opt_ LPCWSTR pwszValueName, _Out_writes_opt_(*pnChars) LPWSTR pwszValue, _Inout_ ULONG *pnChars) +{ + LONG res = 0; + DWORD dataType = REG_NONE; + ULONG pwszValueSize = 0; + + if (pnChars == nullptr) + { + return E_INVALIDARG; + } + + pwszValueSize = (*pnChars)*sizeof(WCHAR); + *pnChars = 0; + + res = RegQueryValueEx(_keyHandle, pwszValueName, NULL, &dataType, (LPBYTE)pwszValue, &pwszValueSize); + if (res != ERROR_SUCCESS) + { + return res; + } + if ((dataType != REG_SZ) && (dataType != REG_EXPAND_SZ)) + { + return ERROR_INVALID_DATA; + } + + *pnChars = pwszValueSize / sizeof(WCHAR); + + return ERROR_SUCCESS; +} + +LONG CRegKey::SetStringValue(_In_opt_ LPCWSTR pwszValueName, _In_ LPCWSTR pwszValue, DWORD dwType) +{ + size_t lenOfValue = 0; + if (pwszValue == nullptr) + { + return ERROR_INVALID_PARAMETER; + } + + if (StringCchLength(pwszValue, STRSAFE_MAX_CCH, &lenOfValue) != S_OK) + { + return ERROR_INVALID_PARAMETER; + } + DWORD len = static_cast(lenOfValue); + return RegSetValueEx(_keyHandle, pwszValueName, NULL, dwType, (LPBYTE)pwszValue, (++len)*sizeof(WCHAR)); +} + +//--------------------------------------------------------------------- +// +// QueryDWORDValue +// SetDWORDValue +// +//--------------------------------------------------------------------- + +LONG CRegKey::QueryDWORDValue(_In_opt_ LPCWSTR pwszValueName, _Out_ DWORD &dwValue) +{ + LONG res = 0; + DWORD dataType = REG_NONE; + ULONG pwszValueSize = 0; + + pwszValueSize = sizeof(DWORD); + res = RegQueryValueEx(_keyHandle, pwszValueName, NULL, &dataType, (LPBYTE)(&dwValue), &pwszValueSize); + if (res != ERROR_SUCCESS) + { + return res; + } + if (dataType != REG_DWORD) + { + return ERROR_INVALID_DATA; + } + + return ERROR_SUCCESS; +} + +LONG CRegKey::SetDWORDValue(_In_opt_ LPCWSTR pwszValueName, DWORD dwValue) +{ + return RegSetValueEx(_keyHandle, pwszValueName, NULL, REG_DWORD, (LPBYTE)(&dwValue), sizeof(DWORD)); +} + +//--------------------------------------------------------------------- +// +// QueryBinaryValue +// SetBinaryValue +// +//--------------------------------------------------------------------- + +LONG CRegKey::QueryBinaryValue(_In_opt_ LPCWSTR pwszValueName, _Out_writes_opt_(cbData) BYTE* lpData, DWORD cbData) +{ + LONG res = 0; + DWORD dataType = REG_NONE; + ULONG pwszValueSize = 0; + + pwszValueSize = cbData; + res = RegQueryValueEx(_keyHandle, pwszValueName, NULL, &dataType, lpData, &pwszValueSize); + if (res != ERROR_SUCCESS) + { + return res; + } + if (dataType != REG_BINARY) + { + return ERROR_INVALID_DATA; + } + if (pwszValueSize != cbData) + { + return ERROR_INVALID_DATA; + } + + return ERROR_SUCCESS; +} + +LONG CRegKey::SetBinaryValue(_In_opt_ LPCWSTR pwszValueName, _In_reads_(cbData) BYTE* lpData, DWORD cbData) +{ + return RegSetValueEx(_keyHandle, pwszValueName, NULL, REG_BINARY, lpData, cbData); +} diff --git a/RegKey.h b/RegKey.h new file mode 100644 index 00000000..cb251fd9 --- /dev/null +++ b/RegKey.h @@ -0,0 +1,45 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#pragma once + +class CRegKey +{ +public: + CRegKey(); + ~CRegKey(); + + HKEY GetHKEY(); + + LONG Create(_In_ HKEY hKeyPresent, _In_ LPCWSTR pwszKeyName, + _In_reads_opt_(255) LPWSTR pwszClass = REG_NONE, + DWORD dwOptions = REG_OPTION_NON_VOLATILE, + REGSAM samDesired = KEY_READ | KEY_WRITE, + _Inout_ LPSECURITY_ATTRIBUTES lpSecAttr = nullptr, + _Out_opt_ LPDWORD lpdwDisposition = nullptr); + + LONG Open(_In_ HKEY hKeyParent, _In_ LPCWSTR pwszKeyName, + REGSAM samDesired = KEY_READ | KEY_WRITE); + + LONG Close(); + + LONG DeleteSubKey(_In_ LPCWSTR pwszSubKey); + LONG RecurseDeleteKey(_In_ LPCWSTR pwszSubKey); + LONG DeleteValue(_In_ LPCWSTR pwszValue); + + LONG QueryStringValue(_In_opt_ LPCWSTR pwszValueName, _Out_writes_opt_(*pnChars) LPWSTR pwszValue, _Inout_ ULONG *pnChars); + LONG SetStringValue(_In_opt_ LPCWSTR pwszValueName, _In_ LPCWSTR pwszValue, DWORD dwType = REG_SZ); + + LONG QueryDWORDValue(_In_opt_ LPCWSTR pwszValueName, _Out_ DWORD &dwValue); + LONG SetDWORDValue(_In_opt_ LPCWSTR pwszValueName, DWORD dwValue); + + LONG QueryBinaryValue(_In_opt_ LPCWSTR pwszValueName, _Out_writes_opt_(cbData) BYTE* lpData, DWORD cbData); + LONG SetBinaryValue(_In_opt_ LPCWSTR pwszValueName, _In_reads_(cbData) BYTE* lpData, DWORD cbData); + +private: + HKEY _keyHandle; +}; diff --git a/Register.cpp b/Register.cpp new file mode 100644 index 00000000..f9ef2b9a --- /dev/null +++ b/Register.cpp @@ -0,0 +1,283 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#include "Private.h" +#include "Globals.h" + +static const WCHAR RegInfo_Prefix_CLSID[] = L"CLSID\\"; +static const WCHAR RegInfo_Key_InProSvr32[] = L"InProcServer32"; +static const WCHAR RegInfo_Key_ThreadModel[] = L"ThreadingModel"; + +static const WCHAR TEXTSERVICE_DESC[] = L"Sample IME"; + +static const GUID SupportCategories[] = { + GUID_TFCAT_TIP_KEYBOARD, + GUID_TFCAT_DISPLAYATTRIBUTEPROVIDER, + GUID_TFCAT_TIPCAP_UIELEMENTENABLED, + GUID_TFCAT_TIPCAP_SECUREMODE, + GUID_TFCAT_TIPCAP_COMLESS, + GUID_TFCAT_TIPCAP_INPUTMODECOMPARTMENT, + GUID_TFCAT_TIPCAP_IMMERSIVESUPPORT, + GUID_TFCAT_TIPCAP_SYSTRAYSUPPORT, +}; +//+--------------------------------------------------------------------------- +// +// RegisterProfiles +// +//---------------------------------------------------------------------------- + +BOOL RegisterProfiles() +{ + HRESULT hr = S_FALSE; + + ITfInputProcessorProfileMgr *pITfInputProcessorProfileMgr = nullptr; + hr = CoCreateInstance(CLSID_TF_InputProcessorProfiles, NULL, CLSCTX_INPROC_SERVER, + IID_ITfInputProcessorProfileMgr, (void**)&pITfInputProcessorProfileMgr); + if (FAILED(hr)) + { + return FALSE; + } + + WCHAR achIconFile[MAX_PATH] = {'\0'}; + DWORD cchA = 0; + cchA = GetModuleFileName(Global::dllInstanceHandle, achIconFile, MAX_PATH); + cchA = cchA >= MAX_PATH ? (MAX_PATH - 1) : cchA; + achIconFile[cchA] = '\0'; + + size_t lenOfDesc = 0; + hr = StringCchLength(TEXTSERVICE_DESC, STRSAFE_MAX_CCH, &lenOfDesc); + if (hr != S_OK) + { + goto Exit; + } + hr = pITfInputProcessorProfileMgr->RegisterProfile(Global::SampleIMECLSID, + TEXTSERVICE_LANGID, + Global::SampleIMEGuidProfile, + TEXTSERVICE_DESC, + static_cast(lenOfDesc), + achIconFile, + cchA, + (UINT)TEXTSERVICE_ICON_INDEX, NULL, 0, TRUE, 0); + + if (FAILED(hr)) + { + goto Exit; + } + +Exit: + if (pITfInputProcessorProfileMgr) + { + pITfInputProcessorProfileMgr->Release(); + } + + return (hr == S_OK); +} + +//+--------------------------------------------------------------------------- +// +// UnregisterProfiles +// +//---------------------------------------------------------------------------- + +void UnregisterProfiles() +{ + HRESULT hr = S_OK; + + ITfInputProcessorProfileMgr *pITfInputProcessorProfileMgr = nullptr; + hr = CoCreateInstance(CLSID_TF_InputProcessorProfiles, NULL, CLSCTX_INPROC_SERVER, + IID_ITfInputProcessorProfileMgr, (void**)&pITfInputProcessorProfileMgr); + if (FAILED(hr)) + { + goto Exit; + } + + hr = pITfInputProcessorProfileMgr->UnregisterProfile(Global::SampleIMECLSID, TEXTSERVICE_LANGID, Global::SampleIMEGuidProfile, 0); + if (FAILED(hr)) + { + goto Exit; + } + +Exit: + if (pITfInputProcessorProfileMgr) + { + pITfInputProcessorProfileMgr->Release(); + } + + return; +} + +//+--------------------------------------------------------------------------- +// +// RegisterCategories +// +//---------------------------------------------------------------------------- + +BOOL RegisterCategories() +{ + ITfCategoryMgr* pCategoryMgr = nullptr; + HRESULT hr = S_OK; + + hr = CoCreateInstance(CLSID_TF_CategoryMgr, NULL, CLSCTX_INPROC_SERVER, IID_ITfCategoryMgr, (void**)&pCategoryMgr); + if (FAILED(hr)) + { + return FALSE; + } + + for each(GUID guid in SupportCategories) + { + hr = pCategoryMgr->RegisterCategory(Global::SampleIMECLSID, guid, Global::SampleIMECLSID); + } + + pCategoryMgr->Release(); + + return (hr == S_OK); +} + +//+--------------------------------------------------------------------------- +// +// UnregisterCategories +// +//---------------------------------------------------------------------------- + +void UnregisterCategories() +{ + ITfCategoryMgr* pCategoryMgr = S_OK; + HRESULT hr = S_OK; + + hr = CoCreateInstance(CLSID_TF_CategoryMgr, NULL, CLSCTX_INPROC_SERVER, IID_ITfCategoryMgr, (void**)&pCategoryMgr); + if (FAILED(hr)) + { + return; + } + + for each(GUID guid in SupportCategories) + { + pCategoryMgr->UnregisterCategory(Global::SampleIMECLSID, guid, Global::SampleIMECLSID); + } + + pCategoryMgr->Release(); + + return; +} + +//+--------------------------------------------------------------------------- +// +// RecurseDeleteKey +// +// RecurseDeleteKey is necessary because on NT RegDeleteKey doesn't work if the +// specified key has subkeys +//---------------------------------------------------------------------------- + +LONG RecurseDeleteKey(_In_ HKEY hParentKey, _In_ LPCTSTR lpszKey) +{ + HKEY regKeyHandle = nullptr; + LONG res = 0; + FILETIME time; + WCHAR stringBuffer[256] = {'\0'}; + DWORD size = ARRAYSIZE(stringBuffer); + + if (RegOpenKey(hParentKey, lpszKey, ®KeyHandle) != ERROR_SUCCESS) + { + return ERROR_SUCCESS; + } + + res = ERROR_SUCCESS; + while (RegEnumKeyEx(regKeyHandle, 0, stringBuffer, &size, NULL, NULL, NULL, &time) == ERROR_SUCCESS) + { + stringBuffer[ARRAYSIZE(stringBuffer)-1] = '\0'; + res = RecurseDeleteKey(regKeyHandle, stringBuffer); + if (res != ERROR_SUCCESS) + { + break; + } + size = ARRAYSIZE(stringBuffer); + } + RegCloseKey(regKeyHandle); + + return res == ERROR_SUCCESS ? RegDeleteKey(hParentKey, lpszKey) : res; +} + +//+--------------------------------------------------------------------------- +// +// RegisterServer +// +//---------------------------------------------------------------------------- + +BOOL RegisterServer() +{ + DWORD copiedStringLen = 0; + HKEY regKeyHandle = nullptr; + HKEY regSubkeyHandle = nullptr; + BOOL ret = FALSE; + WCHAR achIMEKey[ARRAYSIZE(RegInfo_Prefix_CLSID) + CLSID_STRLEN] = {'\0'}; + WCHAR achFileName[MAX_PATH] = {'\0'}; + + if (!CLSIDToString(Global::SampleIMECLSID, achIMEKey + ARRAYSIZE(RegInfo_Prefix_CLSID) - 1)) + { + return FALSE; + } + + memcpy(achIMEKey, RegInfo_Prefix_CLSID, sizeof(RegInfo_Prefix_CLSID) - sizeof(WCHAR)); + + if (RegCreateKeyEx(HKEY_CLASSES_ROOT, achIMEKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, ®KeyHandle, &copiedStringLen) == ERROR_SUCCESS) + { + if (RegSetValueEx(regKeyHandle, NULL, 0, REG_SZ, (const BYTE *)TEXTSERVICE_DESC, (_countof(TEXTSERVICE_DESC))*sizeof(WCHAR)) != ERROR_SUCCESS) + { + goto Exit; + } + + if (RegCreateKeyEx(regKeyHandle, RegInfo_Key_InProSvr32, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, ®SubkeyHandle, &copiedStringLen) == ERROR_SUCCESS) + { + copiedStringLen = GetModuleFileNameW(Global::dllInstanceHandle, achFileName, ARRAYSIZE(achFileName)); + copiedStringLen = (copiedStringLen >= (MAX_PATH - 1)) ? MAX_PATH : (++copiedStringLen); + if (RegSetValueEx(regSubkeyHandle, NULL, 0, REG_SZ, (const BYTE *)achFileName, (copiedStringLen)*sizeof(WCHAR)) != ERROR_SUCCESS) + { + goto Exit; + } + if (RegSetValueEx(regSubkeyHandle, RegInfo_Key_ThreadModel, 0, REG_SZ, (const BYTE *)TEXTSERVICE_MODEL, (_countof(TEXTSERVICE_MODEL)) * sizeof(WCHAR)) != ERROR_SUCCESS) + { + goto Exit; + } + + ret = TRUE; + } + } + +Exit: + if (regSubkeyHandle) + { + RegCloseKey(regSubkeyHandle); + regSubkeyHandle = nullptr; + } + if (regKeyHandle) + { + RegCloseKey(regKeyHandle); + regKeyHandle = nullptr; + } + + return ret; +} + +//+--------------------------------------------------------------------------- +// +// UnregisterServer +// +//---------------------------------------------------------------------------- + +void UnregisterServer() +{ + WCHAR achIMEKey[ARRAYSIZE(RegInfo_Prefix_CLSID) + CLSID_STRLEN] = {'\0'}; + + if (!CLSIDToString(Global::SampleIMECLSID, achIMEKey + ARRAYSIZE(RegInfo_Prefix_CLSID) - 1)) + { + return; + } + + memcpy(achIMEKey, RegInfo_Prefix_CLSID, sizeof(RegInfo_Prefix_CLSID) - sizeof(WCHAR)); + + RecurseDeleteKey(HKEY_CLASSES_ROOT, achIMEKey); +} diff --git a/SampleIME.cpp b/SampleIME.cpp new file mode 100644 index 00000000..374f6da8 --- /dev/null +++ b/SampleIME.cpp @@ -0,0 +1,432 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#include "Private.h" +#include "globals.h" +#include "SampleIME.h" +#include "CandidateListUIPresenter.h" +#include "CompositionProcessorEngine.h" +#include "Compartment.h" + +//+--------------------------------------------------------------------------- +// +// CreateInstance +// +//---------------------------------------------------------------------------- + +/* static */ +HRESULT CSampleIME::CreateInstance(_In_ IUnknown *pUnkOuter, REFIID riid, _Outptr_ void **ppvObj) +{ + CSampleIME* pSampleIME = nullptr; + HRESULT hr = S_OK; + + if (ppvObj == nullptr) + { + return E_INVALIDARG; + } + + *ppvObj = nullptr; + + if (nullptr != pUnkOuter) + { + return CLASS_E_NOAGGREGATION; + } + + pSampleIME = new (std::nothrow) CSampleIME(); + if (pSampleIME == nullptr) + { + return E_OUTOFMEMORY; + } + + hr = pSampleIME->QueryInterface(riid, ppvObj); + + pSampleIME->Release(); + + return hr; +} + +//+--------------------------------------------------------------------------- +// +// ctor +// +//---------------------------------------------------------------------------- + +CSampleIME::CSampleIME() +{ + DllAddRef(); + + _pThreadMgr = nullptr; + + _threadMgrEventSinkCookie = TF_INVALID_COOKIE; + + _pTextEditSinkContext = nullptr; + _textEditSinkCookie = TF_INVALID_COOKIE; + + _activeLanguageProfileNotifySinkCookie = TF_INVALID_COOKIE; + + _dwThreadFocusSinkCookie = TF_INVALID_COOKIE; + + _pComposition = nullptr; + + _pCompositionProcessorEngine = nullptr; + + _candidateMode = CANDIDATE_NONE; + _pCandidateListUIPresenter = nullptr; + _isCandidateWithWildcard = FALSE; + + _pDocMgrLastFocused = nullptr; + + _pSIPIMEOnOffCompartment = nullptr; + _dwSIPIMEOnOffCompartmentSinkCookie = 0; + _msgWndHandle = nullptr; + + _pContext = nullptr; + + _refCount = 1; +} + +//+--------------------------------------------------------------------------- +// +// dtor +// +//---------------------------------------------------------------------------- + +CSampleIME::~CSampleIME() +{ + if (_pCandidateListUIPresenter) + { + delete _pCandidateListUIPresenter; + _pCandidateListUIPresenter = nullptr; + } + DllRelease(); +} + +//+--------------------------------------------------------------------------- +// +// QueryInterface +// +//---------------------------------------------------------------------------- + +STDAPI CSampleIME::QueryInterface(REFIID riid, _Outptr_ void **ppvObj) +{ + if (ppvObj == nullptr) + { + return E_INVALIDARG; + } + + *ppvObj = nullptr; + + if (IsEqualIID(riid, IID_IUnknown) || + IsEqualIID(riid, IID_ITfTextInputProcessor)) + { + *ppvObj = (ITfTextInputProcessor *)this; + } + else if (IsEqualIID(riid, IID_ITfTextInputProcessorEx)) + { + *ppvObj = (ITfTextInputProcessorEx *)this; + } + else if (IsEqualIID(riid, IID_ITfThreadMgrEventSink)) + { + *ppvObj = (ITfThreadMgrEventSink *)this; + } + else if (IsEqualIID(riid, IID_ITfTextEditSink)) + { + *ppvObj = (ITfTextEditSink *)this; + } + else if (IsEqualIID(riid, IID_ITfKeyEventSink)) + { + *ppvObj = (ITfKeyEventSink *)this; + } + else if (IsEqualIID(riid, IID_ITfActiveLanguageProfileNotifySink)) + { + *ppvObj = (ITfActiveLanguageProfileNotifySink *)this; + } + else if (IsEqualIID(riid, IID_ITfCompositionSink)) + { + *ppvObj = (ITfKeyEventSink *)this; + } + else if (IsEqualIID(riid, IID_ITfDisplayAttributeProvider)) + { + *ppvObj = (ITfDisplayAttributeProvider *)this; + } + else if (IsEqualIID(riid, IID_ITfThreadFocusSink)) + { + *ppvObj = (ITfThreadFocusSink *)this; + } + else if (IsEqualIID(riid, IID_ITfFunctionProvider)) + { + *ppvObj = (ITfFunctionProvider *)this; + } + else if (IsEqualIID(riid, IID_ITfFunction)) + { + *ppvObj = (ITfFunction *)this; + } + else if (IsEqualIID(riid, IID_ITfFnGetPreferredTouchKeyboardLayout)) + { + *ppvObj = (ITfFnGetPreferredTouchKeyboardLayout *)this; + } + + if (*ppvObj) + { + AddRef(); + return S_OK; + } + + return E_NOINTERFACE; +} + + +//+--------------------------------------------------------------------------- +// +// AddRef +// +//---------------------------------------------------------------------------- + +STDAPI_(ULONG) CSampleIME::AddRef() +{ + return ++_refCount; +} + +//+--------------------------------------------------------------------------- +// +// Release +// +//---------------------------------------------------------------------------- + +STDAPI_(ULONG) CSampleIME::Release() +{ + LONG cr = --_refCount; + + assert(_refCount >= 0); + + if (_refCount == 0) + { + delete this; + } + + return cr; +} + +//+--------------------------------------------------------------------------- +// +// ITfTextInputProcessorEx::ActivateEx +// +//---------------------------------------------------------------------------- + +STDAPI CSampleIME::ActivateEx(ITfThreadMgr *pThreadMgr, TfClientId tfClientId, DWORD dwFlags) +{ + _pThreadMgr = pThreadMgr; + _pThreadMgr->AddRef(); + + _tfClientId = tfClientId; + _dwActivateFlags = dwFlags; + + if (!_InitThreadMgrEventSink()) + { + goto ExitError; + } + + ITfDocumentMgr* pDocMgrFocus = nullptr; + if (SUCCEEDED(_pThreadMgr->GetFocus(&pDocMgrFocus)) && (pDocMgrFocus != nullptr)) + { + _InitTextEditSink(pDocMgrFocus); + pDocMgrFocus->Release(); + } + + if (!_InitKeyEventSink()) + { + goto ExitError; + } + + if (!_InitActiveLanguageProfileNotifySink()) + { + goto ExitError; + } + + if (!_InitThreadFocusSink()) + { + goto ExitError; + } + + if (!_InitDisplayAttributeGuidAtom()) + { + goto ExitError; + } + + if (!_InitFunctionProviderSink()) + { + goto ExitError; + } + + if (!_AddTextProcessorEngine()) + { + goto ExitError; + } + + return S_OK; + +ExitError: + Deactivate(); + return E_FAIL; +} + +//+--------------------------------------------------------------------------- +// +// ITfTextInputProcessorEx::Deactivate +// +//---------------------------------------------------------------------------- + +STDAPI CSampleIME::Deactivate() +{ + if (_pCompositionProcessorEngine) + { + delete _pCompositionProcessorEngine; + _pCompositionProcessorEngine = nullptr; + } + + ITfContext* pContext = _pContext; + if (_pContext) + { + pContext->AddRef(); + _EndComposition(_pContext); + } + + if (_pCandidateListUIPresenter) + { + delete _pCandidateListUIPresenter; + _pCandidateListUIPresenter = nullptr; + + if (pContext) + { + pContext->Release(); + } + + _candidateMode = CANDIDATE_NONE; + _isCandidateWithWildcard = FALSE; + } + + _UninitFunctionProviderSink(); + + _UninitThreadFocusSink(); + + _UninitActiveLanguageProfileNotifySink(); + + _UninitKeyEventSink(); + + _UninitThreadMgrEventSink(); + + CCompartment CompartmentKeyboardOpen(_pThreadMgr, _tfClientId, GUID_COMPARTMENT_KEYBOARD_OPENCLOSE); + CompartmentKeyboardOpen._ClearCompartment(); + + CCompartment CompartmentDoubleSingleByte(_pThreadMgr, _tfClientId, Global::SampleIMEGuidCompartmentDoubleSingleByte); + CompartmentDoubleSingleByte._ClearCompartment(); + + CCompartment CompartmentPunctuation(_pThreadMgr, _tfClientId, Global::SampleIMEGuidCompartmentPunctuation); + CompartmentDoubleSingleByte._ClearCompartment(); + + if (_pThreadMgr != nullptr) + { + _pThreadMgr->Release(); + } + + _tfClientId = TF_CLIENTID_NULL; + + if (_pDocMgrLastFocused) + { + _pDocMgrLastFocused->Release(); + _pDocMgrLastFocused = nullptr; + } + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// ITfFunctionProvider::GetType +// +//---------------------------------------------------------------------------- +HRESULT CSampleIME::GetType(__RPC__out GUID *pguid) +{ + HRESULT hr = E_INVALIDARG; + if (pguid) + { + *pguid = Global::SampleIMECLSID; + hr = S_OK; + } + return hr; +} + +//+--------------------------------------------------------------------------- +// +// ITfFunctionProvider::::GetDescription +// +//---------------------------------------------------------------------------- +HRESULT CSampleIME::GetDescription(__RPC__deref_out_opt BSTR *pbstrDesc) +{ + HRESULT hr = E_INVALIDARG; + if (pbstrDesc != nullptr) + { + *pbstrDesc = nullptr; + hr = E_NOTIMPL; + } + return hr; +} + +//+--------------------------------------------------------------------------- +// +// ITfFunctionProvider::::GetFunction +// +//---------------------------------------------------------------------------- +HRESULT CSampleIME::GetFunction(__RPC__in REFGUID rguid, __RPC__in REFIID riid, __RPC__deref_out_opt IUnknown **ppunk) +{ + HRESULT hr = E_NOINTERFACE; + + if ((IsEqualGUID(rguid, GUID_NULL)) + && (IsEqualGUID(riid, __uuidof(ITfFnSearchCandidateProvider)))) + { + hr = _pITfFnSearchCandidateProvider->QueryInterface(riid, (void**)ppunk); + } + else if (IsEqualGUID(rguid, GUID_NULL)) + { + hr = QueryInterface(riid, (void **)ppunk); + } + + return hr; +} + +//+--------------------------------------------------------------------------- +// +// ITfFunction::GetDisplayName +// +//---------------------------------------------------------------------------- +HRESULT CSampleIME::GetDisplayName(_Out_ BSTR *pbstrDisplayName) +{ + HRESULT hr = E_INVALIDARG; + if (pbstrDisplayName != nullptr) + { + *pbstrDisplayName = nullptr; + hr = E_NOTIMPL; + } + return hr; +} + +//+--------------------------------------------------------------------------- +// +// ITfFnGetPreferredTouchKeyboardLayout::GetLayout +// The tkblayout will be Optimized layout. +//---------------------------------------------------------------------------- +HRESULT CSampleIME::GetLayout(_Out_ TKBLayoutType *ptkblayoutType, _Out_ WORD *pwPreferredLayoutId) +{ + HRESULT hr = E_INVALIDARG; + if ((ptkblayoutType != nullptr) && (pwPreferredLayoutId != nullptr)) + { + *ptkblayoutType = TKBLT_OPTIMIZED; + *pwPreferredLayoutId = TKBL_OPT_SIMPLIFIED_CHINESE_PINYIN; + hr = S_OK; + } + return hr; +} \ No newline at end of file diff --git a/SampleIME.def b/SampleIME.def new file mode 100644 index 00000000..8342c9cb --- /dev/null +++ b/SampleIME.def @@ -0,0 +1,7 @@ +LIBRARY SampleIME.dll + +EXPORTS + DllGetClassObject PRIVATE + DllCanUnloadNow PRIVATE + DllRegisterServer PRIVATE + DllUnregisterServer PRIVATE diff --git a/SampleIME.h b/SampleIME.h new file mode 100644 index 00000000..b361acd2 --- /dev/null +++ b/SampleIME.h @@ -0,0 +1,248 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#pragma once + +#include "KeyHandlerEditSession.h" +#include "SampleIMEBaseStructure.h" + +class CLangBarItemButton; +class CCandidateListUIPresenter; +class CCompositionProcessorEngine; + +const DWORD WM_CheckGlobalCompartment = WM_USER; +LRESULT CALLBACK CSampleIME_WindowProc(HWND wndHandle, UINT uMsg, WPARAM wParam, LPARAM lParam); + +class CSampleIME : public ITfTextInputProcessorEx, + public ITfThreadMgrEventSink, + public ITfTextEditSink, + public ITfKeyEventSink, + public ITfCompositionSink, + public ITfDisplayAttributeProvider, + public ITfActiveLanguageProfileNotifySink, + public ITfThreadFocusSink, + public ITfFunctionProvider, + public ITfFnGetPreferredTouchKeyboardLayout +{ +public: + CSampleIME(); + ~CSampleIME(); + + // IUnknown + STDMETHODIMP QueryInterface(REFIID riid, _Outptr_ void **ppvObj); + STDMETHODIMP_(ULONG) AddRef(void); + STDMETHODIMP_(ULONG) Release(void); + + // ITfTextInputProcessor + STDMETHODIMP Activate(ITfThreadMgr *pThreadMgr, TfClientId tfClientId) { + return ActivateEx(pThreadMgr, tfClientId, 0); + } + // ITfTextInputProcessorEx + STDMETHODIMP ActivateEx(ITfThreadMgr *pThreadMgr, TfClientId tfClientId, DWORD dwFlags); + STDMETHODIMP Deactivate(); + + // ITfThreadMgrEventSink + STDMETHODIMP OnInitDocumentMgr(_In_ ITfDocumentMgr *pDocMgr); + STDMETHODIMP OnUninitDocumentMgr(_In_ ITfDocumentMgr *pDocMgr); + STDMETHODIMP OnSetFocus(_In_ ITfDocumentMgr *pDocMgrFocus, _In_ ITfDocumentMgr *pDocMgrPrevFocus); + STDMETHODIMP OnPushContext(_In_ ITfContext *pContext); + STDMETHODIMP OnPopContext(_In_ ITfContext *pContext); + + // ITfTextEditSink + STDMETHODIMP OnEndEdit(__RPC__in_opt ITfContext *pContext, TfEditCookie ecReadOnly, __RPC__in_opt ITfEditRecord *pEditRecord); + + // ITfKeyEventSink + STDMETHODIMP OnSetFocus(BOOL fForeground); + STDMETHODIMP OnTestKeyDown(ITfContext *pContext, WPARAM wParam, LPARAM lParam, BOOL *pIsEaten); + STDMETHODIMP OnKeyDown(ITfContext *pContext, WPARAM wParam, LPARAM lParam, BOOL *pIsEaten); + STDMETHODIMP OnTestKeyUp(ITfContext *pContext, WPARAM wParam, LPARAM lParam, BOOL *pIsEaten); + STDMETHODIMP OnKeyUp(ITfContext *pContext, WPARAM wParam, LPARAM lParam, BOOL *pIsEaten); + STDMETHODIMP OnPreservedKey(ITfContext *pContext, REFGUID rguid, BOOL *pIsEaten); + + // ITfCompositionSink + STDMETHODIMP OnCompositionTerminated(TfEditCookie ecWrite, _In_ ITfComposition *pComposition); + + // ITfDisplayAttributeProvider + STDMETHODIMP EnumDisplayAttributeInfo(__RPC__deref_out_opt IEnumTfDisplayAttributeInfo **ppEnum); + STDMETHODIMP GetDisplayAttributeInfo(__RPC__in REFGUID guidInfo, __RPC__deref_out_opt ITfDisplayAttributeInfo **ppInfo); + + // ITfActiveLanguageProfileNotifySink + STDMETHODIMP OnActivated(_In_ REFCLSID clsid, _In_ REFGUID guidProfile, _In_ BOOL isActivated); + + // ITfThreadFocusSink + STDMETHODIMP OnSetThreadFocus(); + STDMETHODIMP OnKillThreadFocus(); + + // ITfFunctionProvider + STDMETHODIMP GetType(__RPC__out GUID *pguid); + STDMETHODIMP GetDescription(__RPC__deref_out_opt BSTR *pbstrDesc); + STDMETHODIMP GetFunction(__RPC__in REFGUID rguid, __RPC__in REFIID riid, __RPC__deref_out_opt IUnknown **ppunk); + + // ITfFunction + STDMETHODIMP GetDisplayName(_Out_ BSTR *pbstrDisplayName); + + // ITfFnGetPreferredTouchKeyboardLayout, it is the Optimized layout feature. + STDMETHODIMP GetLayout(_Out_ TKBLayoutType *ptkblayoutType, _Out_ WORD *pwPreferredLayoutId); + + // CClassFactory factory callback + static HRESULT CreateInstance(_In_ IUnknown *pUnkOuter, REFIID riid, _Outptr_ void **ppvObj); + + // utility function for thread manager. + ITfThreadMgr* _GetThreadMgr() { return _pThreadMgr; } + TfClientId _GetClientId() { return _tfClientId; } + + // functions for the composition object. + void _SetComposition(_In_ ITfComposition *pComposition); + void _TerminateComposition(TfEditCookie ec, _In_ ITfContext *pContext, BOOL isCalledFromDeactivate = FALSE); + void _SaveCompositionContext(_In_ ITfContext *pContext); + + // key event handlers for composition/candidate/phrase common objects. + HRESULT _HandleComplete(TfEditCookie ec, _In_ ITfContext *pContext); + HRESULT _HandleCancel(TfEditCookie ec, _In_ ITfContext *pContext); + + // key event handlers for composition object. + HRESULT _HandleCompositionInput(TfEditCookie ec, _In_ ITfContext *pContext, WCHAR wch); + HRESULT _HandleCompositionFinalize(TfEditCookie ec, _In_ ITfContext *pContext, BOOL fCandidateList); + HRESULT _HandleCompositionConvert(TfEditCookie ec, _In_ ITfContext *pContext, BOOL isWildcardSearch); + HRESULT _HandleCompositionBackspace(TfEditCookie ec, _In_ ITfContext *pContext); + HRESULT _HandleCompositionArrowKey(TfEditCookie ec, _In_ ITfContext *pContext, KEYSTROKE_FUNCTION keyFunction); + HRESULT _HandleCompositionPunctuation(TfEditCookie ec, _In_ ITfContext *pContext, WCHAR wch); + HRESULT _HandleCompositionDoubleSingleByte(TfEditCookie ec, _In_ ITfContext *pContext, WCHAR wch); + + // key event handlers for candidate object. + HRESULT _HandleCandidateFinalize(TfEditCookie ec, _In_ ITfContext *pContext); + HRESULT _HandleCandidateConvert(TfEditCookie ec, _In_ ITfContext *pContext); + HRESULT _HandleCandidateArrowKey(TfEditCookie ec, _In_ ITfContext *pContext, _In_ KEYSTROKE_FUNCTION keyFunction); + HRESULT _HandleCandidateSelectByNumber(TfEditCookie ec, _In_ ITfContext *pContext, _In_ UINT uCode); + + // key event handlers for phrase object. + HRESULT _HandlePhraseFinalize(TfEditCookie ec, _In_ ITfContext *pContext); + HRESULT _HandlePhraseArrowKey(TfEditCookie ec, _In_ ITfContext *pContext, _In_ KEYSTROKE_FUNCTION keyFunction); + HRESULT _HandlePhraseSelectByNumber(TfEditCookie ec, _In_ ITfContext *pContext, _In_ UINT uCode); + + BOOL _IsSecureMode(void) { return (_dwActivateFlags & TF_TMAE_SECUREMODE) ? TRUE : FALSE; } + BOOL _IsComLess(void) { return (_dwActivateFlags & TF_TMAE_COMLESS) ? TRUE : FALSE; } + BOOL _IsStoreAppMode(void) { return (_dwActivateFlags & TF_TMF_IMMERSIVEMODE) ? TRUE : FALSE; }; + + CCompositionProcessorEngine* GetCompositionProcessorEngine() { return (_pCompositionProcessorEngine); }; + + // comless helpers + static HRESULT CSampleIME::CreateInstance(REFCLSID rclsid, REFIID riid, _Outptr_result_maybenull_ LPVOID* ppv, _Out_opt_ HINSTANCE* phInst, BOOL isComLessMode); + static HRESULT CSampleIME::ComLessCreateInstance(REFGUID rclsid, REFIID riid, _Outptr_result_maybenull_ void **ppv, _Out_opt_ HINSTANCE *phInst); + static HRESULT CSampleIME::GetComModuleName(REFGUID rclsid, _Out_writes_(cchPath)WCHAR* wchPath, DWORD cchPath); + +private: + // functions for the composition object. + HRESULT _HandleCompositionInputWorker(_In_ CCompositionProcessorEngine *pCompositionProcessorEngine, TfEditCookie ec, _In_ ITfContext *pContext); + HRESULT _CreateAndStartCandidate(_In_ CCompositionProcessorEngine *pCompositionProcessorEngine, TfEditCookie ec, _In_ ITfContext *pContext); + HRESULT _HandleCandidateWorker(TfEditCookie ec, _In_ ITfContext *pContext); + + void _StartComposition(_In_ ITfContext *pContext); + void _EndComposition(_In_opt_ ITfContext *pContext); + BOOL _IsComposing(); + BOOL _IsKeyboardDisabled(); + + HRESULT _AddComposingAndChar(TfEditCookie ec, _In_ ITfContext *pContext, _In_ CStringRange *pstrAddString); + HRESULT _AddCharAndFinalize(TfEditCookie ec, _In_ ITfContext *pContext, _In_ CStringRange *pstrAddString); + + BOOL _FindComposingRange(TfEditCookie ec, _In_ ITfContext *pContext, _In_ ITfRange *pSelection, _Outptr_result_maybenull_ ITfRange **ppRange); + HRESULT _SetInputString(TfEditCookie ec, _In_ ITfContext *pContext, _Out_opt_ ITfRange *pRange, _In_ CStringRange *pstrAddString, BOOL exist_composing); + HRESULT _InsertAtSelection(TfEditCookie ec, _In_ ITfContext *pContext, _In_ CStringRange *pstrAddString, _Outptr_ ITfRange **ppCompRange); + + HRESULT _RemoveDummyCompositionForComposing(TfEditCookie ec, _In_ ITfComposition *pComposition); + + // Invoke key handler edit session + HRESULT _InvokeKeyHandler(_In_ ITfContext *pContext, UINT code, WCHAR wch, DWORD flags, _KEYSTROKE_STATE keyState); + + // function for the language property + BOOL _SetCompositionLanguage(TfEditCookie ec, _In_ ITfContext *pContext); + + // function for the display attribute + void _ClearCompositionDisplayAttributes(TfEditCookie ec, _In_ ITfContext *pContext); + BOOL _SetCompositionDisplayAttributes(TfEditCookie ec, _In_ ITfContext *pContext, TfGuidAtom gaDisplayAttribute); + BOOL _InitDisplayAttributeGuidAtom(); + + BOOL _InitThreadMgrEventSink(); + void _UninitThreadMgrEventSink(); + + BOOL _InitTextEditSink(_In_ ITfDocumentMgr *pDocMgr); + + void _UpdateLanguageBarOnSetFocus(_In_ ITfDocumentMgr *pDocMgrFocus); + + BOOL _InitKeyEventSink(); + void _UninitKeyEventSink(); + + BOOL _InitActiveLanguageProfileNotifySink(); + void _UninitActiveLanguageProfileNotifySink(); + + BOOL _IsKeyEaten(_In_ ITfContext *pContext, UINT codeIn, _Out_ UINT *pCodeOut, _Out_writes_(1) WCHAR *pwch, _Out_opt_ _KEYSTROKE_STATE *pKeyState); + + BOOL _IsRangeCovered(TfEditCookie ec, _In_ ITfRange *pRangeTest, _In_ ITfRange *pRangeCover); + VOID _DeleteCandidateList(BOOL fForce, _In_opt_ ITfContext *pContext); + + WCHAR ConvertVKey(UINT code); + + BOOL _InitThreadFocusSink(); + void _UninitThreadFocusSink(); + + BOOL _InitFunctionProviderSink(); + void _UninitFunctionProviderSink(); + + BOOL _AddTextProcessorEngine(); + + BOOL VerifySampleIMECLSID(_In_ REFCLSID clsid); + + friend LRESULT CALLBACK CSampleIME_WindowProc(HWND wndHandle, UINT uMsg, WPARAM wParam, LPARAM lParam); + +private: + ITfThreadMgr* _pThreadMgr; + TfClientId _tfClientId; + DWORD _dwActivateFlags; + + // The cookie of ThreadMgrEventSink + DWORD _threadMgrEventSinkCookie; + + ITfContext* _pTextEditSinkContext; + DWORD _textEditSinkCookie; + + // The cookie of ActiveLanguageProfileNotifySink + DWORD _activeLanguageProfileNotifySinkCookie; + + // The cookie of ThreadFocusSink + DWORD _dwThreadFocusSinkCookie; + + // Composition Processor Engine object. + CCompositionProcessorEngine* _pCompositionProcessorEngine; + + // Language bar item object. + CLangBarItemButton* _pLangBarItem; + + // the current composition object. + ITfComposition* _pComposition; + + // guidatom for the display attibute. + TfGuidAtom _gaDisplayAttributeInput; + TfGuidAtom _gaDisplayAttributeConverted; + + CANDIDATE_MODE _candidateMode; + CCandidateListUIPresenter *_pCandidateListUIPresenter; + BOOL _isCandidateWithWildcard : 1; + + ITfDocumentMgr* _pDocMgrLastFocused; + + ITfContext* _pContext; + + ITfCompartment* _pSIPIMEOnOffCompartment; + DWORD _dwSIPIMEOnOffCompartmentSinkCookie; + + HWND _msgWndHandle; + + LONG _refCount; + + // Support the search integration + ITfFnSearchCandidateProvider* _pITfFnSearchCandidateProvider; +}; diff --git a/SampleIME.rc b/SampleIME.rc new file mode 100644 index 00000000..04b15abe --- /dev/null +++ b/SampleIME.rc @@ -0,0 +1,122 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDIS_SAMPLEIME ICON "image\\SampleIme.ico" +IDI_IME_MODE_ON ICON "image\\ImeModeOn.ico" +IDI_IME_MODE_OFF ICON "image\\ImeModeOff.ico" +IDI_DOUBLE_SINGLE_BYTE_ON ICON "image\\DoubleSingleByteOn.ico" +IDI_DOUBLE_SINGLE_BYTE_OFF ICON "image\\DoubleSingleByteOff.ico" +IDI_PUNCTUATION_ON ICON "image\\PunctuationOn.ico" +IDI_PUNCTUATION_OFF ICON "image\\PunctuationOff.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +IDR_VERSION2 VERSIONINFO + FILEVERSION 1,0,0,1 + PRODUCTVERSION 1,0,0,1 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "MSFT" + VALUE "FileDescription", "The Sample code of Windows 8 IME" + VALUE "FileVersion", "1.0.0.1" + VALUE "InternalName", "SampleIM.dll" + VALUE "LegalCopyright", "Copyright (C)" + VALUE "OriginalFilename", "SampleIM.dll" + VALUE "ProductName", "SampleIME" + VALUE "ProductVersion", "1.0.0.1" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDIS_SAMPLEIME "Chinese Simplified QuanPin (version 6.0)" + IDS_DEFAULT_FONT "Microsoft YaHei UI" +END + +STRINGTABLE +BEGIN + IDS_IME_MODE "Chinese/English input (Shift)" + IDS_DOUBLE_SINGLE_BYTE "Double/Single byte (Shift+Space)" + IDS_PUNCTUATION "Chinese/English punctuation (Ctrl+.)" +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/SampleIME.vcxproj b/SampleIME.vcxproj new file mode 100644 index 00000000..cb15b955 --- /dev/null +++ b/SampleIME.vcxproj @@ -0,0 +1,267 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {0C8FE010-5146-44D8-AF99-82702C15FEA5} + Win32Proj + SampleIME + SampleIME + $(VCTargetsPath11) + + + + DynamicLibrary + true + Unicode + v110 + + + DynamicLibrary + true + Unicode + v110 + + + DynamicLibrary + false + true + Unicode + v110 + + + DynamicLibrary + false + true + Unicode + v110 + + + + + + + + + + + + + + + + + + + true + + + false + + + false + + + false + + + + + + Level4 + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;SampleIME_EXPORTS;%(PreprocessorDefinitions) + $(MSBuildProgramFiles32)\Windows Kits\8.0\Include\um;$(MSBuildProgramFiles32)\Windows Kits\8.0\Include\shared;%(AdditionalIncludeDirectories) + Default + true + + + Windows + true + $(MSBuildProgramFiles32)\Windows Kits\8.0\Lib\win8\um\x86;%(AdditionalLibraryDirectories) + %(AdditionalDependencies) + SampleIME.def + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;SampleIME_EXPORTS;%(PreprocessorDefinitions) + $(MSBuildProgramFiles32)\Windows Kits\8.0\Include\um;$(MSBuildProgramFiles32)\Windows Kits\8.0\Include\shared;%(AdditionalIncludeDirectories) + Default + + + Windows + true + $(MSBuildProgramFiles32)\Windows Kits\8.0\Lib\win8\um\x64;%(AdditionalLibraryDirectories) + shlwapi.lib;%(AdditionalDependencies) + SampleIME.def + + + + + Level4 + + + Disabled + true + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;SampleIME_EXPORTS;%(PreprocessorDefinitions) + $(MSBuildProgramFiles32)\Windows Kits\8.0\Include\um;$(MSBuildProgramFiles32)\Windows Kits\8.0\Include\shared;%(AdditionalIncludeDirectories) + false + true + + + Windows + true + false + false + $(MSBuildProgramFiles32)\Windows Kits\8.0\Lib\win8\um\x86;%(AdditionalLibraryDirectories) + shlwapi.lib;%(AdditionalDependencies) + SampleIME.def + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;SampleIME_EXPORTS;%(PreprocessorDefinitions) + $(MSBuildProgramFiles32)\Windows Kits\8.0\Include\um;$(MSBuildProgramFiles32)\Windows Kits\8.0\Include\shared;%(AdditionalIncludeDirectories) + false + + + Windows + true + false + false + $(MSBuildProgramFiles32)\Windows Kits\8.0\Lib\win8\um\x64;%(AdditionalLibraryDirectories) + shlwapi.lib;%(AdditionalDependencies) + SampleIME.def + + + + + + \ No newline at end of file diff --git a/SampleIME.vcxproj.filters b/SampleIME.vcxproj.filters new file mode 100644 index 00000000..8eea86ce --- /dev/null +++ b/SampleIME.vcxproj.filters @@ -0,0 +1,301 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {fe9b9e3e-f048-48b1-ae0b-718fc88cff49} + + + + + Source Files + + + Dictionary + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/SampleIMEBaseStructure.cpp b/SampleIMEBaseStructure.cpp new file mode 100644 index 00000000..e4e989f0 --- /dev/null +++ b/SampleIMEBaseStructure.cpp @@ -0,0 +1,325 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#include "Globals.h" + +//--------------------------------------------------------------------- +// +// CLSIDToString +// +//--------------------------------------------------------------------- + +const BYTE GuidSymbols[] = { + 3, 2, 1, 0, '-', 5, 4, '-', 7, 6, '-', 8, 9, '-', 10, 11, 12, 13, 14, 15 +}; + +const WCHAR HexDigits[] = L"0123456789ABCDEF"; + +BOOL CLSIDToString(REFGUID refGUID, _Out_writes_(39) WCHAR *pCLSIDString) +{ + WCHAR* pTemp = pCLSIDString; + const BYTE* pBytes = (const BYTE *) &refGUID; + + DWORD j = 0; + pTemp[j++] = L'{'; + for (int i = 0; i < sizeof(GuidSymbols) && j < (CLSID_STRLEN - 2); i++) + { + if (GuidSymbols[i] == '-') + { + pTemp[j++] = L'-'; + } + else + { + pTemp[j++] = HexDigits[ (pBytes[GuidSymbols[i]] & 0xF0) >> 4 ]; + pTemp[j++] = HexDigits[ (pBytes[GuidSymbols[i]] & 0x0F) ]; + } + } + + pTemp[j++] = L'}'; + pTemp[j] = L'\0'; + + return TRUE; +} + +//--------------------------------------------------------------------- +// +// SkipWhiteSpace +// +// dwBufLen - in character count +// +//--------------------------------------------------------------------- + +HRESULT SkipWhiteSpace(LCID locale, _In_ LPCWSTR pwszBuffer, DWORD_PTR dwBufLen, _Out_ DWORD_PTR *pdwIndex) +{ + DWORD_PTR index = 0; + + *pdwIndex = 0; + while (*pwszBuffer && IsSpace(locale, *pwszBuffer) && dwBufLen) + { + dwBufLen--; + pwszBuffer++; + index++; + } + + if (*pwszBuffer && dwBufLen) + { + *pdwIndex = index; + return S_OK; + } + + return E_FAIL; +} + +//--------------------------------------------------------------------- +// +// FindChar +// +// dwBufLen - in character count +// +//--------------------------------------------------------------------- + +HRESULT FindChar(WCHAR wch, _In_ LPCWSTR pwszBuffer, DWORD_PTR dwBufLen, _Out_ DWORD_PTR *pdwIndex) +{ + DWORD_PTR index = 0; + + *pdwIndex = 0; + while (*pwszBuffer && (*pwszBuffer != wch) && dwBufLen) + { + dwBufLen--; + pwszBuffer++; + index++; + } + + if (*pwszBuffer && dwBufLen) + { + *pdwIndex = index; + return S_OK; + } + + return E_FAIL; +} + +//--------------------------------------------------------------------- +// +// IsSpace +// +//--------------------------------------------------------------------- + +BOOL IsSpace(LCID locale, WCHAR wch) +{ + WORD wCharType = 0; + + GetStringTypeEx(locale, CT_CTYPE1, &wch, 1, &wCharType); + return (wCharType & C1_SPACE); +} + +CStringRange::CStringRange() +{ + _stringBufLen = 0; + _pStringBuf = nullptr; +} + +CStringRange::~CStringRange() +{ +} + +const WCHAR *CStringRange::Get() const +{ + return _pStringBuf; +} + +const DWORD_PTR CStringRange::GetLength() const +{ + return _stringBufLen; +} + +void CStringRange::Clear() +{ + _stringBufLen = 0; + _pStringBuf = nullptr; +} + +void CStringRange::Set(const WCHAR *pwch, DWORD_PTR dwLength) +{ + _stringBufLen = dwLength; + _pStringBuf = pwch; +} + +void CStringRange::Set(CStringRange &sr) +{ + *this = sr; +} + +CStringRange& CStringRange::operator =(const CStringRange& sr) +{ + _stringBufLen = sr._stringBufLen; + _pStringBuf = sr._pStringBuf; + return *this; +} + +void CStringRange::CharNext(_Inout_ CStringRange* pCharNext) +{ + if (!_stringBufLen) + { + pCharNext->_stringBufLen = 0; + pCharNext->_pStringBuf = nullptr; + } + else + { + pCharNext->_stringBufLen = _stringBufLen; + pCharNext->_pStringBuf = _pStringBuf; + + while (pCharNext->_stringBufLen) + { + BOOL isSurrogate = (IS_HIGH_SURROGATE(*pCharNext->_pStringBuf) || IS_LOW_SURROGATE(*pCharNext->_pStringBuf)); + pCharNext->_stringBufLen--; + pCharNext->_pStringBuf++; + if (!isSurrogate) + { + break; + } + } + } +} + +int CStringRange::Compare(LCID locale, _In_ CStringRange* pString1, _In_ CStringRange* pString2) +{ + return CompareString(locale, + NORM_IGNORECASE, + pString1->Get(), + (DWORD)pString1->GetLength(), + pString2->Get(), + (DWORD)pString2->GetLength()); +} + +BOOL CStringRange::WildcardCompare(LCID locale, _In_ CStringRange* stringWithWildcard, _In_ CStringRange* targetString) +{ + if (stringWithWildcard->GetLength() == 0) + { + return targetString->GetLength() == 0 ? TRUE : FALSE; + } + + CStringRange stringWithWildcard_next; + CStringRange targetString_next; + stringWithWildcard->CharNext(&stringWithWildcard_next); + targetString->CharNext(&targetString_next); + + if (*stringWithWildcard->Get() == L'*') + { + return WildcardCompare(locale, &stringWithWildcard_next, targetString) || ((targetString->GetLength() != 0) && WildcardCompare(locale, stringWithWildcard, &targetString_next)); + } + if (*stringWithWildcard->Get() == L'?') + { + return ((targetString->GetLength() != 0) && WildcardCompare(locale, &stringWithWildcard_next, &targetString_next)); + } + + BOOL isSurrogate1 = (IS_HIGH_SURROGATE(*stringWithWildcard->Get()) || IS_LOW_SURROGATE(*stringWithWildcard->Get())); + BOOL isSurrogate2 = (IS_HIGH_SURROGATE(*targetString->Get()) || IS_LOW_SURROGATE(*targetString->Get())); + + return ((CompareString(locale, + NORM_IGNORECASE, + stringWithWildcard->Get(), + (isSurrogate1 ? 2 : 1), + targetString->Get(), + (isSurrogate2 ? 2 : 1)) == CSTR_EQUAL) && WildcardCompare(locale, &stringWithWildcard_next, &targetString_next)); +} + +CCandidateRange::CCandidateRange(void) +{ +} + + +CCandidateRange::~CCandidateRange(void) +{ +} + + +BOOL CCandidateRange::IsRange(UINT vKey) +{ + DWORD value = vKey - L'0'; + + for (UINT i = 0; i < _CandidateListIndexRange.Count(); i++) + { + if (value == *_CandidateListIndexRange.GetAt(i)) + { + return TRUE; + } + else if ((VK_NUMPAD0 <= vKey) && (vKey <= VK_NUMPAD9)) + { + if ((vKey-VK_NUMPAD0) == *_CandidateListIndexRange.GetAt(i)) + { + return TRUE; + } + } + } + return FALSE; +} + +int CCandidateRange::GetIndex(UINT vKey) +{ + DWORD value = vKey - L'0'; + + for (UINT i = 0; i < _CandidateListIndexRange.Count(); i++) + { + if (value == *_CandidateListIndexRange.GetAt(i)) + { + return i; + } + else if ((VK_NUMPAD0 <= vKey) && (vKey <= VK_NUMPAD9)) + { + if ((vKey-VK_NUMPAD0) == *_CandidateListIndexRange.GetAt(i)) + { + return i; + } + } + } + return -1; +} + +CPunctuationPair::CPunctuationPair() +{ + _punctuation._Code = 0; + _punctuation._Punctuation = 0; + _pairPunctuation = 0; + _isPairToggle = FALSE; +} + +CPunctuationPair::CPunctuationPair(WCHAR code, WCHAR punctuation, WCHAR pair) +{ + _punctuation._Code = code; + _punctuation._Punctuation = punctuation; + _pairPunctuation = pair; + _isPairToggle = FALSE; +} + +CPunctuationNestPair::CPunctuationNestPair() +{ + _punctuation_begin._Code = 0; + _punctuation_begin._Punctuation = 0; + _pairPunctuation_begin = 0; + + _punctuation_end._Code = 0; + _punctuation_end._Punctuation = 0; + _pairPunctuation_end = 0; + + _nestCount = 0; +} + +CPunctuationNestPair::CPunctuationNestPair(WCHAR codeBegin, WCHAR punctuationBegin, WCHAR pairBegin, + WCHAR codeEnd, WCHAR punctuationEnd, WCHAR pairEnd) +{ + pairEnd;punctuationEnd; + _punctuation_begin._Code = codeBegin; + _punctuation_begin._Punctuation = punctuationBegin; + _pairPunctuation_begin = pairBegin; + + _punctuation_end._Code = codeEnd; + _punctuation_end._Punctuation = punctuationBegin; + _pairPunctuation_end = pairBegin; + + _nestCount = 0; +} \ No newline at end of file diff --git a/SampleIMEBaseStructure.h b/SampleIMEBaseStructure.h new file mode 100644 index 00000000..9d76c3d9 --- /dev/null +++ b/SampleIMEBaseStructure.h @@ -0,0 +1,271 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#pragma once + +#include "stdafx.h" +#include +#include "assert.h" +#include + +using std::cout; +using std::endl; + +//--------------------------------------------------------------------- +// defined keyword +//--------------------------------------------------------------------- +template +struct _DEFINED_KEYWORD +{ + LPCWSTR _pwszKeyword; + VALUE _value; +}; + +//--------------------------------------------------------------------- +// enum +//--------------------------------------------------------------------- +enum KEYSTROKE_CATEGORY +{ + CATEGORY_NONE = 0, + CATEGORY_COMPOSING, + CATEGORY_CANDIDATE, + CATEGORY_PHRASE, + CATEGORY_PHRASEFROMKEYSTROKE, + CATEGORY_INVOKE_COMPOSITION_EDIT_SESSION +}; + +enum KEYSTROKE_FUNCTION +{ + FUNCTION_NONE = 0, + FUNCTION_INPUT, + + FUNCTION_CANCEL, + FUNCTION_FINALIZE_TEXTSTORE, + FUNCTION_FINALIZE_TEXTSTORE_AND_INPUT, + FUNCTION_FINALIZE_CANDIDATELIST, + FUNCTION_FINALIZE_CANDIDATELIST_AND_INPUT, + FUNCTION_CONVERT, + FUNCTION_CONVERT_WILDCARD, + FUNCTION_SELECT_BY_NUMBER, + FUNCTION_BACKSPACE, + FUNCTION_MOVE_LEFT, + FUNCTION_MOVE_RIGHT, + FUNCTION_MOVE_UP, + FUNCTION_MOVE_DOWN, + FUNCTION_MOVE_PAGE_UP, + FUNCTION_MOVE_PAGE_DOWN, + FUNCTION_MOVE_PAGE_TOP, + FUNCTION_MOVE_PAGE_BOTTOM, + + // Function Double/Single byte + FUNCTION_DOUBLE_SINGLE_BYTE, + + // Function Punctuation + FUNCTION_PUNCTUATION +}; + +//--------------------------------------------------------------------- +// candidate list +//--------------------------------------------------------------------- +enum CANDIDATE_MODE +{ + CANDIDATE_NONE = 0, + CANDIDATE_ORIGINAL, + CANDIDATE_PHRASE, + CANDIDATE_INCREMENTAL, + CANDIDATE_WITH_NEXT_COMPOSITION +}; + +//--------------------------------------------------------------------- +// structure +//--------------------------------------------------------------------- +struct _KEYSTROKE_STATE +{ + KEYSTROKE_CATEGORY Category; + KEYSTROKE_FUNCTION Function; +}; + +struct _PUNCTUATION +{ + WCHAR _Code; + WCHAR _Punctuation; +}; + +BOOL CLSIDToString(REFGUID refGUID, _Out_writes_ (39) WCHAR *pCLSIDString); + +HRESULT SkipWhiteSpace(LCID locale, _In_ LPCWSTR pwszBuffer, DWORD_PTR dwBufLen, _Out_ DWORD_PTR *pdwIndex); +HRESULT FindChar(WCHAR wch, _In_ LPCWSTR pwszBuffer, DWORD_PTR dwBufLen, _Out_ DWORD_PTR *pdwIndex); + +BOOL IsSpace(LCID locale, WCHAR wch); + +template +class CSampleImeArray +{ + typedef typename std::vector CSampleImeInnerArray; + typedef typename std::vector::iterator CSampleImeInnerIter; + +public: + CSampleImeArray(): _innerVect() + { + } + + explicit CSampleImeArray(size_t count): _innerVect(count) + { + } + + virtual ~CSampleImeArray() + { + } + + inline T* GetAt(size_t index) + { + assert(index >= 0); + assert(index < _innerVect.size()); + + T& curT = _innerVect.at(index); + + return &(curT); + } + + inline const T* GetAt(size_t index) const + { + assert(index >= 0); + assert(index < _innerVect.size()); + + T& curT = _innerVect.at(index); + + return &(curT); + } + + void RemoveAt(size_t index) + { + assert(index >= 0); + assert(index < _innerVect.size()); + + CSampleImeInnerIter iter = _innerVect.begin(); + _innerVect.erase(iter + index); + } + + UINT Count() const + { + return static_cast(_innerVect.size()); + } + + T* Append() + { + T newT; + _innerVect.push_back(newT); + T& backT = _innerVect.back(); + + return &(backT); + } + + void reserve(size_t Count) + { + _innerVect.reserve(Count); + } + + void Clear() + { + _innerVect.clear(); + } + +private: + CSampleImeInnerArray _innerVect; +}; + +class CCandidateRange +{ +public: + CCandidateRange(void); + ~CCandidateRange(void); + + BOOL IsRange(UINT vKey); + int GetIndex(UINT vKey); + + inline int Count() const + { + return _CandidateListIndexRange.Count(); + } + inline DWORD *GetAt(int index) + { + return _CandidateListIndexRange.GetAt(index); + } + inline DWORD *Append() + { + return _CandidateListIndexRange.Append(); + } + +private: + CSampleImeArray _CandidateListIndexRange; +}; + +class CStringRange +{ +public: + CStringRange(); + ~CStringRange(); + + const WCHAR *Get() const; + const DWORD_PTR GetLength() const; + void Clear(); + void Set(const WCHAR *pwch, DWORD_PTR dwLength); + void Set(CStringRange &sr); + CStringRange& operator=(const CStringRange& sr); + void CharNext(_Inout_ CStringRange* pCharNext); + static int Compare(LCID locale, _In_ CStringRange* pString1, _In_ CStringRange* pString2); + static BOOL WildcardCompare(LCID locale, _In_ CStringRange* stringWithWildcard, _In_ CStringRange* targetString); + +protected: + DWORD_PTR _stringBufLen; // Length is in character count. + const WCHAR *_pStringBuf; // Buffer which is not add zero terminate. +}; + +//--------------------------------------------------------------------- +// CCandidateListItem +// _ItemString - candidate string +// _FindKeyCode - tailing string +//--------------------------------------------------------------------- +struct CCandidateListItem +{ + CStringRange _ItemString; + CStringRange _FindKeyCode; + + CCandidateListItem& CCandidateListItem::operator =( const CCandidateListItem& rhs) + { + _ItemString = rhs._ItemString; + _FindKeyCode = rhs._FindKeyCode; + return *this; + } +}; + +class CPunctuationPair +{ +public: + CPunctuationPair(); + CPunctuationPair(WCHAR code, WCHAR punctuation, WCHAR pair); + + struct _PUNCTUATION _punctuation; + WCHAR _pairPunctuation; + BOOL _isPairToggle; +}; + +class CPunctuationNestPair +{ +public: + CPunctuationNestPair(); + CPunctuationNestPair(WCHAR wchCode_begin, WCHAR wch_begin, WCHAR wchPair_begin, + WCHAR wchCode_end, WCHAR wch_end, WCHAR wchPair_end); + + struct _PUNCTUATION _punctuation_begin; + WCHAR _pairPunctuation_begin; + + struct _PUNCTUATION _punctuation_end; + WCHAR _pairPunctuation_end; + + int _nestCount; +}; \ No newline at end of file diff --git a/SampleIMEStructureArray.h b/SampleIMEStructureArray.h new file mode 100644 index 00000000..6b74cd49 --- /dev/null +++ b/SampleIMEStructureArray.h @@ -0,0 +1,80 @@ +////////////////////////////////////////////////////////////////////// +// +// Copyright (C) Microsoft Corporation. All rights reserved. +// +// CSampleIMESampleIMEStructureArray.h +// +// CSampleIMEStructureArray declaration. +// +////////////////////////////////////////////////////////////////////// + +#pragma once + +#include + +template +class CSampleIMEStructureArray +{ + typedef typename std::vector value_type; + typedef const T& CONST_REF; + typedef typename value_type CSampleIMEArray; + typedef typename value_type::iterator CSampleIMEIter; + +public: + CSampleIMEStructureArray(): _imeVector() + { + } + + explicit CSampleIMEStructureArray(size_t iCount): _imeVector(iCount) + { + } + + CSampleIMEStructureArray(size_t iCount, CONST_REF tVal): _imeVector(iCount, tVal) + { + } + + virtual ~CSampleIMEStructureArray() {} + + inline CONST_REF GetAt(size_t iIndex) const + { + assert(iIndex <= _imeVector.size()); + assert(_imeVector.size() > 0); + + return _imeVector[iIndex]; + } + + inline T& GetAt(size_t iIndex) + { + assert(iIndex <= _imeVector.size()); + assert(_imeVector.size() > 0); + + return _imeVector[iIndex]; + } + + void RemoveAt(size_t iIndex, size_t iElements) + { + assert(iIndex <= _imeVector.size()); + assert(_imeVector.size() > 0); + + CSampleIMEIter beginIter = _imeVector.begin() + iIndex; + CSampleIMEIter lastIter = beginIter + iElements - 1; + + _imeVector.erase(beginIter, lastIter); + } + + size_t Count() const { return _imeVector.size(); } + + void Append(const T& tVal) + { + _imeVector.push_back(tVal); + } + + void Clear() + { + _imeVector.clear(); + } + +private: + CSampleIMEArray _imeVector; // the actual array of data + CSampleIMEIter _imeIter; +}; diff --git a/ScrollBarWindow.cpp b/ScrollBarWindow.cpp new file mode 100644 index 00000000..0e63fdd2 --- /dev/null +++ b/ScrollBarWindow.cpp @@ -0,0 +1,650 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#include "Private.h" +#include "Globals.h" +#include "BaseWindow.h" +#include "ScrollBarWindow.h" + +////////////////////////////////////////////////////////////////////// +// +// CScrollBarWindowFactory class. +// +////////////////////////////////////////////////////////////////////// +CScrollBarWindowFactory* CScrollBarWindowFactory::_instance; + +CScrollBarWindowFactory::CScrollBarWindowFactory() +{ + _instance = nullptr; +} + +CScrollBarWindowFactory* CScrollBarWindowFactory::Instance() +{ + if (nullptr == _instance) + { + _instance = new (std::nothrow) CScrollBarWindowFactory(); + } + + return _instance; +} + +CScrollBarWindow* CScrollBarWindowFactory::MakeScrollBarWindow(SHELL_MODE shellMode) +{ + CScrollBarWindow* pScrollBarWindow = nullptr; + + switch (shellMode) + { + case STOREAPP: + pScrollBarWindow = new (std::nothrow) CScrollBarWindow(); + break; + + case DESKTOP: + pScrollBarWindow = new (std::nothrow) CScrollBarNullWindow(); + break; + + default: + pScrollBarWindow = new (std::nothrow) CScrollBarNullWindow(); + break; + } + return pScrollBarWindow; +} + +void CScrollBarWindowFactory::Release() +{ + if (_instance) + { + delete _instance; + _instance = nullptr; + } +} + +////////////////////////////////////////////////////////////////////// +// +// CScrollBarWindow class. +// +////////////////////////////////////////////////////////////////////// + +//+--------------------------------------------------------------------------- +// +// ctor +// +//---------------------------------------------------------------------------- + +CScrollBarWindow::CScrollBarWindow() +{ + _pBtnUp = nullptr; + _pBtnDn = nullptr; + + _scrollDir = SCROLL_NONE_DIR; + + _sizeOfScrollBtn.cx = GetSystemMetrics(SM_CXVSCROLL) * 2; + _sizeOfScrollBtn.cy = GetSystemMetrics(SM_CYVSCROLL) * 2; +} + +//+--------------------------------------------------------------------------- +// +// dtor +// +//---------------------------------------------------------------------------- + +CScrollBarWindow::~CScrollBarWindow() +{ + if (_pBtnUp) + { + delete _pBtnUp; + _pBtnUp = nullptr; + } + if (_pBtnDn) + { + delete _pBtnDn; + _pBtnDn = nullptr; + } +} + +//+--------------------------------------------------------------------------- +// +// _Create +// +//---------------------------------------------------------------------------- + +BOOL CScrollBarWindow::_Create(ATOM atom, DWORD dwExStyle, DWORD dwStyle, CBaseWindow *pParent, int wndWidth, int wndHeight) +{ + if (!CBaseWindow::_Create(atom, dwExStyle, dwStyle, pParent, wndWidth, wndHeight)) + { + return FALSE; + } + + _pBtnUp = new (std::nothrow) CScrollButtonWindow(dwStyle & WS_HSCROLL ? DFCS_SCROLLLEFT : DFCS_SCROLLUP); + if (_pBtnUp == nullptr) + { + return FALSE; + } + + _pBtnUp->_Create(NULL, 0, 0, this); + _pBtnUp->_SetUIWnd(_GetUIWnd()); + + _pBtnDn = new (std::nothrow) CScrollButtonWindow(dwStyle & WS_HSCROLL ? DFCS_SCROLLRIGHT : DFCS_SCROLLDOWN); + if (_pBtnDn == nullptr) + { + delete _pBtnUp; + _pBtnUp = nullptr; + return FALSE; + } + + _pBtnDn->_Create(NULL, 0, 0, this); + _pBtnDn->_SetUIWnd(_GetUIWnd()); + + return TRUE; +} + +//+--------------------------------------------------------------------------- +// +// _WindowProcCallback +// +// Scrollbar window proc. +//---------------------------------------------------------------------------- + +LRESULT CALLBACK CScrollBarWindow::_WindowProcCallback(_In_ HWND wndHandle, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam) +{ + switch (uMsg) + { + case WM_PAINT: + { + HDC dcHandle = nullptr; + PAINTSTRUCT ps; + + dcHandle = BeginPaint(wndHandle, &ps); + + // paint itself at first + _OnPaint(dcHandle, &ps); + + // paint all children + _pBtnUp->_OnPaint(dcHandle, &ps); + _pBtnDn->_OnPaint(dcHandle, &ps); + + EndPaint(wndHandle, &ps); + } + return 0; + } + + return DefWindowProc(wndHandle, uMsg, wParam, lParam); +} + +//+--------------------------------------------------------------------------- +// +// _OnPaint +// +//---------------------------------------------------------------------------- + +void CScrollBarWindow::_OnPaint(_In_ HDC dcHandle, _In_ PAINTSTRUCT *pps) +{ + HBRUSH hBrush = nullptr; + CBaseWindow* pUIWnd = _GetUIWnd(); + + if (pUIWnd != nullptr && pUIWnd->_GetWnd()) + { + hBrush = (HBRUSH)DefWindowProc(pUIWnd->_GetWnd(), WM_CTLCOLORSCROLLBAR, (WPARAM)dcHandle, (LPARAM)pUIWnd->_GetWnd()); + } + + if (hBrush == nullptr) + { + hBrush = GetSysColorBrush(COLOR_SCROLLBAR); + } + + FillRect(dcHandle, &pps->rcPaint, hBrush); +} + +//+--------------------------------------------------------------------------- +// +// _OnLButtonDown +// +//---------------------------------------------------------------------------- + +void CScrollBarWindow::_OnLButtonDown(POINT pt) +{ + _StartCapture(); + + RECT rc = {0, 0, 0, 0}; + + _pBtnUp->_GetClientRect(&rc); + if (PtInRect(&rc, pt)) + { + _pBtnUp->_OnLButtonDown(pt); + } + else + { + _pBtnDn->_GetClientRect(&rc); + if (PtInRect(&rc, pt)) + { + _pBtnDn->_OnLButtonDown(pt); + } + } +} + +//+--------------------------------------------------------------------------- +// +// _OnLButtonUp +// +//---------------------------------------------------------------------------- + +void CScrollBarWindow::_OnLButtonUp(POINT pt) +{ + if (_IsCapture()) + { + CBaseWindow* pUIWnd = _GetTopmostUIWnd(); + if (pUIWnd) + { + CBaseWindow *pCapture = pUIWnd->_GetCaptureObject(); + if (pCapture && pCapture != this) + { + pCapture->_OnLButtonUp(pt); + } + } + } + else + { + RECT rc = {0, 0, 0, 0}; + + _pBtnUp->_GetClientRect(&rc); + if (PtInRect(&rc, pt)) + { + _pBtnUp->_OnLButtonUp(pt); + } + else + { + _pBtnDn->_GetClientRect(&rc); + if (PtInRect(&rc, pt)) + { + _pBtnDn->_OnLButtonUp(pt); + } + } + } + + if (_IsCapture()) + { + _EndCapture(); + } + if (_IsTimer()) + { + _EndTimer(); + } + + _scrollDir = SCROLL_NONE_DIR; + _InvalidateRect(); +} + +//+--------------------------------------------------------------------------- +// +// _OnMouseMove +// +//---------------------------------------------------------------------------- + +void CScrollBarWindow::_OnMouseMove(POINT pt) +{ + RECT rc = {0, 0, 0, 0}; + + _pBtnUp->_GetClientRect(&rc); + if (PtInRect(&rc, pt)) + { + _pBtnUp->_OnMouseMove(pt); + } + else + { + _pBtnDn->_GetClientRect(&rc); + if (PtInRect(&rc, pt)) + { + _pBtnDn->_OnMouseMove(pt); + } + } +} + +//+--------------------------------------------------------------------------- +// +// _OnOwnerWndMoved +// +//---------------------------------------------------------------------------- + +void CScrollBarWindow::_OnOwnerWndMoved(BOOL isResized) +{ + isResized; + + if (IsWindow(_GetWnd()) && IsWindowVisible(_GetWnd())) + { + _AdjustWindowPos(); + } +} + +//+--------------------------------------------------------------------------- +// +// _Resize +// +//---------------------------------------------------------------------------- + +void CScrollBarWindow::_Resize(int x, int y, int cx, int cy) +{ + CBaseWindow::_Resize(x, y, cx, cy); + + RECT rc = {0, 0, 0, 0}; + + _GetBtnUpRect(&rc); + _pBtnUp->_Resize(rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top); + + _GetBtnDnRect(&rc); + _pBtnDn->_Resize(rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top); +} + +//+--------------------------------------------------------------------------- +// +// _Show +// +//---------------------------------------------------------------------------- + +void CScrollBarWindow::_Show(BOOL isShowWnd) +{ + if (_IsWindowVisible() != isShowWnd) + { + CBaseWindow::_Show(isShowWnd); + + _pBtnUp->_Show(isShowWnd); + _pBtnDn->_Show(isShowWnd); + } +} + +//+--------------------------------------------------------------------------- +// +// _Enable +// +//---------------------------------------------------------------------------- + +void CScrollBarWindow::_Enable(BOOL isEnable) +{ + if (_IsEnabled() != isEnable) + { + CBaseWindow::_Enable(isEnable); + + _pBtnUp->_Enable(isEnable); + _pBtnDn->_Enable(isEnable); + } +} + +//+--------------------------------------------------------------------------- +// +// _AdjustWindowPos +// +//---------------------------------------------------------------------------- + +void CScrollBarWindow::_AdjustWindowPos() +{ + if (!IsWindow(_GetWnd())) + { + return; + } + + RECT rc = {0, 0, 0, 0}; + CBaseWindow* pParent = _GetParent(); + if (pParent == nullptr) + { + return; + } + + GetWindowRect(pParent->_GetWnd(), &rc); + SetWindowPos(_GetWnd(), pParent->_GetWnd(), + rc.left, + rc.top, + rc.right - rc.left, + rc.bottom - rc.top, + SWP_NOOWNERZORDER | SWP_NOACTIVATE); +} + +//+--------------------------------------------------------------------------- +// +// _SetScrollInfo +// +//---------------------------------------------------------------------------- + +void CScrollBarWindow::_SetScrollInfo(_In_ CScrollInfo *lpsi) +{ + _scrollInfo = *lpsi; + + BOOL isEnable = (_scrollInfo.nMax > _scrollInfo.nPage); + + _Enable(isEnable); + + _scrollDir = SCROLL_NONE_DIR; + + _SetCurPos(_scrollInfo.nPos, -1); +} + +//+--------------------------------------------------------------------------- +// +// _GetScrollInfo +// +//---------------------------------------------------------------------------- + +void CScrollBarWindow::_GetScrollInfo(_Out_ CScrollInfo *lpsi) +{ + *lpsi = _scrollInfo; +} + +//+--------------------------------------------------------------------------- +// +// _GetBtnUpRect +// +//---------------------------------------------------------------------------- + +BOOL CScrollBarWindow::_GetBtnUpRect(_Out_ RECT *prc) +{ + RECT rc = {0, 0, 0, 0}; + + _GetClientRect(&rc); + + if (prc == nullptr) + { + return FALSE; + } + + prc->left = rc.left; + prc->top = rc.top; + prc->right = rc.right; + prc->bottom = rc.top + min(_sizeOfScrollBtn.cy, (rc.bottom - rc.top)/2); + + return TRUE; +} + +//+--------------------------------------------------------------------------- +// +// _GetBtnDnRect +// +//---------------------------------------------------------------------------- + +BOOL CScrollBarWindow::_GetBtnDnRect(_Out_ RECT *prc) +{ + RECT rc = {0, 0, 0, 0}; + + _GetClientRect(&rc); + + if (prc == nullptr) + { + return FALSE; + } + + prc->left = rc.left; + prc->top = rc.bottom - min(_sizeOfScrollBtn.cy, (rc.bottom - rc.top)/2); + prc->right = rc.right; + prc->bottom = rc.bottom; + + return TRUE; +} + +//+--------------------------------------------------------------------------- +// +// _GetScrollArea +// +//---------------------------------------------------------------------------- + +void CScrollBarWindow::_GetScrollArea(_Out_ RECT *prc) +{ + RECT rcBtnUp = {0, 0, 0, 0}; + RECT rcBtnDn = {0, 0, 0, 0}; + + _GetBtnUpRect(&rcBtnUp); + _GetBtnDnRect(&rcBtnDn); + + RECT rc = {0, 0, 0, 0}; + + _GetClientRect(&rc); + + if (prc == nullptr) + { + return; + } + + prc->left = rc.left; + prc->top = rc.top + (rcBtnUp.bottom - rcBtnUp.top); + prc->right = rc.right; + prc->bottom = rc.bottom - (rcBtnDn.bottom - rcBtnDn.top); +} + +//+--------------------------------------------------------------------------- +// +// _SetCurPos +// +// param: dwSB - SB_xxx for WM_VSCROLL or WM_HSCROLL. +// if -1 is specified, function doesn't send WM_xSCROLL message +//---------------------------------------------------------------------------- + +void CScrollBarWindow::_SetCurPos(int nPos, int dwSB) +{ + int posMax = (_scrollInfo.nMax <= _scrollInfo.nPage) ? 0 : _scrollInfo.nMax - _scrollInfo.nPage; + + nPos = min(nPos, posMax); + nPos = max(nPos, 0); + + _scrollInfo.nPos = nPos; + + if (_IsWindowVisible()) { + _InvalidateRect(); + } + + if ((_IsCapture() && dwSB != -1) || (dwSB == SB_THUMBPOSITION)) + { + _NotifyCommand(WM_VSCROLL, dwSB, nPos); + } +} + +////////////////////////////////////////////////////////////////////// +// +// CScrollButtonWindow class. +// +////////////////////////////////////////////////////////////////////// + +//+--------------------------------------------------------------------------- +// +// _OnPaint +// +//---------------------------------------------------------------------------- + +void CScrollButtonWindow::_OnPaint(_In_ HDC dcHandle, _In_ PAINTSTRUCT *pps) +{ + pps; + + RECT rc = {0, 0, 0, 0}; + + _GetClientRect(&rc); + + DrawFrameControl(dcHandle, &rc, DFC_SCROLL, subTypeOfControl | typeOfControl | (!_IsEnabled() ? DFCS_INACTIVE : 0)); +} + +//+--------------------------------------------------------------------------- +// +// _OnLButtonDown +// +//---------------------------------------------------------------------------- + +void CScrollButtonWindow::_OnLButtonDown(POINT pt) +{ + CButtonWindow::_OnLButtonDown(pt); + + CScrollBarWindow* pParent = (CScrollBarWindow*)_GetParent(); // more secure w/ dynamic_cast + if (pParent == nullptr) + { + return; + } + + switch (subTypeOfControl) + { + case DFCS_SCROLLDOWN: + _NotifyCommand(WM_VSCROLL, SB_LINEDOWN, 0); + pParent->_ShiftLine(+1, FALSE); + break; + case DFCS_SCROLLUP: + _NotifyCommand(WM_VSCROLL, SB_LINEUP, 0); + pParent->_ShiftLine(-1, FALSE); + break; + } + + _StartTimer(_GetScrollDelay()); +} + +//+--------------------------------------------------------------------------- +// +// _OnLButtonUp +// +//---------------------------------------------------------------------------- + +void CScrollButtonWindow::_OnLButtonUp(POINT pt) +{ + CButtonWindow::_OnLButtonUp(pt); + + _InvalidateRect(); + + if (_IsTimer()) + { + _EndTimer(); + } +} + +//+--------------------------------------------------------------------------- +// +// _OnTimer : Speed up page Down/Up while holding Down/Up Button +// +//---------------------------------------------------------------------------- + +void CScrollButtonWindow::_OnTimer() +{ + POINT pt = {0, 0}; + CScrollBarWindow* pParent = (CScrollBarWindow*)_GetParent(); // more secure w/ dynamic_cast + if (pParent == nullptr) + { + return; + } + + // start another faster timer + _StartTimer(_GetScrollSpeed()); + + GetCursorPos(&pt); + MapWindowPoints(HWND_DESKTOP, pParent->_GetWnd(), &pt, 1); + + RECT rc = {0, 0, 0, 0}; + + _GetClientRect(&rc); + + if (PtInRect(&rc, pt)) + { + switch (subTypeOfControl) + { + case DFCS_SCROLLDOWN: + _NotifyCommand(WM_VSCROLL, SB_LINEDOWN, 0); + pParent->_ShiftLine(+1, FALSE); + break; + case DFCS_SCROLLUP: + _NotifyCommand(WM_VSCROLL, SB_LINEUP, 0); + pParent->_ShiftLine(-1, FALSE); + break; + } + } +} \ No newline at end of file diff --git a/ScrollBarWindow.h b/ScrollBarWindow.h new file mode 100644 index 00000000..2bfc1bba --- /dev/null +++ b/ScrollBarWindow.h @@ -0,0 +1,220 @@ +////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2005 Microsoft Corporation. All rights reserved. +// +// ScrollBarWindow.h +// +// CScrollBarWindow declaration. +// +// +////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "BaseWindow.h" +#include "ButtonWindow.h" + +class CScrollButtonWindow; + +enum SCROLLBAR_DIRECTION +{ + SCROLL_NONE_DIR, + SCROLL_PAGEDOWN, // or page left + SCROLL_PAGEUP // or page right +}; + +enum SHELL_MODE +{ + STOREAPP, + DESKTOP, +}; + +struct CScrollInfo +{ + CScrollInfo() + { + nMax = 0; + nPage = 0; + nPos = 0; + } + + int nMax; + int nPage; + int nPos; +}; + +class CScrollBarWindow; + +class CScrollBarWindowFactory +{ +public: + static CScrollBarWindowFactory* Instance(); + CScrollBarWindow* MakeScrollBarWindow(SHELL_MODE shellMode); + void Release(); + +protected: + CScrollBarWindowFactory(); + +private: + static CScrollBarWindowFactory* _instance; + +}; +////////////////////////////////////////////////////////////////////// +// +// CScrollBarWindow declaration. +// +////////////////////////////////////////////////////////////////////// + +class CScrollBarWindow : public CBaseWindow +{ +public: + CScrollBarWindow(); + virtual ~CScrollBarWindow(); + + BOOL _Create(ATOM atom, DWORD dwExStyle, DWORD dwStyle, CBaseWindow *pParent = nullptr, int wndWidth = 0, int wndHeight = 0); + + void _Resize(int x, int y, int cx, int cy); + void _Show(BOOL isShowWnd); + void _Enable(BOOL isEnable); + + LRESULT CALLBACK _WindowProcCallback(_In_ HWND wndHandle, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam); + virtual void _OnPaint(_In_ HDC dcHandle, _In_ PAINTSTRUCT *pps); + virtual void _OnLButtonDown(POINT pt); + virtual void _OnLButtonUp(POINT pt); + virtual void _OnMouseMove(POINT pt); + virtual void _OnTimer() { }; + + virtual void _OnOwnerWndMoved(BOOL isResized); + + virtual void _SetScrollInfo(_In_ CScrollInfo *lpsi); + virtual void _GetScrollInfo(_Out_ CScrollInfo *lpsi); + + virtual BOOL _GetBtnUpRect(_Out_ RECT *prc); + virtual BOOL _GetBtnDnRect(_Out_ RECT *prc); + + virtual void _ShiftLine(int nLine, BOOL isNotify) + { + DWORD dwSB = 0; + + dwSB = (nLine > 0 ? SB_LINEDOWN : + nLine < 0 ? SB_LINEUP : 0); + + _SetCurPos(_scrollInfo.nPos + nLine, isNotify ? dwSB : -1); + } + + virtual void _ShiftPage(int nPage, BOOL isNotify) + { + DWORD dwSB = 0; + + dwSB = (nPage > 0 ? SB_PAGEDOWN : + nPage < 0 ? SB_PAGEUP : 0); + + _SetCurPos(_scrollInfo.nPos + _scrollInfo.nPage * nPage, isNotify ? dwSB : -1); + } + + virtual void _ShiftPosition(int iPos, BOOL isNotify) + { + _SetCurPos(iPos, isNotify ? SB_THUMBPOSITION : -1); + } + + virtual void _GetScrollArea(_Out_ RECT *prc); + virtual void _SetCurPos(int nPos, int dwSB); + +private: + void _AdjustWindowPos(); + + CScrollButtonWindow* _pBtnUp; + CScrollButtonWindow* _pBtnDn; + + CScrollInfo _scrollInfo; + SIZE _sizeOfScrollBtn; + SCROLLBAR_DIRECTION _scrollDir; +}; + + +////////////////////////////////////////////////////////////////////// +// +// CScrollBarNullWindow declaration. +// +////////////////////////////////////////////////////////////////////// + +class CScrollBarNullWindow : public CScrollBarWindow +{ +public: + CScrollBarNullWindow() { }; + virtual ~CScrollBarNullWindow() { }; + + virtual BOOL _Create(ATOM atom, DWORD dwExStyle, DWORD dwStyle, _In_opt_ CBaseWindow *pParent = nullptr, int wndWidth = 0, int wndHeight = 0) + { + atom; dwExStyle; dwStyle; pParent; wndWidth; wndHeight; + return TRUE; + }; + + virtual void _Destroy() { }; + + virtual void _Move(int x, int y) { x;y; }; + virtual void _Resize(int x, int y, int cx, int cy) { x; y; cx; cy; }; + virtual void _Show(BOOL isShowWnd) { isShowWnd; }; + virtual BOOL _IsWindowVisible() { return TRUE; }; + virtual void _Enable(BOOL isEnable) { isEnable; }; + virtual BOOL _IsEnabled() { return TRUE; }; + virtual void _InvalidateRect() { }; + virtual BOOL _GetWindowRect(_Out_ LPRECT lpRect) { lpRect->bottom = 0; lpRect->left = 0; lpRect->right = 0; lpRect->top = 0; return TRUE; }; + virtual BOOL _GetClientRect(_Out_ LPRECT lpRect) { lpRect->bottom = 0; lpRect->left = 0; lpRect->right = 0; lpRect->top = 0; return TRUE; }; + + virtual LRESULT CALLBACK _WindowProcCallback(_In_ HWND wndHandle, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam) + { + wndHandle; uMsg; wParam; lParam; + return 0; + }; + virtual void _OnPaint(_In_ HDC dcHandle, _In_ PAINTSTRUCT *pps) { dcHandle;pps; }; + virtual void _OnLButtonDown(POINT pt) { pt; }; + virtual void _OnLButtonUp(POINT pt) { pt; }; + virtual void _OnMouseMove(POINT pt) { pt; }; + virtual void _OnTimer() { }; + + virtual void _OnOwnerWndMoved(BOOL isResized) { isResized; }; + + virtual void _SetScrollInfo(_In_ CScrollInfo *lpsi) { lpsi; }; + virtual void _GetScrollInfo(_In_ CScrollInfo *lpsi) { lpsi; }; + + virtual BOOL _GetBtnUpRect(_Out_ RECT *prc) { prc->bottom = 0; prc->left = 0; prc->right = 0; prc->top = 0; return TRUE; }; + virtual BOOL _GetBtnDnRect(_Out_ RECT *prc) { prc->bottom = 0; prc->left = 0; prc->right = 0; prc->top = 0; return TRUE; }; + + virtual void _ShiftLine(int nLine, BOOL isNotify) { nLine; isNotify; }; + virtual void _ShiftPage(int nPage, BOOL isNotify) { nPage; isNotify; }; + virtual void _ShiftPosition(int iPos, BOOL isNotify) { iPos; isNotify; }; + virtual void _GetScrollArea(_Out_ RECT *prc) { prc->bottom = 0; prc->left = 0; prc->right = 0; prc->top = 0; }; + virtual void _SetCurPos(int nPos, DWORD dwSB) { nPos; dwSB; }; +}; + +////////////////////////////////////////////////////////////////////// +// +// CScrollButtonWondow declaration. +// +////////////////////////////////////////////////////////////////////// + +class CScrollButtonWindow : public CButtonWindow +{ +public: + CScrollButtonWindow(UINT uSubType) + { + subTypeOfControl = uSubType; + } + virtual ~CScrollButtonWindow() + { + } + + LRESULT CALLBACK _WindowProcCallback(_In_ HWND wndHandle, UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam) + { + wndHandle;uMsg;wParam;lParam; + return 0; + } + virtual void _OnPaint(_In_ HDC dcHandle, _In_ PAINTSTRUCT *pps); + virtual void _OnLButtonDown(POINT pt); + virtual void _OnLButtonUp(POINT pt); + virtual void _OnTimer(); + +private: + UINT subTypeOfControl; // DFCS_SCROLLxxx for DrawFrameControl +}; diff --git a/SearchCandidateProvider.cpp b/SearchCandidateProvider.cpp new file mode 100644 index 00000000..ebd680f9 --- /dev/null +++ b/SearchCandidateProvider.cpp @@ -0,0 +1,210 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#include "private.h" +#include "SearchCandidateProvider.h" +#include +#include "SampleIME.h" +#include "CompositionProcessorEngine.h" +#include "TipCandidateList.h" +#include "TipCandidateString.h" + +/*------------------------------------------------------------------------------ + +create instance of CSearchCandidateProvider + +------------------------------------------------------------------------------*/ +HRESULT CSearchCandidateProvider::CreateInstance(_Outptr_ ITfFnSearchCandidateProvider **ppobj, _In_ ITfTextInputProcessorEx *ptip) +{ + if (ppobj == nullptr) + { + return E_INVALIDARG; + } + *ppobj = nullptr; + + *ppobj = new (std::nothrow) CSearchCandidateProvider(ptip); + if (nullptr == *ppobj) + { + return E_OUTOFMEMORY; + } + + return S_OK; +} + +/*------------------------------------------------------------------------------ + +create instance of CSearchCandidateProvider + +------------------------------------------------------------------------------*/ +HRESULT CSearchCandidateProvider::CreateInstance(REFIID riid, _Outptr_ void **ppvObj, _In_ ITfTextInputProcessorEx *ptip) +{ + if (ppvObj == nullptr) + { + return E_INVALIDARG; + } + *ppvObj = nullptr; + + *ppvObj = new (std::nothrow) CSearchCandidateProvider(ptip); + if (nullptr == *ppvObj) + { + return E_OUTOFMEMORY; + } + + return ((CSearchCandidateProvider*)(*ppvObj))->QueryInterface(riid, ppvObj); +} + +/*------------------------------------------------------------------------------ + +constructor of CSearchCandidateProvider + +------------------------------------------------------------------------------*/ +CSearchCandidateProvider::CSearchCandidateProvider(_In_ ITfTextInputProcessorEx *ptip) +{ + assert(ptip != nullptr); + + _pTip = ptip; + _refCount = 0; +} + +/*------------------------------------------------------------------------------ + +destructor of CSearchCandidateProvider + +------------------------------------------------------------------------------*/ +CSearchCandidateProvider::~CSearchCandidateProvider(void) +{ +} + +/*------------------------------------------------------------------------------ + +query interface +(IUnknown method) + +------------------------------------------------------------------------------*/ +STDMETHODIMP CSearchCandidateProvider::QueryInterface(REFIID riid, _Outptr_ void **ppvObj) +{ + if (ppvObj == nullptr) + { + return E_POINTER; + } + *ppvObj = nullptr; + + if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, __uuidof(ITfFnSearchCandidateProvider))) + { + *ppvObj = (ITfFnSearchCandidateProvider*)this; + } + else if (IsEqualIID(riid, IID_ITfFunction)) + { + *ppvObj = (ITfFunction*)this; + } + + if (*ppvObj == nullptr) + { + return E_NOINTERFACE; + } + + AddRef(); + return S_OK; +} + +/*------------------------------------------------------------------------------ + +increment reference count +(IUnknown method) + +------------------------------------------------------------------------------*/ +STDMETHODIMP_(ULONG) CSearchCandidateProvider::AddRef() +{ + return (ULONG)InterlockedIncrement(&_refCount); +} + +/*------------------------------------------------------------------------------ + +decrement reference count and release object +(IUnknown method) + +------------------------------------------------------------------------------*/ +STDMETHODIMP_(ULONG) CSearchCandidateProvider::Release() +{ + ULONG ref = (ULONG)InterlockedDecrement(&_refCount); + if (0 < ref) + { + return ref; + } + + delete this; + return 0; +} + +STDMETHODIMP CSearchCandidateProvider::GetDisplayName(_Out_ BSTR *pbstrName) +{ + if (pbstrName == nullptr) + { + return E_INVALIDARG; + } + + *pbstrName = SysAllocString(L"SearchCandidateProvider"); + return S_OK; +} + +STDMETHODIMP CSearchCandidateProvider::GetSearchCandidates(BSTR bstrQuery, BSTR bstrApplicationID, _Outptr_result_maybenull_ ITfCandidateList **pplist) +{ + bstrApplicationID;bstrQuery; + HRESULT hr = E_FAIL; + *pplist = nullptr; + + if (nullptr == _pTip) + { + return hr; + } + + CCompositionProcessorEngine* pCompositionProcessorEngine = ((CSampleIME*)_pTip)->GetCompositionProcessorEngine(); + if (nullptr == pCompositionProcessorEngine) + { + return hr; + } + + CSampleImeArray candidateList; + pCompositionProcessorEngine->GetCandidateList(&candidateList, TRUE, FALSE); + + int cCand = min(candidateList.Count(), FAKECANDIDATENUMBER); + if (0 < cCand) + { + hr = CTipCandidateList::CreateInstance(pplist, cCand); + if (FAILED(hr)) + { + return hr; + } + for (int iCand = 0; iCand < cCand; iCand++) + { + ITfCandidateString* pCandStr = nullptr; + CTipCandidateString::CreateInstance(IID_ITfCandidateString, (void**)&pCandStr); + + ((CTipCandidateString*)pCandStr)->SetIndex(iCand); + ((CTipCandidateString*)pCandStr)->SetString(candidateList.GetAt(iCand)->_ItemString.Get(), candidateList.GetAt(iCand)->_ItemString.GetLength()); + + ((CTipCandidateList*)(*pplist))->SetCandidate(&pCandStr); + } + } + hr = S_OK; + + return hr; +} + +/*------------------------------------------------------------------------------ + +set result +(ITfFnSearchCandidateProvider method) + +------------------------------------------------------------------------------*/ +STDMETHODIMP CSearchCandidateProvider::SetResult(BSTR bstrQuery, BSTR bstrApplicationID, BSTR bstrResult) +{ + bstrQuery;bstrApplicationID;bstrResult; + + return E_NOTIMPL; +} + diff --git a/SearchCandidateProvider.h b/SearchCandidateProvider.h new file mode 100644 index 00000000..3c57a514 --- /dev/null +++ b/SearchCandidateProvider.h @@ -0,0 +1,50 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#pragma once + +// +// CSearchCandidateProvider +// +// TfFnSearchCandidateProvider is used for search integration feature, CSearchCandidateProvider +// implements this interface that can be called by search integration, and offer the candidate list +// as search integration suggestion. +// +#define FAKECANDIDATENUMBER (16) +class CSearchCandidateProvider : public ITfFnSearchCandidateProvider +{ +protected: + // constructor/destructor + CSearchCandidateProvider(_In_ ITfTextInputProcessorEx *ptip); + virtual ~CSearchCandidateProvider(); + +public: + // create instance + static HRESULT CreateInstance(_Outptr_ ITfFnSearchCandidateProvider **ppobj, _In_ ITfTextInputProcessorEx *ptip); + static HRESULT CreateInstance(REFIID riid, _Outptr_ void **ppvObj, _In_ ITfTextInputProcessorEx *ptip); + + // IUnknown methods + STDMETHODIMP QueryInterface(REFIID riid, _Outptr_ void **ppvObj); + STDMETHODIMP_(ULONG) AddRef(void); + STDMETHODIMP_(ULONG) Release(void); + + // ITfFunction methods + STDMETHODIMP GetDisplayName(_Out_ BSTR *pbstrName); + + // ITfFnSearchCandidateProvider methods + // + // GetSearchCandidates is responsible for supporting the candidates to caller, search integration + // SetResult is not used + // + STDMETHODIMP GetSearchCandidates(BSTR bstrQuery, BSTR bstrApplicationID, _Outptr_result_maybenull_ ITfCandidateList **pplist); + STDMETHODIMP SetResult(BSTR bstrQuery, BSTR bstrApplicationID, BSTR bstrResult); + +private: + LONG _refCount; + ITfTextInputProcessorEx* _pTip; +}; + diff --git a/Server.cpp b/Server.cpp new file mode 100644 index 00000000..01f990b6 --- /dev/null +++ b/Server.cpp @@ -0,0 +1,285 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#include "Private.h" +#include "Globals.h" +#include "SampleIME.h" + +// from Register.cpp +BOOL RegisterProfiles(); +void UnregisterProfiles(); +BOOL RegisterCategories(); +void UnregisterCategories(); +BOOL RegisterServer(); +void UnregisterServer(); + +void FreeGlobalObjects(void); + +class CClassFactory; +static CClassFactory* classFactoryObjects[1] = { nullptr }; + +//+--------------------------------------------------------------------------- +// +// DllAddRef +// +//---------------------------------------------------------------------------- + +void DllAddRef(void) +{ + InterlockedIncrement(&Global::dllRefCount); +} + +//+--------------------------------------------------------------------------- +// +// DllRelease +// +//---------------------------------------------------------------------------- + +void DllRelease(void) +{ + if (InterlockedDecrement(&Global::dllRefCount) < 0) + { + EnterCriticalSection(&Global::CS); + + if (nullptr != classFactoryObjects[0]) + { + FreeGlobalObjects(); + } + assert(Global::dllRefCount == -1); + + LeaveCriticalSection(&Global::CS); + } +} + +//+--------------------------------------------------------------------------- +// +// CClassFactory declaration with IClassFactory Interface +// +//---------------------------------------------------------------------------- + +class CClassFactory : public IClassFactory +{ +public: + // IUnknown methods + STDMETHODIMP QueryInterface(REFIID riid, _Outptr_ void **ppvObj); + STDMETHODIMP_(ULONG) AddRef(void); + STDMETHODIMP_(ULONG) Release(void); + + // IClassFactory methods + STDMETHODIMP CreateInstance(_In_opt_ IUnknown *pUnkOuter, _In_ REFIID riid, _COM_Outptr_ void **ppvObj); + STDMETHODIMP LockServer(BOOL fLock); + + // Constructor + CClassFactory(REFCLSID rclsid, HRESULT (*pfnCreateInstance)(IUnknown *pUnkOuter, REFIID riid, void **ppvObj)) + : _rclsid(rclsid) + { + _pfnCreateInstance = pfnCreateInstance; + } + +public: + REFCLSID _rclsid; + HRESULT (*_pfnCreateInstance)(IUnknown *pUnkOuter, REFIID riid, _COM_Outptr_ void **ppvObj); +private: + CClassFactory& operator=(const CClassFactory& rhn) {rhn;}; +}; + +//+--------------------------------------------------------------------------- +// +// CClassFactory::QueryInterface +// +//---------------------------------------------------------------------------- + +STDAPI CClassFactory::QueryInterface(REFIID riid, _Outptr_ void **ppvObj) +{ + if (IsEqualIID(riid, IID_IClassFactory) || IsEqualIID(riid, IID_IUnknown)) + { + *ppvObj = this; + DllAddRef(); + return NOERROR; + } + *ppvObj = nullptr; + + return E_NOINTERFACE; +} + +//+--------------------------------------------------------------------------- +// +// CClassFactory::AddRef +// +//---------------------------------------------------------------------------- + +STDAPI_(ULONG) CClassFactory::AddRef() +{ + DllAddRef(); + return (Global::dllRefCount + 1); +} + +//+--------------------------------------------------------------------------- +// +// CClassFactory::Release +// +//---------------------------------------------------------------------------- + +STDAPI_(ULONG) CClassFactory::Release() +{ + DllRelease(); + return (Global::dllRefCount + 1); +} + +//+--------------------------------------------------------------------------- +// +// CClassFactory::CreateInstance +// +//---------------------------------------------------------------------------- + +STDAPI CClassFactory::CreateInstance(_In_opt_ IUnknown *pUnkOuter, _In_ REFIID riid, _COM_Outptr_ void **ppvObj) +{ + return _pfnCreateInstance(pUnkOuter, riid, ppvObj); +} + +//+--------------------------------------------------------------------------- +// +// CClassFactory::LockServer +// +//---------------------------------------------------------------------------- + +STDAPI CClassFactory::LockServer(BOOL fLock) +{ + if (fLock) + { + DllAddRef(); + } + else + { + DllRelease(); + } + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// BuildGlobalObjects +// +//---------------------------------------------------------------------------- + +void BuildGlobalObjects(void) +{ + classFactoryObjects[0] = new (std::nothrow) CClassFactory(Global::SampleIMECLSID, CSampleIME::CreateInstance); +} + +//+--------------------------------------------------------------------------- +// +// FreeGlobalObjects +// +//---------------------------------------------------------------------------- + +void FreeGlobalObjects(void) +{ + for (int i = 0; i < ARRAYSIZE(classFactoryObjects); i++) + { + if (nullptr != classFactoryObjects[i]) + { + delete classFactoryObjects[i]; + classFactoryObjects[i] = nullptr; + } + } + + DeleteObject(Global::defaultlFontHandle); +} + +//+--------------------------------------------------------------------------- +// +// DllGetClassObject +// +//---------------------------------------------------------------------------- +_Check_return_ +STDAPI DllGetClassObject( + _In_ REFCLSID rclsid, + _In_ REFIID riid, + _Outptr_ void** ppv) +{ + if (classFactoryObjects[0] == nullptr) + { + EnterCriticalSection(&Global::CS); + + // need to check ref again after grabbing mutex + if (classFactoryObjects[0] == nullptr) + { + BuildGlobalObjects(); + } + + LeaveCriticalSection(&Global::CS); + } + + if (IsEqualIID(riid, IID_IClassFactory) || + IsEqualIID(riid, IID_IUnknown)) + { + for (int i = 0; i < ARRAYSIZE(classFactoryObjects); i++) + { + if (nullptr != classFactoryObjects[i] && + IsEqualGUID(rclsid, classFactoryObjects[i]->_rclsid)) + { + *ppv = (void *)classFactoryObjects[i]; + DllAddRef(); // class factory holds DLL ref count + return NOERROR; + } + } + } + + *ppv = nullptr; + + return CLASS_E_CLASSNOTAVAILABLE; +} + +//+--------------------------------------------------------------------------- +// +// DllCanUnloadNow +// +//---------------------------------------------------------------------------- + +STDAPI DllCanUnloadNow(void) +{ + if (Global::dllRefCount >= 0) + { + return S_FALSE; + } + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// DllUnregisterServer +// +//---------------------------------------------------------------------------- + +STDAPI DllUnregisterServer(void) +{ + UnregisterProfiles(); + UnregisterCategories(); + UnregisterServer(); + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// DllRegisterServer +// +//---------------------------------------------------------------------------- + +STDAPI DllRegisterServer(void) +{ + if ((!RegisterServer()) || (!RegisterProfiles()) || (!RegisterCategories())) + { + DllUnregisterServer(); + return E_FAIL; + } + return S_OK; +} + diff --git a/ShadowWindow.cpp b/ShadowWindow.cpp new file mode 100644 index 00000000..9c99ee8a --- /dev/null +++ b/ShadowWindow.cpp @@ -0,0 +1,370 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#include "Private.h" +#include "Globals.h" +#include "BaseWindow.h" +#include "ShadowWindow.h" + +#define SHADOW_ALPHANUMBER (5) +#define ALPHA(x) (255 - (x)) + +//// Gray scale values for the shadow grades +#define GS01 255 +#define GS02 254 +#define GS03 253 +#define GS04 252 +#define GS05 250 +#define GS06 246 +#define GS07 245 +#define GS08 242 +#define GS09 241 +#define GS10 227 +#define GS11 217 +#define GS12 213 +#define GS13 212 +#define GS14 199 +#define GS15 180 +#define GS16 172 +#define GS17 171 +#define GS18 155 +#define GS19 144 +#define GS20 142 + +// pre-computed alpha values for the shadow +const BYTE AlphaTable[SHADOW_ALPHANUMBER] = { + ALPHA(GS04), ALPHA(GS09), ALPHA(GS13), ALPHA(GS17), ALPHA(GS20) +}; + +const BYTE CornerShadowAlphaMetric[SHADOW_ALPHANUMBER][SHADOW_ALPHANUMBER] = { + ALPHA(GS08), ALPHA(GS06), ALPHA(GS05), ALPHA(GS03), ALPHA(GS02), + ALPHA(GS11), ALPHA(GS10), ALPHA(GS09), ALPHA(GS05), ALPHA(GS02), + ALPHA(GS15), ALPHA(GS14), ALPHA(GS10), ALPHA(GS07), ALPHA(GS03), + ALPHA(GS18), ALPHA(GS15), ALPHA(GS11), ALPHA(GS08), ALPHA(GS03), + ALPHA(GS19), ALPHA(GS16), ALPHA(GS12), ALPHA(GS09), ALPHA(GS04), +}; + + +//+--------------------------------------------------------------------------- +// +// _Create +// +//---------------------------------------------------------------------------- + +BOOL CShadowWindow::_Create(ATOM atom, DWORD dwExStyle, DWORD dwStyle, _In_opt_ CBaseWindow *pParent, int wndWidth, int wndHeight) +{ + if (!CBaseWindow::_Create(atom, dwExStyle, dwStyle, pParent, wndWidth, wndHeight)) + { + return FALSE; + } + + return _Initialize(); +} + +//+--------------------------------------------------------------------------- +// +// _WindowProcCallback +// +// Shadow window proc. +//---------------------------------------------------------------------------- + +LRESULT CALLBACK CShadowWindow::_WindowProcCallback(_In_ HWND wndHandle, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) + { + case WM_PAINT: + { + HDC dcHandle; + PAINTSTRUCT ps; + + dcHandle = BeginPaint(wndHandle, &ps); + + HBRUSH hBrush = CreateSolidBrush(_color); + FillRect(dcHandle, &ps.rcPaint, hBrush); + DeleteObject(hBrush); + + EndPaint(wndHandle, &ps); + } + return 0; + + case WM_SETTINGCHANGE: + _OnSettingChange(); + break; + } + + return DefWindowProc(wndHandle, uMsg, wParam, lParam); +} + +//+--------------------------------------------------------------------------- +// +// _OnSettingChange +// +//---------------------------------------------------------------------------- + +void CShadowWindow::_OnSettingChange() +{ + _InitSettings(); + + DWORD dwWndStyleEx = GetWindowLong(_GetWnd(), GWL_EXSTYLE); + + if (_isGradient) + { + SetWindowLong(_GetWnd(), GWL_EXSTYLE, (dwWndStyleEx | WS_EX_LAYERED)); + } + else + { + SetWindowLong(_GetWnd(), GWL_EXSTYLE, (dwWndStyleEx & ~WS_EX_LAYERED)); + } + + _AdjustWindowPos(); + _InitShadow(); +} + +//+--------------------------------------------------------------------------- +// +// _OnOwnerWndMoved +// +//---------------------------------------------------------------------------- + +void CShadowWindow::_OnOwnerWndMoved(BOOL isResized) +{ + if (IsWindow(_GetWnd()) && _IsWindowVisible()) + { + _AdjustWindowPos(); + if (isResized) + { + _InitShadow(); + } + } +} + + +//+--------------------------------------------------------------------------- +// +// _Show +// +//---------------------------------------------------------------------------- + +void CShadowWindow::_Show(BOOL isShowWnd) +{ + _OnOwnerWndMoved(TRUE); + CBaseWindow::_Show(isShowWnd); +} + +//+--------------------------------------------------------------------------- +// +// _Initialize +// +//---------------------------------------------------------------------------- + +BOOL CShadowWindow::_Initialize() +{ + _InitSettings(); + + return TRUE; +} + +//+--------------------------------------------------------------------------- +// +// _InitSettings +// +//---------------------------------------------------------------------------- + +void CShadowWindow::_InitSettings() +{ + HDC dcHandle = GetDC(nullptr); + + // device caps + int cBitsPixelScreen = GetDeviceCaps(dcHandle, BITSPIXEL); + + _isGradient = cBitsPixelScreen > 8; + + ReleaseDC(nullptr, dcHandle); + + if (_isGradient) + { + _color = RGB(0, 0, 0); + _sizeShift.cx = SHADOW_ALPHANUMBER; + _sizeShift.cy = SHADOW_ALPHANUMBER; + } + else + { + _color = RGB(128, 128, 128); + _sizeShift.cx = 2; + _sizeShift.cy = 2; + } +} + +//+--------------------------------------------------------------------------- +// +// _AdjustWindowPos +// +//---------------------------------------------------------------------------- + +void CShadowWindow::_AdjustWindowPos() +{ + if (!IsWindow(_GetWnd())) + { + return; + } + + HWND hWndOwner = _pWndOwner->_GetWnd(); + RECT rc = {0, 0, 0, 0}; + + GetWindowRect(hWndOwner, &rc); + SetWindowPos(_GetWnd(), hWndOwner, + rc.left + _sizeShift.cx, + rc.top + _sizeShift.cy, + rc.right - rc.left, + rc.bottom - rc.top, + SWP_NOOWNERZORDER | SWP_NOACTIVATE); +} + +//+--------------------------------------------------------------------------- +// +// _InitShadow +// +//---------------------------------------------------------------------------- + +#define GETRGBALPHA(_x_, _y_) ((RGBALPHA *)pDIBits + (_y_) * size.cx + (_x_)) + +void CShadowWindow::_InitShadow() +{ + typedef struct _RGBAPLHA { + BYTE rgbBlue; + BYTE rgbGreen; + BYTE rgbRed; + BYTE rgbAlpha; + } RGBALPHA; + + HDC dcScreenHandle = nullptr; + HDC dcLayeredHandle = nullptr; + RECT rcWindow = {0, 0, 0, 0}; + SIZE size = {0, 0}; + BITMAPINFO bitmapInfo; + HBITMAP bitmapMemHandle = nullptr; + HBITMAP bitmapOldHandle = nullptr; + void* pDIBits = nullptr; + int i = 0; + int j = 0; + POINT ptSrc = {0, 0}; + POINT ptDst = {0, 0}; + BLENDFUNCTION Blend; + + if (!_isGradient) + { + return; + } + + SetWindowLong(_GetWnd(), GWL_EXSTYLE, (GetWindowLong(_GetWnd(), GWL_EXSTYLE) | WS_EX_LAYERED)); + + _GetWindowRect(&rcWindow); + size.cx = rcWindow.right - rcWindow.left; + size.cy = rcWindow.bottom - rcWindow.top; + + dcScreenHandle = GetDC(nullptr); + if (dcScreenHandle == nullptr) { + return; + } + + dcLayeredHandle = CreateCompatibleDC(dcScreenHandle); + if (dcLayeredHandle == nullptr) { + ReleaseDC(nullptr, dcScreenHandle); + return; + } + + // create bitmap + bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bitmapInfo.bmiHeader.biWidth = size.cx; + bitmapInfo.bmiHeader.biHeight = size.cy; + bitmapInfo.bmiHeader.biPlanes = 1; + bitmapInfo.bmiHeader.biBitCount = 32; + 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; + + bitmapMemHandle = CreateDIBSection(dcScreenHandle, &bitmapInfo, DIB_RGB_COLORS, &pDIBits, nullptr, 0); + if (pDIBits == nullptr || bitmapMemHandle == nullptr) { + ReleaseDC(nullptr, dcScreenHandle); + DeleteDC(dcLayeredHandle); + return; + } + + memset(pDIBits, 0, ((((32 * size.cx) + 31) & ~31) / 8) * size.cy); + + // edges + for (i = 0; i < SHADOW_ALPHANUMBER; i++) { + RGBALPHA *ppxl; + BYTE bAlpha = AlphaTable[i]; + + // bottom + if (i <= (size.cy + 1)/2) { + for (j = SHADOW_ALPHANUMBER; j < size.cx - SHADOW_ALPHANUMBER; j++) { + ppxl = GETRGBALPHA(j, i); + ppxl->rgbAlpha = bAlpha; + } + } + + // right + if (i <= (size.cx + 1)/2) { + for (j = SHADOW_ALPHANUMBER; j < size.cy - SHADOW_ALPHANUMBER; j++) { + ppxl = GETRGBALPHA(size.cx - 1 - i, j); + ppxl->rgbAlpha = bAlpha; + } + } + } + + // corners + for (i = 0; i < SHADOW_ALPHANUMBER; i++) { + for (j = 0; j < SHADOW_ALPHANUMBER; j++) { + RGBALPHA *ppxl; + BYTE bAlpha; + bAlpha = CornerShadowAlphaMetric[i][SHADOW_ALPHANUMBER - j - 1]; + + // top-right + if ((i <= (size.cy + 1)/2) && (j <= (size.cx + 1)/2)) { + ppxl = GETRGBALPHA(size.cx - 1 - j, size.cy - 1 - i); + ppxl->rgbAlpha = bAlpha; + } + + // bottom-left + if ((i <= (size.cy + 1)/2) && (j <= (size.cx + 1)/2)) { + ppxl = GETRGBALPHA(j, i + 1); + ppxl->rgbAlpha = bAlpha; + } + + // bottom-right + if ((i <= (size.cy + 1)/2) && (j <= (size.cx + 1)/2)) { + ppxl = GETRGBALPHA(size.cx - 1 - j, i + 1); + ppxl->rgbAlpha = bAlpha; + } + } + } + + ptSrc.x = 0; + ptSrc.y = 0; + ptDst.x = rcWindow.left; + ptDst.y = rcWindow.top; + Blend.BlendOp = AC_SRC_OVER; + Blend.BlendFlags = 0; + Blend.SourceConstantAlpha = 255; + Blend.AlphaFormat = AC_SRC_ALPHA; + + bitmapOldHandle = (HBITMAP)SelectObject(dcLayeredHandle, bitmapMemHandle); + + UpdateLayeredWindow(_GetWnd(), dcScreenHandle, nullptr, &size, dcLayeredHandle, &ptSrc, 0, &Blend, ULW_ALPHA); + + SelectObject(dcLayeredHandle, bitmapOldHandle); + + // done + ReleaseDC(nullptr, dcScreenHandle); + DeleteDC(dcLayeredHandle); + DeleteObject(bitmapMemHandle); +} diff --git a/ShadowWindow.h b/ShadowWindow.h new file mode 100644 index 00000000..ff7d7142 --- /dev/null +++ b/ShadowWindow.h @@ -0,0 +1,47 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#pragma once + +#include "BaseWindow.h" + +class CShadowWindow : public CBaseWindow +{ +public: + CShadowWindow(_In_ CBaseWindow *pWndOwner) + { + _pWndOwner = pWndOwner; + _color = RGB(0, 0, 0); + _sizeShift.cx = 0; + _sizeShift.cy = 0; + _isGradient = FALSE; + } + virtual ~CShadowWindow() + { + } + + BOOL _Create(ATOM atom, DWORD dwExStyle, DWORD dwStyle, _In_opt_ CBaseWindow *pParent = nullptr, int wndWidth = 0, int wndHeight = 0); + + void _Show(BOOL isShowWnd); + + LRESULT CALLBACK _WindowProcCallback(_In_ HWND wndHandle, UINT uMsg, WPARAM wParam, LPARAM lParam); + + void _OnSettingChange(); + void _OnOwnerWndMoved(BOOL isResized); + +private: + BOOL _Initialize(); + void _InitSettings(); + void _AdjustWindowPos(); + void _InitShadow(); + +private: + CBaseWindow* _pWndOwner; + COLORREF _color; + SIZE _sizeShift; + BOOL _isGradient; +}; diff --git a/StartComposition.cpp b/StartComposition.cpp new file mode 100644 index 00000000..99e23820 --- /dev/null +++ b/StartComposition.cpp @@ -0,0 +1,133 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#include "Private.h" +#include "Globals.h" +#include "EditSession.h" +#include "SampleIME.h" + +//+--------------------------------------------------------------------------- +// +// CStartCompositinoEditSession +// +//---------------------------------------------------------------------------- + +class CStartCompositionEditSession : public CEditSessionBase +{ +public: + CStartCompositionEditSession(_In_ CSampleIME *pTextService, _In_ ITfContext *pContext) : CEditSessionBase(pTextService, pContext) + { + } + + // ITfEditSession + STDMETHODIMP DoEditSession(TfEditCookie ec); +}; + +//+--------------------------------------------------------------------------- +// +// ITfEditSession::DoEditSession +// +//---------------------------------------------------------------------------- + +STDAPI CStartCompositionEditSession::DoEditSession(TfEditCookie ec) +{ + ITfInsertAtSelection* pInsertAtSelection = nullptr; + ITfRange* pRangeInsert = nullptr; + ITfContextComposition* pContextComposition = nullptr; + ITfComposition* pComposition = nullptr; + + if (FAILED(_pContext->QueryInterface(IID_ITfInsertAtSelection, (void **)&pInsertAtSelection))) + { + goto Exit; + } + + if (FAILED(pInsertAtSelection->InsertTextAtSelection(ec, TF_IAS_QUERYONLY, NULL, 0, &pRangeInsert))) + { + goto Exit; + } + + if (FAILED(_pContext->QueryInterface(IID_ITfContextComposition, (void **)&pContextComposition))) + { + goto Exit; + } + + if (SUCCEEDED(pContextComposition->StartComposition(ec, pRangeInsert, _pTextService, &pComposition)) && (nullptr != pComposition)) + { + _pTextService->_SetComposition(pComposition); + + // set selection to the adjusted range + TF_SELECTION tfSelection; + tfSelection.range = pRangeInsert; + tfSelection.style.ase = TF_AE_NONE; + tfSelection.style.fInterimChar = FALSE; + + _pContext->SetSelection(ec, 1, &tfSelection); + _pTextService->_SaveCompositionContext(_pContext); + } + +Exit: + if (nullptr != pContextComposition) + { + pContextComposition->Release(); + } + + if (nullptr != pRangeInsert) + { + pRangeInsert->Release(); + } + + if (nullptr != pInsertAtSelection) + { + pInsertAtSelection->Release(); + } + + return S_OK; +} + +////////////////////////////////////////////////////////////////////// +// +// CSampleIME class +// +////////////////////////////////////////////////////////////////////// + +//+--------------------------------------------------------------------------- +// +// _StartComposition +// +// this starts the new (std::nothrow) composition at the selection of the current +// focus context. +//---------------------------------------------------------------------------- + +void CSampleIME::_StartComposition(_In_ ITfContext *pContext) +{ + CStartCompositionEditSession* pStartCompositionEditSession = new (std::nothrow) CStartCompositionEditSession(this, pContext); + + if (nullptr != pStartCompositionEditSession) + { + HRESULT hr = S_OK; + pContext->RequestEditSession(_tfClientId, pStartCompositionEditSession, TF_ES_SYNC | TF_ES_READWRITE, &hr); + + pStartCompositionEditSession->Release(); + } +} + +//+--------------------------------------------------------------------------- +// +// _SaveCompositionContext +// +// this saves the context _pComposition belongs to; we need this to clear +// text attribute in case composition has not been terminated on +// deactivation +//---------------------------------------------------------------------------- + +void CSampleIME::_SaveCompositionContext(_In_ ITfContext *pContext) +{ + assert(_pContext == nullptr); + + pContext->AddRef(); + _pContext = pContext; +} diff --git a/TableDictionaryEngine.cpp b/TableDictionaryEngine.cpp new file mode 100644 index 00000000..0a8922cc --- /dev/null +++ b/TableDictionaryEngine.cpp @@ -0,0 +1,120 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#include "Private.h" +#include "TableDictionaryEngine.h" +#include "DictionarySearch.h" + +//+--------------------------------------------------------------------------- +// +// CollectWord +// +//---------------------------------------------------------------------------- + +VOID CTableDictionaryEngine::CollectWord(_In_ CStringRange *pKeyCode, _Inout_ CSampleImeArray *pWordStrings) +{ + CDictionaryResult* pdret = nullptr; + CDictionarySearch dshSearch(_locale, _pDictionaryFile, pKeyCode); + + while (dshSearch.FindPhrase(&pdret)) + { + for (UINT index = 0; index < pdret->_FindPhraseList.Count(); index++) + { + CStringRange* pPhrase = nullptr; + pPhrase = pWordStrings->Append(); + if (pPhrase) + { + *pPhrase = *pdret->_FindPhraseList.GetAt(index); + } + } + + delete pdret; + pdret = nullptr; + } +} + +VOID CTableDictionaryEngine::CollectWord(_In_ CStringRange *pKeyCode, _Inout_ CSampleImeArray *pItemList) +{ + CDictionaryResult* pdret = nullptr; + CDictionarySearch dshSearch(_locale, _pDictionaryFile, pKeyCode); + + while (dshSearch.FindPhrase(&pdret)) + { + for (UINT iIndex = 0; iIndex < pdret->_FindPhraseList.Count(); iIndex++) + { + CCandidateListItem* pLI = nullptr; + pLI = pItemList->Append(); + if (pLI) + { + pLI->_ItemString.Set(*pdret->_FindPhraseList.GetAt(iIndex)); + pLI->_FindKeyCode.Set(pdret->_FindKeyCode.Get(), pdret->_FindKeyCode.GetLength()); + } + } + + delete pdret; + pdret = nullptr; + } +} + +//+--------------------------------------------------------------------------- +// +// CollectWordForWildcard +// +//---------------------------------------------------------------------------- + +VOID CTableDictionaryEngine::CollectWordForWildcard(_In_ CStringRange *pKeyCode, _Inout_ CSampleImeArray *pItemList) +{ + CDictionaryResult* pdret = nullptr; + CDictionarySearch dshSearch(_locale, _pDictionaryFile, pKeyCode); + + while (dshSearch.FindPhraseForWildcard(&pdret)) + { + for (UINT iIndex = 0; iIndex < pdret->_FindPhraseList.Count(); iIndex++) + { + CCandidateListItem* pLI = nullptr; + pLI = pItemList->Append(); + if (pLI) + { + pLI->_ItemString.Set(*pdret->_FindPhraseList.GetAt(iIndex)); + pLI->_FindKeyCode.Set(pdret->_FindKeyCode.Get(), pdret->_FindKeyCode.GetLength()); + } + } + + delete pdret; + pdret = nullptr; + } +} + +//+--------------------------------------------------------------------------- +// +// CollectWordFromConvertedStringForWildcard +// +//---------------------------------------------------------------------------- + +VOID CTableDictionaryEngine::CollectWordFromConvertedStringForWildcard(_In_ CStringRange *pString, _Inout_ CSampleImeArray *pItemList) +{ + CDictionaryResult* pdret = nullptr; + CDictionarySearch dshSearch(_locale, _pDictionaryFile, pString); + + while (dshSearch.FindConvertedStringForWildcard(&pdret)) // TAIL ALL CHAR MATCH + { + for (UINT index = 0; index < pdret->_FindPhraseList.Count(); index++) + { + CCandidateListItem* pLI = nullptr; + pLI = pItemList->Append(); + if (pLI) + { + pLI->_ItemString.Set(*pdret->_FindPhraseList.GetAt(index)); + pLI->_FindKeyCode.Set(pdret->_FindKeyCode.Get(), pdret->_FindKeyCode.GetLength()); + } + } + + delete pdret; + pdret = nullptr; + } +} + diff --git a/TableDictionaryEngine.h b/TableDictionaryEngine.h new file mode 100644 index 00000000..bea5efd2 --- /dev/null +++ b/TableDictionaryEngine.h @@ -0,0 +1,30 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#pragma once + +#include "BaseDictionaryEngine.h" + +class CTableDictionaryEngine : public CBaseDictionaryEngine +{ +public: + CTableDictionaryEngine(LCID locale, _In_ CFile *pDictionaryFile) : CBaseDictionaryEngine(locale, pDictionaryFile) { } + virtual ~CTableDictionaryEngine() { } + + // Collect word from phrase string. + // param + // [in] psrgKeyCode - Specified key code pointer + // [out] pasrgWordString - Specified returns pointer of word as CStringRange. + // returns + // none. + VOID CollectWord(_In_ CStringRange *pKeyCode, _Inout_ CSampleImeArray *pWordStrings); + VOID CollectWord(_In_ CStringRange *pKeyCode, _Inout_ CSampleImeArray *pItemList); + + VOID CollectWordForWildcard(_In_ CStringRange *psrgKeyCode, _Inout_ CSampleImeArray *pItemList); + + VOID CollectWordFromConvertedStringForWildcard(_In_ CStringRange *pString, _Inout_ CSampleImeArray *pItemList); +}; diff --git a/TextEditSink.cpp b/TextEditSink.cpp new file mode 100644 index 00000000..b8e5f075 --- /dev/null +++ b/TextEditSink.cpp @@ -0,0 +1,134 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#include "Private.h" +#include "globals.h" +#include "SampleIME.h" + +//+--------------------------------------------------------------------------- +// +// ITfTextEditSink::OnEndEdit +// +// Called by the system whenever anyone releases a write-access document lock. +//---------------------------------------------------------------------------- + +STDAPI CSampleIME::OnEndEdit(__RPC__in_opt ITfContext *pContext, TfEditCookie ecReadOnly, __RPC__in_opt ITfEditRecord *pEditRecord) +{ + BOOL isSelectionChanged; + + // + // did the selection change? + // The selection change includes the movement of caret as well. + // The caret position is represent as the empty selection range when + // there is no selection. + // + if (pEditRecord == nullptr) + { + return E_INVALIDARG; + } + if (SUCCEEDED(pEditRecord->GetSelectionStatus(&isSelectionChanged)) && + isSelectionChanged) + { + // If the selection is moved to out side of the current composition, + // we terminate the composition. This TextService supports only one + // composition in one context object. + if (_IsComposing()) + { + TF_SELECTION tfSelection; + ULONG fetched = 0; + + if (pContext == nullptr) + { + return E_INVALIDARG; + } + if (FAILED(pContext->GetSelection(ecReadOnly, TF_DEFAULT_SELECTION, 1, &tfSelection, &fetched)) || fetched != 1) + { + return S_FALSE; + } + + ITfRange* pRangeComposition = nullptr; + if (SUCCEEDED(_pComposition->GetRange(&pRangeComposition))) + { + if (!_IsRangeCovered(ecReadOnly, tfSelection.range, pRangeComposition)) + { + _EndComposition(pContext); + } + + pRangeComposition->Release(); + } + + tfSelection.range->Release(); + } + } + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// _InitTextEditSink +// +// Init a text edit sink on the topmost context of the document. +// Always release any previous sink. +//---------------------------------------------------------------------------- + +BOOL CSampleIME::_InitTextEditSink(_In_ ITfDocumentMgr *pDocMgr) +{ + ITfSource* pSource = nullptr; + BOOL ret = TRUE; + + // clear out any previous sink first + if (_textEditSinkCookie != TF_INVALID_COOKIE) + { + if (SUCCEEDED(_pTextEditSinkContext->QueryInterface(IID_ITfSource, (void **)&pSource))) + { + pSource->UnadviseSink(_textEditSinkCookie); + pSource->Release(); + } + + _pTextEditSinkContext->Release(); + _pTextEditSinkContext = nullptr; + _textEditSinkCookie = TF_INVALID_COOKIE; + } + + if (pDocMgr == nullptr) + { + return TRUE; // caller just wanted to clear the previous sink + } + + if (FAILED(pDocMgr->GetTop(&_pTextEditSinkContext))) + { + return FALSE; + } + + if (_pTextEditSinkContext == nullptr) + { + return TRUE; // empty document, no sink possible + } + + ret = FALSE; + if (SUCCEEDED(_pTextEditSinkContext->QueryInterface(IID_ITfSource, (void **)&pSource))) + { + if (SUCCEEDED(pSource->AdviseSink(IID_ITfTextEditSink, (ITfTextEditSink *)this, &_textEditSinkCookie))) + { + ret = TRUE; + } + else + { + _textEditSinkCookie = TF_INVALID_COOKIE; + } + pSource->Release(); + } + + if (ret == FALSE) + { + _pTextEditSinkContext->Release(); + _pTextEditSinkContext = nullptr; + } + + return ret; +} diff --git a/TfInputProcessorProfile.cpp b/TfInputProcessorProfile.cpp new file mode 100644 index 00000000..cad5506a --- /dev/null +++ b/TfInputProcessorProfile.cpp @@ -0,0 +1,54 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#include "Private.h" +#include "TfInputProcessorProfile.h" + +CTfInputProcessorProfile::CTfInputProcessorProfile() +{ + _pInputProcessorProfile = nullptr; +} + +CTfInputProcessorProfile::~CTfInputProcessorProfile() +{ + if (_pInputProcessorProfile) { + _pInputProcessorProfile->Release(); + _pInputProcessorProfile = nullptr; + } +} + +HRESULT CTfInputProcessorProfile::CreateInstance() +{ + HRESULT hr = CoCreateInstance(CLSID_TF_InputProcessorProfiles, nullptr, CLSCTX_INPROC_SERVER, + IID_ITfInputProcessorProfiles, (void**)&_pInputProcessorProfile); + + return hr; +} + +HRESULT CTfInputProcessorProfile::GetCurrentLanguage(_Out_ LANGID *plangid) +{ + if (_pInputProcessorProfile) + { + return _pInputProcessorProfile->GetCurrentLanguage(plangid); + } + else + { + return E_FAIL; + } +} + +HRESULT CTfInputProcessorProfile::GetDefaultLanguageProfile(LANGID langid, REFGUID catid, _Out_ CLSID *pclsid, _Out_ GUID *pguidProfile) +{ + if (_pInputProcessorProfile) + { + return _pInputProcessorProfile->GetDefaultLanguageProfile(langid, catid, pclsid, pguidProfile); + } + else + { + return E_FAIL; + } +} diff --git a/TfInputProcessorProfile.h b/TfInputProcessorProfile.h new file mode 100644 index 00000000..212b5f12 --- /dev/null +++ b/TfInputProcessorProfile.h @@ -0,0 +1,22 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#pragma once + +class CTfInputProcessorProfile +{ +public: + CTfInputProcessorProfile(); + ~CTfInputProcessorProfile(); + + HRESULT CreateInstance(); + HRESULT GetCurrentLanguage(_Out_ LANGID *plangid); + HRESULT GetDefaultLanguageProfile(LANGID langid, REFGUID catid, _Out_ CLSID *pclsid, _Out_ GUID *pguidProfile); + +private: + ITfInputProcessorProfiles* _pInputProcessorProfile; +}; diff --git a/TfTextLayoutSink.cpp b/TfTextLayoutSink.cpp new file mode 100644 index 00000000..0ca396df --- /dev/null +++ b/TfTextLayoutSink.cpp @@ -0,0 +1,220 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#include "Private.h" +#include "TfTextLayoutSink.h" +#include "SampleIME.h" +#include "GetTextExtentEditSession.h" + +CTfTextLayoutSink::CTfTextLayoutSink(_In_ CSampleIME *pTextService) +{ + _pTextService = pTextService; + _pTextService->AddRef(); + + _pRangeComposition = nullptr; + _pContextDocument = nullptr; + _tfEditCookie = TF_INVALID_EDIT_COOKIE; + + _dwCookieTextLayoutSink = TF_INVALID_COOKIE; + + _refCount = 1; + + DllAddRef(); +} + +CTfTextLayoutSink::~CTfTextLayoutSink() +{ + if (_pTextService) + { + _pTextService->Release(); + } + + DllRelease(); +} + +STDAPI CTfTextLayoutSink::QueryInterface(REFIID riid, _Outptr_ void **ppvObj) +{ + if (ppvObj == nullptr) + { + return E_INVALIDARG; + } + + *ppvObj = nullptr; + + if (IsEqualIID(riid, IID_IUnknown) || + IsEqualIID(riid, IID_ITfTextLayoutSink)) + { + *ppvObj = (ITfTextLayoutSink *)this; + } + + if (*ppvObj) + { + AddRef(); + return S_OK; + } + + return E_NOINTERFACE; +} + +STDAPI_(ULONG) CTfTextLayoutSink::AddRef() +{ + return ++_refCount; +} + +STDAPI_(ULONG) CTfTextLayoutSink::Release() +{ + LONG cr = --_refCount; + + assert(_refCount >= 0); + + if (_refCount == 0) + { + delete this; + } + + return cr; +} + +//+--------------------------------------------------------------------------- +// +// ITfTextLayoutSink::OnLayoutChange +// +//---------------------------------------------------------------------------- + +STDAPI CTfTextLayoutSink::OnLayoutChange(_In_ ITfContext *pContext, TfLayoutCode lcode, _In_ ITfContextView *pContextView) +{ + // we're interested in only document context. + if (pContext != _pContextDocument) + { + return S_OK; + } + + switch (lcode) + { + case TF_LC_CHANGE: + { + CGetTextExtentEditSession* pEditSession = nullptr; + pEditSession = new (std::nothrow) CGetTextExtentEditSession(_pTextService, pContext, pContextView, _pRangeComposition, this); + if (nullptr != (pEditSession)) + { + HRESULT hr = S_OK; + pContext->RequestEditSession(_pTextService->_GetClientId(), pEditSession, TF_ES_SYNC | TF_ES_READ, &hr); + + pEditSession->Release(); + } + } + break; + + case TF_LC_DESTROY: + _LayoutDestroyNotification(); + break; + + } + return S_OK; +} + +HRESULT CTfTextLayoutSink::_StartLayout(_In_ ITfContext *pContextDocument, TfEditCookie ec, _In_ ITfRange *pRangeComposition) +{ + _pContextDocument = pContextDocument; + _pContextDocument->AddRef(); + + _pRangeComposition = pRangeComposition; + _pRangeComposition->AddRef(); + + _tfEditCookie = ec; + + return _AdviseTextLayoutSink(); +} + +VOID CTfTextLayoutSink::_EndLayout() +{ + if (_pRangeComposition) + { + _pRangeComposition->Release(); + _pRangeComposition = nullptr; + } + + if (_pContextDocument) + { + _UnadviseTextLayoutSink(); + _pContextDocument->Release(); + _pContextDocument = nullptr; + } +} + +HRESULT CTfTextLayoutSink::_AdviseTextLayoutSink() +{ + HRESULT hr = S_OK; + ITfSource* pSource = nullptr; + + hr = _pContextDocument->QueryInterface(IID_ITfSource, (void **)&pSource); + if (FAILED(hr)) + { + return hr; + } + + hr = pSource->AdviseSink(IID_ITfTextLayoutSink, (ITfTextLayoutSink *)this, &_dwCookieTextLayoutSink); + if (FAILED(hr)) + { + pSource->Release(); + return hr; + } + + pSource->Release(); + + return hr; +} + +HRESULT CTfTextLayoutSink::_UnadviseTextLayoutSink() +{ + HRESULT hr = S_OK; + ITfSource* pSource = nullptr; + + if (nullptr == _pContextDocument) + { + return E_FAIL; + } + + hr = _pContextDocument->QueryInterface(IID_ITfSource, (void **)&pSource); + if (FAILED(hr)) + { + return hr; + } + + hr = pSource->UnadviseSink(_dwCookieTextLayoutSink); + if (FAILED(hr)) + { + pSource->Release(); + return hr; + } + + pSource->Release(); + + return hr; +} + +HRESULT CTfTextLayoutSink::_GetTextExt(_Out_ RECT *lpRect) +{ + HRESULT hr = S_OK; + BOOL isClipped = TRUE; + ITfContextView* pContextView = nullptr; + + hr = _pContextDocument->GetActiveView(&pContextView); + if (FAILED(hr)) + { + return hr; + } + + if (FAILED(hr = pContextView->GetTextExt(_tfEditCookie, _pRangeComposition, lpRect, &isClipped))) + { + return hr; + } + + pContextView->Release(); + + return S_OK; +} diff --git a/TfTextLayoutSink.h b/TfTextLayoutSink.h new file mode 100644 index 00000000..a1f6b0fe --- /dev/null +++ b/TfTextLayoutSink.h @@ -0,0 +1,46 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#pragma once + +class CSampleIME; + +class CTfTextLayoutSink : public ITfTextLayoutSink +{ +public: + CTfTextLayoutSink(_In_ CSampleIME *pTextService); + virtual ~CTfTextLayoutSink(); + + // IUnknown methods + STDMETHODIMP QueryInterface(REFIID riid, _Outptr_ void **ppvObj); + STDMETHODIMP_(ULONG) AddRef(void); + STDMETHODIMP_(ULONG) Release(void); + + // ITfTextLayoutSink + STDMETHODIMP OnLayoutChange(_In_ ITfContext *pContext, TfLayoutCode lcode, _In_ ITfContextView *pContextView); + + HRESULT _StartLayout(_In_ ITfContext *pContextDocument, TfEditCookie ec, _In_ ITfRange *pRangeComposition); + VOID _EndLayout(); + + HRESULT _GetTextExt(_Out_ RECT *lpRect); + ITfContext* _GetContextDocument() { return _pContextDocument; }; + + virtual VOID _LayoutChangeNotification(_In_ RECT *lpRect) = 0; + virtual VOID _LayoutDestroyNotification() = 0; + +private: + HRESULT _AdviseTextLayoutSink(); + HRESULT _UnadviseTextLayoutSink(); + +private: + ITfRange* _pRangeComposition; + ITfContext* _pContextDocument; + TfEditCookie _tfEditCookie; + CSampleIME* _pTextService; + DWORD _dwCookieTextLayoutSink; + LONG _refCount; +}; diff --git a/ThreadFocusSink.cpp b/ThreadFocusSink.cpp new file mode 100644 index 00000000..ca206649 --- /dev/null +++ b/ThreadFocusSink.cpp @@ -0,0 +1,106 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#include "Private.h" +#include "SampleIME.h" +#include "CandidateListUIPresenter.h" + +//+--------------------------------------------------------------------------- +// +// ITfTextLayoutSink::OnSetThreadFocus +// +//---------------------------------------------------------------------------- + +STDAPI CSampleIME::OnSetThreadFocus() +{ + if (_pCandidateListUIPresenter) + { + ITfDocumentMgr* pCandidateListDocumentMgr = nullptr; + ITfContext* pTfContext = _pCandidateListUIPresenter->_GetContextDocument(); + + if ((nullptr != pTfContext) && SUCCEEDED(pTfContext->GetDocumentMgr(&pCandidateListDocumentMgr))) + { + if (pCandidateListDocumentMgr == _pDocMgrLastFocused) + { + _pCandidateListUIPresenter->OnSetThreadFocus(); + } + + pCandidateListDocumentMgr->Release(); + } + } + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// ITfTextLayoutSink::OnKillThreadFocus +// +//---------------------------------------------------------------------------- + +STDAPI CSampleIME::OnKillThreadFocus() +{ + if (_pCandidateListUIPresenter) + { + ITfDocumentMgr* pCandidateListDocumentMgr = nullptr; + ITfContext* pTfContext = _pCandidateListUIPresenter->_GetContextDocument(); + + if ((nullptr != pTfContext) && SUCCEEDED(pTfContext->GetDocumentMgr(&pCandidateListDocumentMgr))) + { + if (_pDocMgrLastFocused) + { + _pDocMgrLastFocused->Release(); + _pDocMgrLastFocused = nullptr; + } + _pDocMgrLastFocused = pCandidateListDocumentMgr; + if (_pDocMgrLastFocused) + { + _pDocMgrLastFocused->AddRef(); + } + } + _pCandidateListUIPresenter->OnKillThreadFocus(); + } + return S_OK; +} + +BOOL CSampleIME::_InitThreadFocusSink() +{ + ITfSource* pSource = nullptr; + + if (FAILED(_pThreadMgr->QueryInterface(IID_ITfSource, (void **)&pSource))) + { + return FALSE; + } + + if (FAILED(pSource->AdviseSink(IID_ITfThreadFocusSink, (ITfThreadFocusSink *)this, &_dwThreadFocusSinkCookie))) + { + pSource->Release(); + return FALSE; + } + + pSource->Release(); + + return TRUE; +} + +void CSampleIME::_UninitThreadFocusSink() +{ + ITfSource* pSource = nullptr; + + if (FAILED(_pThreadMgr->QueryInterface(IID_ITfSource, (void **)&pSource))) + { + return; + } + + if (FAILED(pSource->UnadviseSink(_dwThreadFocusSinkCookie))) + { + pSource->Release(); + return; + } + + pSource->Release(); +} \ No newline at end of file diff --git a/ThreadMgrEventSink.cpp b/ThreadMgrEventSink.cpp new file mode 100644 index 00000000..d77ee4c6 --- /dev/null +++ b/ThreadMgrEventSink.cpp @@ -0,0 +1,178 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#include "Private.h" +#include "Globals.h" +#include "SampleIME.h" +#include "CandidateListUIPresenter.h" + +//+--------------------------------------------------------------------------- +// +// ITfThreadMgrEventSink::OnInitDocumentMgr +// +// Sink called by the framework just before the first context is pushed onto +// a document. +//---------------------------------------------------------------------------- + +STDAPI CSampleIME::OnInitDocumentMgr(_In_ ITfDocumentMgr *pDocMgr) +{ + pDocMgr; + return E_NOTIMPL; +} + +//+--------------------------------------------------------------------------- +// +// ITfThreadMgrEventSink::OnUninitDocumentMgr +// +// Sink called by the framework just after the last context is popped off a +// document. +//---------------------------------------------------------------------------- + +STDAPI CSampleIME::OnUninitDocumentMgr(_In_ ITfDocumentMgr *pDocMgr) +{ + pDocMgr; + return E_NOTIMPL; +} + +//+--------------------------------------------------------------------------- +// +// ITfThreadMgrEventSink::OnSetFocus +// +// Sink called by the framework when focus changes from one document to +// another. Either document may be NULL, meaning previously there was no +// focus document, or now no document holds the input focus. +//---------------------------------------------------------------------------- + +STDAPI CSampleIME::OnSetFocus(_In_ ITfDocumentMgr *pDocMgrFocus, _In_ ITfDocumentMgr *pDocMgrPrevFocus) +{ + pDocMgrPrevFocus; + + _InitTextEditSink(pDocMgrFocus); + + _UpdateLanguageBarOnSetFocus(pDocMgrFocus); + + // + // We have to hide/unhide candidate list depending on whether they are + // associated with pDocMgrFocus. + // + if (_pCandidateListUIPresenter) + { + ITfDocumentMgr* pCandidateListDocumentMgr = nullptr; + ITfContext* pTfContext = _pCandidateListUIPresenter->_GetContextDocument(); + if ((nullptr != pTfContext) && SUCCEEDED(pTfContext->GetDocumentMgr(&pCandidateListDocumentMgr))) + { + if (pCandidateListDocumentMgr != pDocMgrFocus) + { + _pCandidateListUIPresenter->OnKillThreadFocus(); + } + else + { + _pCandidateListUIPresenter->OnSetThreadFocus(); + } + + pCandidateListDocumentMgr->Release(); + } + } + + if (_pDocMgrLastFocused) + { + _pDocMgrLastFocused->Release(); + _pDocMgrLastFocused = nullptr; + } + + _pDocMgrLastFocused = pDocMgrFocus; + + if (_pDocMgrLastFocused) + { + _pDocMgrLastFocused->AddRef(); + } + + return S_OK; +} + +//+--------------------------------------------------------------------------- +// +// ITfThreadMgrEventSink::OnPushContext +// +// Sink called by the framework when a context is pushed. +//---------------------------------------------------------------------------- + +STDAPI CSampleIME::OnPushContext(_In_ ITfContext *pContext) +{ + pContext; + + return E_NOTIMPL; +} + +//+--------------------------------------------------------------------------- +// +// ITfThreadMgrEventSink::OnPopContext +// +// Sink called by the framework when a context is popped. +//---------------------------------------------------------------------------- + +STDAPI CSampleIME::OnPopContext(_In_ ITfContext *pContext) +{ + pContext; + + return E_NOTIMPL; +} + +//+--------------------------------------------------------------------------- +// +// _InitThreadMgrEventSink +// +// Advise our sink. +//---------------------------------------------------------------------------- + +BOOL CSampleIME::_InitThreadMgrEventSink() +{ + ITfSource* pSource = nullptr; + BOOL ret = FALSE; + + if (FAILED(_pThreadMgr->QueryInterface(IID_ITfSource, (void **)&pSource))) + { + return ret; + } + + if (FAILED(pSource->AdviseSink(IID_ITfThreadMgrEventSink, (ITfThreadMgrEventSink *)this, &_threadMgrEventSinkCookie))) + { + _threadMgrEventSinkCookie = TF_INVALID_COOKIE; + goto Exit; + } + + ret = TRUE; + +Exit: + pSource->Release(); + return ret; +} + +//+--------------------------------------------------------------------------- +// +// _UninitThreadMgrEventSink +// +// Unadvise our sink. +//---------------------------------------------------------------------------- + +void CSampleIME::_UninitThreadMgrEventSink() +{ + ITfSource* pSource = nullptr; + + if (_threadMgrEventSinkCookie == TF_INVALID_COOKIE) + { + return; + } + + if (SUCCEEDED(_pThreadMgr->QueryInterface(IID_ITfSource, (void **)&pSource))) + { + pSource->UnadviseSink(_threadMgrEventSinkCookie); + pSource->Release(); + } + + _threadMgrEventSinkCookie = TF_INVALID_COOKIE; +} diff --git a/TipCandidateList.cpp b/TipCandidateList.cpp new file mode 100644 index 00000000..eb52d151 --- /dev/null +++ b/TipCandidateList.cpp @@ -0,0 +1,185 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#include "private.h" +#include "TipCandidateList.h" +#include "EnumTfCandidates.h" +#include "TipCandidateString.h" + +HRESULT CTipCandidateList::CreateInstance(_Outptr_ ITfCandidateList **ppobj, size_t candStrReserveSize) +{ + if (ppobj == nullptr) + { + return E_INVALIDARG; + } + *ppobj = nullptr; + + *ppobj = new (std::nothrow) CTipCandidateList(candStrReserveSize); + if (*ppobj == nullptr) + { + return E_OUTOFMEMORY; + } + + return S_OK; +} + +HRESULT CTipCandidateList::CreateInstance(REFIID riid, _Outptr_ void **ppvObj, size_t candStrReserveSize) +{ + if (ppvObj == nullptr) + { + return E_INVALIDARG; + } + *ppvObj = nullptr; + + *ppvObj = new (std::nothrow) CTipCandidateList(candStrReserveSize); + if (*ppvObj == nullptr) + { + return E_OUTOFMEMORY; + } + + return ((CTipCandidateList*)(*ppvObj))->QueryInterface(riid, ppvObj); +} + +CTipCandidateList::CTipCandidateList(size_t candStrReserveSize) +{ + _refCount = 0; + + if (0 < candStrReserveSize) + { + _tfCandStrList.reserve(candStrReserveSize); + } +} + +CTipCandidateList::~CTipCandidateList() +{ +} + +STDMETHODIMP CTipCandidateList::QueryInterface(REFIID riid, _Outptr_ void **ppvObj) +{ + if (ppvObj == nullptr) + { + return E_POINTER; + } + *ppvObj = nullptr; + + if (IsEqualIID(riid, IID_IUnknown)) + { + *ppvObj = (ITfCandidateList*)this; + } + else if (IsEqualIID(riid, IID_ITfCandidateList)) + { + *ppvObj = (ITfCandidateList*)this; + } + + if (*ppvObj == nullptr) + { + return E_NOINTERFACE; + } + + AddRef(); + return S_OK; +} + +STDMETHODIMP_(ULONG) CTipCandidateList::AddRef() +{ + return (ULONG)InterlockedIncrement((LONG*)&_refCount); +} + +STDMETHODIMP_(ULONG) CTipCandidateList::Release() +{ + ULONG cRefT = (ULONG)InterlockedDecrement((LONG*)&_refCount); + if (0 < cRefT) + { + return cRefT; + } + + delete this; + + return 0; +} + +STDMETHODIMP CTipCandidateList::EnumCandidates(_Outptr_ IEnumTfCandidates **ppEnum) +{ + return CEnumTfCandidates::CreateInstance(IID_IEnumTfCandidates, (void**)ppEnum, _tfCandStrList); +} + +STDMETHODIMP CTipCandidateList::GetCandidate(ULONG nIndex, _Outptr_result_maybenull_ ITfCandidateString **ppCandStr) +{ + if (ppCandStr == nullptr) + { + return E_POINTER; + } + *ppCandStr = nullptr; + + ULONG sizeCandStr = (ULONG)_tfCandStrList.Count(); + if (sizeCandStr <= nIndex) + { + return E_FAIL; + } + + for (UINT i = 0; i < _tfCandStrList.Count(); i++) + { + ITfCandidateString** ppCandStrCur = _tfCandStrList.GetAt(i); + ULONG indexCur = 0; + if ((nullptr != ppCandStrCur) && (SUCCEEDED((*ppCandStrCur)->GetIndex(&indexCur)))) + { + if (nIndex == indexCur) + { + BSTR bstr; + CTipCandidateString* pTipCandidateStrCur = (CTipCandidateString*)(*ppCandStrCur); + pTipCandidateStrCur->GetString(&bstr); + + CTipCandidateString::CreateInstance(IID_ITfCandidateString, (void**)ppCandStr); + + if (nullptr != (*ppCandStr)) + { + CTipCandidateString* pTipCandidateStr = (CTipCandidateString*)(*ppCandStr); + pTipCandidateStr->SetString((LPCWSTR)bstr, SysStringLen(bstr)); + } + + SysFreeString(bstr); + + break; + } + } + } + return S_OK; +} + +STDMETHODIMP CTipCandidateList::GetCandidateNum(_Out_ ULONG *pnCnt) +{ + if (pnCnt == nullptr) + { + return E_POINTER; + } + + *pnCnt = (ULONG)(_tfCandStrList.Count()); + return S_OK; +} + +STDMETHODIMP CTipCandidateList::SetResult(ULONG nIndex, TfCandidateResult imcr) +{ + nIndex;imcr; + + return E_NOTIMPL; +} + +STDMETHODIMP CTipCandidateList::SetCandidate(_In_ ITfCandidateString **ppCandStr) +{ + if (ppCandStr == nullptr) + { + return E_POINTER; + } + + ITfCandidateString** ppCandLast = _tfCandStrList.Append(); + if (ppCandLast) + { + *ppCandLast = *ppCandStr; + } + + return S_OK; +} diff --git a/TipCandidateList.h b/TipCandidateList.h new file mode 100644 index 00000000..535d54b6 --- /dev/null +++ b/TipCandidateList.h @@ -0,0 +1,43 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#pragma once + +#include "SampleIMEBaseStructure.h" + +// +// CTipCandidateList +// + +class CTipCandidateList : public ITfCandidateList +{ +protected: + CTipCandidateList(size_t candStrReserveSize); + virtual ~CTipCandidateList(); + +public: + static HRESULT CreateInstance(_Outptr_ ITfCandidateList **ppobj, size_t candStrReserveSize = 0); + static HRESULT CreateInstance(REFIID riid, _Outptr_ void **ppvObj, size_t candStrReserveSize = 0); + + // IUnknown methods + virtual STDMETHODIMP QueryInterface(REFIID riid, _Outptr_ void **ppvObj); + virtual STDMETHODIMP_(ULONG) AddRef(); + virtual STDMETHODIMP_(ULONG) Release(); + + // ITfCandidateList methods + virtual STDMETHODIMP EnumCandidates(_Outptr_ IEnumTfCandidates **ppEnum); + virtual STDMETHODIMP GetCandidate(ULONG nIndex, _Outptr_result_maybenull_ ITfCandidateString **ppCandStr); + virtual STDMETHODIMP GetCandidateNum(_Out_ ULONG *pnCnt); + virtual STDMETHODIMP SetResult(ULONG nIndex, TfCandidateResult imcr); + + virtual STDMETHODIMP SetCandidate(_In_ ITfCandidateString **ppCandStr); + +protected: + long _refCount; + CSampleImeArray _tfCandStrList; +}; + diff --git a/TipCandidateString.cpp b/TipCandidateString.cpp new file mode 100644 index 00000000..1d1fee1a --- /dev/null +++ b/TipCandidateString.cpp @@ -0,0 +1,128 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#include "private.h" +#include "TipCandidateString.h" + +HRESULT CTipCandidateString::CreateInstance(_Outptr_ CTipCandidateString **ppobj) +{ + if (ppobj == nullptr) + { + return E_INVALIDARG; + } + *ppobj = nullptr; + + *ppobj = new (std::nothrow) CTipCandidateString(); + if (*ppobj == nullptr) + { + return E_OUTOFMEMORY; + } + + return S_OK; +} + +HRESULT CTipCandidateString::CreateInstance(REFIID riid, _Outptr_ void **ppvObj) +{ + if (ppvObj == nullptr) + { + return E_INVALIDARG; + } + *ppvObj = nullptr; + + *ppvObj = new (std::nothrow) CTipCandidateString(); + if (*ppvObj == nullptr) + { + return E_OUTOFMEMORY; + } + + return ((CTipCandidateString*)(*ppvObj))->QueryInterface(riid, ppvObj); +} + +CTipCandidateString::CTipCandidateString(void) +{ + _refCount = 0; + _index = 0; +} + +CTipCandidateString::~CTipCandidateString() +{ +} + +// IUnknown methods +STDMETHODIMP CTipCandidateString::QueryInterface(REFIID riid, _Outptr_ void **ppvObj) +{ + if (ppvObj == nullptr) + { + return E_POINTER; + } + *ppvObj = nullptr; + + if (IsEqualIID(riid, IID_IUnknown)) + { + *ppvObj = (CTipCandidateString*)this; + } + else if (IsEqualIID(riid, IID_ITfCandidateString)) + { + *ppvObj = (CTipCandidateString*)this; + } + + if (*ppvObj == nullptr) + { + return E_NOINTERFACE; + } + + AddRef(); + return S_OK; +} + +STDMETHODIMP_(ULONG) CTipCandidateString::AddRef(void) +{ + return (ULONG)InterlockedIncrement((LONG*)&_refCount); +} + +STDMETHODIMP_(ULONG) CTipCandidateString::Release(void) +{ + ULONG refT = (ULONG)InterlockedDecrement((LONG*)&_refCount); + if (0 < refT) + { + return refT; + } + + delete this; + + return 0; +} + +// ITfCandidateString methods +STDMETHODIMP CTipCandidateString::GetString(BSTR *pbstr) +{ + *pbstr = SysAllocString(_candidateStr.c_str()); + return S_OK; +} + +STDMETHODIMP CTipCandidateString::GetIndex(_Out_ ULONG *pnIndex) +{ + if (pnIndex == nullptr) + { + return E_POINTER; + } + + *pnIndex = _index; + return S_OK; +} + +STDMETHODIMP CTipCandidateString::SetIndex(ULONG uIndex) +{ + _index = uIndex; + return S_OK; +} + +STDMETHODIMP CTipCandidateString::SetString(_In_ const WCHAR *pch, DWORD_PTR length) +{ + _candidateStr.assign(pch, 0, length); + return S_OK; +} diff --git a/TipCandidateString.h b/TipCandidateString.h new file mode 100644 index 00000000..5faa8b4c --- /dev/null +++ b/TipCandidateString.h @@ -0,0 +1,39 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#pragma once + +#include "private.h" +#include + +class CTipCandidateString : public ITfCandidateString +{ +protected: + CTipCandidateString(); + virtual ~CTipCandidateString(); + +public: + static HRESULT CreateInstance(_Outptr_ CTipCandidateString **ppobj); + static HRESULT CreateInstance(REFIID riid, _Outptr_ void **ppvObj); + + // IUnknown methods + virtual STDMETHODIMP QueryInterface(REFIID riid, _Outptr_ void **ppvObj); + virtual STDMETHODIMP_(ULONG) AddRef(); + virtual STDMETHODIMP_(ULONG) Release(); + + // ITfCandidateString methods + virtual STDMETHODIMP GetString(BSTR *pbstr); + virtual STDMETHODIMP GetIndex(_Out_ ULONG *pnIndex); + + virtual STDMETHODIMP SetIndex(ULONG uIndex); + virtual STDMETHODIMP SetString(_In_ const WCHAR *pch, DWORD_PTR length); + +protected: + long _refCount; + int _index; + std::wstring _candidateStr; +}; \ No newline at end of file diff --git a/image/DoubleSingleByteOff.ico b/image/DoubleSingleByteOff.ico new file mode 100644 index 00000000..1f87d9a1 Binary files /dev/null and b/image/DoubleSingleByteOff.ico differ diff --git a/image/DoubleSingleByteOn.ico b/image/DoubleSingleByteOn.ico new file mode 100644 index 00000000..a1405b72 Binary files /dev/null and b/image/DoubleSingleByteOn.ico differ diff --git a/image/ImeModeOff.ico b/image/ImeModeOff.ico new file mode 100644 index 00000000..5bfbd093 Binary files /dev/null and b/image/ImeModeOff.ico differ diff --git a/image/ImeModeOn.ico b/image/ImeModeOn.ico new file mode 100644 index 00000000..7d6a9c03 Binary files /dev/null and b/image/ImeModeOn.ico differ diff --git a/image/PunctuationOff.ico b/image/PunctuationOff.ico new file mode 100644 index 00000000..e3003693 Binary files /dev/null and b/image/PunctuationOff.ico differ diff --git a/image/PunctuationOn.ico b/image/PunctuationOn.ico new file mode 100644 index 00000000..14486840 Binary files /dev/null and b/image/PunctuationOn.ico differ diff --git a/image/SampleIme.ico b/image/SampleIme.ico new file mode 100644 index 00000000..21845ba8 Binary files /dev/null and b/image/SampleIme.ico differ diff --git a/lcompile.ps1 b/lcompile.ps1 new file mode 100644 index 00000000..6469fba5 --- /dev/null +++ b/lcompile.ps1 @@ -0,0 +1,28 @@ +# generate and compile exe files + +$currentDirectory = Get-Location +$cmakeListsPath = Join-Path -Path $currentDirectory -ChildPath "CMakeLists.txt" + +if (-not (Test-Path $cmakeListsPath)) +{ + Write-Host("No CMakeLists.txt in current directory, please check.") + return +} + +Write-Host "Start generating and compiling..." + +$buildFolderPath = ".\build" + +if (-not (Test-Path $buildFolderPath)) +{ + New-Item -ItemType Directory -Path $buildFolderPath | Out-Null + Write-Host "build folder created." +} + +cmake -G "Visual Studio 17 2022" -A Win32 -S . -B ./build/ + +if ($LASTEXITCODE -eq 0) +{ + cmake --build ./build/ --config DEBUG +} + diff --git a/resource.h b/resource.h new file mode 100644 index 00000000..4aabddf3 --- /dev/null +++ b/resource.h @@ -0,0 +1,29 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by SampleIME.rc +// +#define SUBLANG_ENGLISH_US 0x01 +#define LANG_ENGLISH 0x09 +#define IDIS_SAMPLEIME 12 +#define IDS_DEFAULT_FONT 13 +#define IDS_IME_MODE 20 +#define IDI_IME_MODE_ON 21 +#define IDI_IME_MODE_OFF 22 +#define IDS_DOUBLE_SINGLE_BYTE 23 +#define IDI_DOUBLE_SINGLE_BYTE_ON 24 +#define IDI_DOUBLE_SINGLE_BYTE_OFF 25 +#define IDS_PUNCTUATION 26 +#define IDI_PUNCTUATION_ON 27 +#define IDI_PUNCTUATION_OFF 28 +#define IDR_VERSION2 107 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 108 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/stdafx.h b/stdafx.h new file mode 100644 index 00000000..dd99a888 --- /dev/null +++ b/stdafx.h @@ -0,0 +1,9 @@ +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved + +#pragma once +#include