#include "private.h" #include "commdlg.h" #include "TextInputCtrl.h" extern HINSTANCE g_hInst; #define TEXTINPUTCTRL_CLASSNAME TEXT("TextInputCtrlClassName") #define TIMERID_CARET 0x100 //---------------------------------------------------------------- // // // //---------------------------------------------------------------- ATOM CTextInputCtrl::RegisterClass(HINSTANCE hInstance) { WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = (WNDPROC)s_WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = NULL; wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wcex.lpszMenuName = NULL; wcex.lpszClassName = TEXTINPUTCTRL_CLASSNAME; wcex.hIconSm = NULL; return RegisterClassEx(&wcex); } //---------------------------------------------------------------- // // // //---------------------------------------------------------------- HWND CTextInputCtrl::Create(HWND hwndParent) { _hwnd = CreateWindow(TEXTINPUTCTRL_CLASSNAME, NULL, WS_CHILD, 0, 0, 0, 0, hwndParent, NULL, g_hInst, this); return _hwnd; } //---------------------------------------------------------------- // // // //---------------------------------------------------------------- LRESULT CALLBACK CTextInputCtrl::s_WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc; PAINTSTRUCT ps; HIMC himc; CTextInputCtrl *ptic; switch (message) { case WM_CREATE: SetThis(hwnd, ((CREATESTRUCT *)lParam)->lpCreateParams); SetTimer(hwnd, TIMERID_CARET, GetCaretBlinkTime(), NULL); GetThis(hwnd)->OnCreate(hwnd, wParam, lParam); break; case WM_PAINT: ptic = GetThis(hwnd); hdc = BeginPaint(hwnd, &ps); if (ptic) ptic->OnPaint(hdc); EndPaint(hwnd, &ps); break; case WM_KEYDOWN: ptic = GetThis(hwnd); if (ptic) ptic->OnKeyDown(wParam, lParam); break; case WM_LBUTTONDOWN: ptic = GetThis(hwnd); if (ptic) ptic->OnLButtonDown(wParam, lParam); break; case WM_LBUTTONUP: ptic = GetThis(hwnd); if (ptic) ptic->OnLButtonUp(wParam, lParam); break; case WM_MOUSEMOVE: ptic = GetThis(hwnd); if (ptic) ptic->OnMouseMove(wParam, lParam); break; case WM_SETFOCUS: // // Level 2 Support // ptic = GetThis(hwnd); if (ptic) { ptic->_editor.SetCompositionForm(); ptic->SetCompositionFont(); } break; case WM_IME_STARTCOMPOSITION: // // Level 2 Support // ptic = GetThis(hwnd); if (ptic) { ptic->_editor.OnStartComposition(); ptic->_editor.SetCompositionForm(); ptic->SetCompositionFont(); } // If it is a near caret IME, we should keep Level 2 path. if (IsNearCaretIME()) { return DefWindowProc(hwnd, message, wParam, lParam); } break; case WM_IME_COMPOSITION: himc = ImmGetContext(hwnd); if (himc) { BOOL fNearCaret = IsNearCaretIME(); if (lParam & GCS_RESULTSTR) { LONG nSize = ImmGetCompositionString(himc, GCS_RESULTSTR, NULL, 0); if (nSize) { LPWSTR psz = (LPWSTR)LocalAlloc(LPTR, nSize + sizeof(WCHAR)); if (psz) { ImmGetCompositionString(himc, GCS_RESULTSTR, psz, nSize); ptic = GetThis(hwnd); if (ptic) { ptic->_editor.InsertResultAtComposition(psz); ptic->_editor.UpdateLayout(&ptic->_lfCurrentFont); ptic->_editor.SetCompositionForm(); InvalidateRect(hwnd, NULL, TRUE); } LocalFree(psz); } } } if ((lParam & GCS_COMPSTR) && !fNearCaret) { LONG lDeltaStart = ImmGetCompositionString(himc, GCS_DELTASTART, NULL, 0); LONG lCursorPos = ImmGetCompositionString(himc, GCS_CURSORPOS, NULL, 0); LONG lSize = ImmGetCompositionString(himc, GCS_COMPSTR, NULL, 0); LONG lAttrSize = ImmGetCompositionString(himc, GCS_COMPATTR, NULL, 0); LONG lClauseInfoSize = ImmGetCompositionString(himc, GCS_COMPCLAUSE, NULL, 0); if (lSize) { BYTE *prgAttr = NULL; LONG *prgClauseInfo = NULL; LPWSTR psz = (LPWSTR)LocalAlloc(LPTR, lSize + sizeof(WCHAR)); if (lAttrSize) prgAttr = (BYTE *)LocalAlloc(LPTR, lAttrSize + sizeof(BYTE)); if (lClauseInfoSize) prgClauseInfo = (LONG *)LocalAlloc(LPTR, lClauseInfoSize + sizeof(LONG)); if (psz) { if (ImmGetCompositionString(himc, GCS_COMPSTR, psz, lSize)) { if (prgAttr) { if (!ImmGetCompositionString(himc, GCS_COMPATTR, prgAttr, lAttrSize)) { prgAttr = NULL; lAttrSize = 0; } } if (prgClauseInfo) { if (!ImmGetCompositionString(himc, GCS_COMPCLAUSE, prgClauseInfo, lClauseInfoSize)) { prgClauseInfo = NULL; lClauseInfoSize = 0; } } ptic = GetThis(hwnd); if (ptic) { ptic->_editor.UpdateComposition(psz, lDeltaStart, lCursorPos, prgAttr, lAttrSize, prgClauseInfo, lClauseInfoSize); ptic->_editor.UpdateLayout(&ptic->_lfCurrentFont); ptic->_editor.SetCompositionForm(); InvalidateRect(hwnd, NULL, TRUE); } } LocalFree(psz); } if (prgAttr) LocalFree(prgAttr); if (prgClauseInfo) LocalFree(prgClauseInfo); } } ImmReleaseContext(hwnd, himc); // If it is a near caret IME, we should keep Level 2 path. if ((lParam & GCS_COMPSTR) && fNearCaret) { lParam &= ~(GCS_RESULTCLAUSE | GCS_RESULTREADCLAUSE | GCS_RESULTREADSTR | GCS_RESULTSTR); return DefWindowProc(hwnd, message, wParam, lParam); } } break; case WM_IME_ENDCOMPOSITION: // The composition ends. ptic = GetThis(hwnd); if (ptic) { // We need to clear the attributes. // There is a case that the composition string is canceled and it did not // completed (GCS_RESULTSTR did not come). ptic->_editor.ClearAttrAndClauseInfo(); ptic->_editor.UpdateLayout(&ptic->_lfCurrentFont); InvalidateRect(hwnd, NULL, TRUE); } // If it is a near caret IME, we should keep Level 2 path. if (IsNearCaretIME()) { return DefWindowProc(hwnd, message, wParam, lParam); } break; case WM_IME_NOTIFY: if (wParam == IMN_OPENCANDIDATE) { ptic = GetThis(hwnd); if (ptic) { ptic->_editor.SetCandidateForm(); } } return DefWindowProc(hwnd, message, wParam, lParam); case WM_IME_SETCONTEXT: return DefWindowProc(hwnd, message, wParam, lParam & ~ISC_SHOWUICOMPOSITIONWINDOW); case WM_IME_CHAR: // // wParam is a character of the result string. // if we don't want to receive WM_CHAR message for this character, // we should not call DefWindowProc(). // return DefWindowProc(hwnd, message, wParam, lParam); case WM_CHAR: // // wParam is a character of the result string. // switch ((WCHAR)wParam) { case 0x08: case 0x0a: return 0; default: break; } ptic = GetThis(hwnd); if (ptic) { WCHAR wc[2]; wc[0] = (WCHAR)wParam; wc[1] = L'\0'; ptic->_editor.InsertAtSelection(wc); InvalidateRect(hwnd, NULL, TRUE); } break; case WM_TIMER: if (wParam == TIMERID_CARET) { ptic = GetThis(hwnd); if (ptic) { HDC hdc = GetDC(hwnd); ptic->_editor.BlinkCaret(hdc); ReleaseDC(hwnd, hdc); } } break; default: return DefWindowProc(hwnd, message, wParam, lParam); } return 0; } //---------------------------------------------------------------- // // // //---------------------------------------------------------------- void CTextInputCtrl::OnPaint(HDC hdc) { _editor.Render(hdc, &_lfCurrentFont); } //---------------------------------------------------------------- // // // //---------------------------------------------------------------- void CTextInputCtrl::OnKeyDown(WPARAM wParam, LPARAM lParam) { UINT nSelStart; UINT nSelEnd; switch (0xff & wParam) { case VK_LEFT: if (GetKeyState(VK_SHIFT) & 0x80) { nSelStart = _editor.GetSelectionStart(); nSelEnd = _editor.GetSelectionEnd(); if (nSelStart > 0) { _editor.MoveSelection(nSelStart - 1, nSelEnd); } } else { _editor.MoveSelectionPrev(); } InvalidateRect(_hwnd, NULL, TRUE); break; case VK_RIGHT: if (GetKeyState(VK_SHIFT) & 0x80) { nSelStart = _editor.GetSelectionStart(); nSelEnd = _editor.GetSelectionEnd(); _editor.MoveSelection(nSelStart, nSelEnd + 1); } else { _editor.MoveSelectionNext(); } InvalidateRect(_hwnd, NULL, TRUE); break; case VK_UP: _editor.MoveSelectionUpDown(TRUE); InvalidateRect(_hwnd, NULL, TRUE); break; case VK_DOWN: _editor.MoveSelectionUpDown(FALSE); InvalidateRect(_hwnd, NULL, TRUE); break; case VK_HOME: _editor.MoveSelectionToLineFirstEnd(TRUE); InvalidateRect(_hwnd, NULL, TRUE); break; case VK_END: _editor.MoveSelectionToLineFirstEnd(FALSE); InvalidateRect(_hwnd, NULL, TRUE); break; case VK_DELETE: nSelStart = _editor.GetSelectionStart(); nSelEnd = _editor.GetSelectionEnd(); if (nSelStart == nSelEnd) { _editor.DeleteAtSelection(FALSE); } else { _editor.RemoveText(nSelStart, nSelEnd - nSelStart); _editor.MoveSelection(nSelStart, nSelStart); } InvalidateRect(_hwnd, NULL, TRUE); break; case VK_BACK: nSelStart = _editor.GetSelectionStart(); nSelEnd = _editor.GetSelectionEnd(); if (nSelStart == nSelEnd) { _editor.DeleteAtSelection(TRUE); _editor.MoveSelectionPrev(); } else { _editor.RemoveText(nSelStart, nSelEnd - nSelStart); _editor.MoveSelection(nSelStart, nSelStart); } InvalidateRect(_hwnd, NULL, TRUE); break; } } //---------------------------------------------------------------- // // // //---------------------------------------------------------------- void CTextInputCtrl::OnCreate(HWND hwnd, WPARAM wParam, LPARAM lParam) { _editor.SetWnd(hwnd); _editor.SetCompositionForm(); } //---------------------------------------------------------------- // // // //---------------------------------------------------------------- void CTextInputCtrl::OnLButtonDown(WPARAM wParam, LPARAM lParam) { POINT pt; _uSelDragStart = (UINT)-1; pt.x = GET_X_LPARAM(lParam); pt.y = GET_Y_LPARAM(lParam); if (_editor.MoveSelectionAtPoint(pt)) { InvalidateRect(_hwnd, NULL, TRUE); _uSelDragStart = _editor.GetSelectionStart(); } } //---------------------------------------------------------------- // // // //---------------------------------------------------------------- void CTextInputCtrl::OnLButtonUp(WPARAM wParam, LPARAM lParam) { UINT nSelStart = _editor.GetSelectionStart(); UINT nSelEnd = _editor.GetSelectionEnd(); POINT pt; pt.x = GET_X_LPARAM(lParam); pt.y = GET_Y_LPARAM(lParam); if (_editor.MoveSelectionAtPoint(pt)) { UINT nNewSelStart = _editor.GetSelectionStart(); UINT nNewSelEnd = _editor.GetSelectionEnd(); _editor.MoveSelection(min(nSelStart, nNewSelStart), max(nSelEnd, nNewSelEnd)); InvalidateRect(_hwnd, NULL, TRUE); } } //---------------------------------------------------------------- // // // //---------------------------------------------------------------- void CTextInputCtrl::OnMouseMove(WPARAM wParam, LPARAM lParam) { if (wParam & MK_LBUTTON) { POINT pt; pt.x = GET_X_LPARAM(lParam); pt.y = GET_Y_LPARAM(lParam); if (_editor.MoveSelectionAtPoint(pt)) { UINT nNewSelStart = _editor.GetSelectionStart(); UINT nNewSelEnd = _editor.GetSelectionEnd(); _editor.MoveSelection(min(_uSelDragStart, nNewSelStart), max(_uSelDragStart, nNewSelEnd)); InvalidateRect(_hwnd, NULL, TRUE); } } } //---------------------------------------------------------------- // // // //---------------------------------------------------------------- void CTextInputCtrl::SetFont(HWND hwndParent) { CHOOSEFONT cf; LOGFONT lf = _lfCurrentFont; cf.lStructSize = sizeof(CHOOSEFONT); cf.hwndOwner = hwndParent; cf.lpLogFont = &lf; cf.Flags = CF_INITTOLOGFONTSTRUCT | CF_SCREENFONTS; cf.rgbColors = RGB(0, 0, 0); cf.lCustData = 0; cf.lpfnHook = NULL; cf.lpTemplateName = NULL; cf.hInstance = NULL; cf.lpszStyle = NULL; cf.nFontType = SCREEN_FONTTYPE; cf.nSizeMin = 0; cf.nSizeMax = 0; if (ChooseFont(&cf)) { _lfCurrentFont = lf; // Level 2 Support SetCompositionFont(); InvalidateRect(_hwnd, NULL, TRUE); } } //---------------------------------------------------------------- // // // //---------------------------------------------------------------- void CTextInputCtrl::SetCompositionFont() { HIMC himc = ImmGetContext(_hwnd); if (himc) { ImmSetCompositionFont(himc, &_lfCurrentFont); } ImmReleaseContext(_hwnd, himc); }