#include "CtrlCore.h" #ifdef GUI_WIN #include #include namespace Upp { void XpClear(); // Clear cached windows theme handles #define LLOG(x) // DLOG(x) dword Ctrl::KEYtoK(dword chr) { if(chr == VK_TAB) chr = K_TAB; else if(chr == VK_SPACE) chr = K_SPACE; else if(chr == VK_RETURN) chr = K_RETURN; else chr = chr + K_DELTA; if(chr == K_ALT_KEY || chr == K_CTRL_KEY || chr == K_SHIFT_KEY) return chr; if(GetCtrl()) chr |= K_CTRL; if(GetAlt()) chr |= K_ALT; if(GetShift()) chr |= K_SHIFT; return chr; } class NilDrawFull : public NilDraw { virtual bool IsPaintingOp(const Rect& r) const { return true; } }; void AvoidPaintingCheck__() { Ctrl::painting = false; } dword GetKeyStateSafe(dword what) { bool h = Ctrl::painting; Ctrl::painting = false; dword r = GetKeyState(what); Ctrl::painting = h; return r; } static bool pendown = false; bool GetShift() { return !!(GetKeyStateSafe(VK_SHIFT) & 0x8000); } bool GetCtrl() { return !!(GetKeyStateSafe(VK_CONTROL) & 0x8000); } bool GetAlt() { return !!(GetKeyStateSafe(VK_MENU) & 0x8000); } bool GetCapsLock() { return !!(GetKeyStateSafe(VK_CAPITAL) & 1); } bool GetMouseLeft() { return pendown || !!(GetKeyStateSafe(VK_LBUTTON) & 0x8000); } bool GetMouseRight() { return Ctrl::GetPenInfo().barrel || !!(GetKeyStateSafe(VK_RBUTTON) & 0x8000); } bool GetMouseMiddle() { return !!(GetKeyStateSafe(VK_MBUTTON) & 0x8000); } Point Ctrl::CurrentMousePos; Point GetMousePos() { return Ctrl::CurrentMousePos; } bool PassWindowsKey(int wParam); void Ctrl::DoCancelPreedit() { if(!focusCtrlWnd) return; Top *top = focusCtrl->GetTop(); if(top) focusCtrl->HidePreedit(); if(top && top->hwnd) { HIMC himc = ImmGetContext(top->hwnd); if(himc && ImmGetOpenStatus(himc)) { ImmNotifyIME(himc, NI_COMPOSITIONSTR, CPS_CANCEL, 0); ImmReleaseContext(top->hwnd, himc); } } } static UINT getDpiForWindow(HWND hwnd) { static UINT (WINAPI *GetDpiForWindow)(HWND hwnd); DllFn(GetDpiForWindow, "user32.dll", "GetDpiForWindow"); return GetDpiForWindow ? GetDpiForWindow(hwnd) : 96; } static int getSystemMetricsForDpi(int nIndex, UINT dpi) { static int (*GetSystemMetricsForDpi)(int nIndex, UINT dpi); DllFn(GetSystemMetricsForDpi, "user32.dll", "GetSystemMetricsForDpi"); return GetSystemMetricsForDpi ? GetSystemMetricsForDpi(nIndex, dpi) : 0; } static bool IsMaximized(HWND hwnd) { WINDOWPLACEMENT placement = {0}; placement.length = sizeof(WINDOWPLACEMENT); return GetWindowPlacement(hwnd, &placement) && placement.showCmd == SW_SHOWMAXIMIZED; } static int win32_dpi_scale(int value, UINT dpi) { return (int)((double)value * dpi / 96); } static int getTitlebarButtonWidth(int dpi) { return win32_dpi_scale(47, dpi); } int GetWin32TitleBarHeight(HWND hwnd); // implemented in CtrlLib/ChWin32.cpp /* UHD r = [0, 0] - [2854, 60] : (2854, 60) button_width = 94 HD r = [0, 0] - [1424, 31] : (1424, 31) button_width = 47 Light Bar 238 244 249 Min/max 230,234,239 Dark bar 26,34,39 min/max 39,47,52 close 232,17,35 */ static Rect getTitleBarRect(HWND hwnd) // TODO (image, cy) { // Adopted from: https://github.com/oberth/custom-chrome/blob/master/source/gui/window_helper.hpp#L52-L64 const int top_and_bottom_borders = 2; int height = win32_dpi_scale(GetWin32TitleBarHeight(hwnd), GetDpiForWindow(hwnd)) + top_and_bottom_borders; RECT rect; GetClientRect(hwnd, &rect); rect.bottom = rect.top + height; return rect; } void Ctrl::PaintWinBar(SystemDraw& w, const Rect& clip) { bool custom_titlebar = false; auto topwin = dynamic_cast(this); HWND hwnd = GetHWND(); if(topwin && topwin->custom_titlebar && hwnd) { Rect r = getTitleBarRect(hwnd); DDUMP(r); // draw.DrawRect(r, Yellow()); int button_width = getTitlebarButtonWidth(GetDpiForWindow(hwnd)); DDUMP(button_width); int x = r.right; int n = 0; auto Drw = [&](const Image& m) { x -= button_width; Color ink = topwin->HasFocusDeep() ? SBlack() : Gray(); if(topwin->active_titlebar_button == n) { w.DrawRect(x, r.top, button_width, r.GetHeight(), n ? SWhiteGray() : Color(196, 43, 28)); if(n == 0) ink = SWhite(); } w.DrawImage(x + (button_width - m.GetWidth()) / 2, r.top + (r.GetHeight() - m.GetHeight()) / 2, m, ink); n++; }; Drw(CtrlCoreImg::WinClose()); if(topwin->IsZoomable()) { Drw(IsMaximized(hwnd) ? CtrlCoreImg::WinMaximized() : CtrlCoreImg::WinMaximize()); Drw(CtrlCoreImg::WinMinimize()); } // TODO: Fake shadow rect? } } LRESULT Ctrl::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) { GuiLock __; eventid++; // LLOG("Ctrl::WindowProc(" << message << ") in " << ::Name(this) << ", focus " << (void *)::GetFocus()); Ptr _this = this; HWND hwnd = GetHWND(); POINT p; if(::GetCursorPos(&p)) CurrentMousePos = p; bool custom_titlebar = false; auto topwin = dynamic_cast(this); if(topwin) custom_titlebar = topwin->custom_titlebar; Rect titlebar_rect = Null; int titlebar_button_width = 0; int active_titlebar_button = -1; bool active_titlebar_active = false; if(custom_titlebar) { titlebar_rect = getTitleBarRect(hwnd); titlebar_button_width = getTitlebarButtonWidth(GetDpiForWindow(hwnd)); Point p = CurrentMousePos; ScreenToClient(hwnd, p); if(p.y >= titlebar_rect.top && p.y < titlebar_rect.bottom) { int q = (titlebar_rect.right - p.x) / titlebar_button_width; active_titlebar_button = q >= 0 && q < (topwin->IsZoomable() ? 3 : 1) ? q : -1; } active_titlebar_active = topwin->HasFocusDeep(); if(findarg(message, WM_PAINT, WM_NCPAINT) < 0 && active_titlebar_button != topwin->active_titlebar_button || active_titlebar_active != topwin->active_titlebar_active) { topwin->active_titlebar_button = active_titlebar_button; topwin->active_titlebar_active = active_titlebar_active; InvalidateRect(hwnd, titlebar_rect, FALSE); } } cancel_preedit = DoCancelPreedit; // We really need this just once, but whatever.. is_pen_event = (GetMessageExtraInfo() & 0xFFFFFF80) == 0xFF515700; // https://learn.microsoft.com/en-us/windows/win32/tablet/system-events-and-mouse-messages?redirectedfrom=MSDN auto MousePos = [&] { Point p = Point((dword)lParam); CurrentMousePos = p; ::ClientToScreen(hwnd, CurrentMousePos); return p; }; bool has_preedit = HasFocusDeep() && focusCtrl && !IsNull(focusCtrl->GetPreedit()); auto StopPreedit = [&] { HidePreedit(); if(HasFocusDeep()) CancelPreedit(); }; auto ClickActivate = [&] { ClickActivateWnd(); StopPreedit(); }; switch(message) { case WM_POINTERDOWN: case WM_POINTERUPDATE: case WM_POINTERUP: { POINT p = Point((LONG)lParam); CurrentMousePos = p; ScreenToClient(hwnd, &p); pen.action = 0; pen.pressure = pen.rotation = Null; pen.tilt = Null; pen.eraser = pen.barrel = pen.inverted = pen.history = false; static BOOL (WINAPI *GetPointerType)(UINT32 pointerId, POINTER_INPUT_TYPE *pointerType); static BOOL (WINAPI *GetPointerInfo)(UINT32 pointerId, POINTER_INFO *pointerInfo); static BOOL (WINAPI *GetPointerPenInfo)(UINT32 pointerId, POINTER_PEN_INFO *penInfo); static BOOL (WINAPI *GetPointerPenInfoHistory)(UINT32 pointerId, UINT32 *entriesCount, POINTER_PEN_INFO *penInfo); ONCELOCK { DllFn(GetPointerType, "User32.dll", "GetPointerType"); DllFn(GetPointerInfo, "User32.dll", "GetPointerInfo"); DllFn(GetPointerPenInfo, "User32.dll", "GetPointerPenInfo"); DllFn(GetPointerPenInfoHistory, "User32.dll", "GetPointerPenInfoHistory"); }; if(!(GetPointerType && GetPointerInfo && GetPointerPenInfo && GetPointerPenInfoHistory)) break; POINTER_INPUT_TYPE pointerType; auto ProcessPenInfo = [&](POINTER_PEN_INFO& ppi) { if(ppi.penFlags & PEN_FLAG_BARREL) pen.barrel = true; if(ppi.penFlags & PEN_FLAG_INVERTED) pen.inverted = true; if(ppi.penFlags & PEN_FLAG_ERASER) pen.eraser = true; if(ppi.penMask & PEN_MASK_PRESSURE) pen.pressure = ppi.pressure / 1024.0; if(ppi.penMask & PEN_MASK_ROTATION) pen.rotation = ppi.rotation * M_2PI / 360; if(ppi.penMask & PEN_MASK_TILT_X) pen.tilt.x = ppi.tiltX * M_2PI / 360; if(ppi.penMask & PEN_MASK_TILT_Y) pen.tilt.y = ppi.tiltY * M_2PI / 360; }; auto DoPen = [&](Point p) { GuiLock __; eventCtrl = this; Ctrl *q = this; if(captureCtrl){ q = captureCtrl; p += GetScreenRect().TopLeft()-captureCtrl->GetScreenRect().TopLeft(); } else for(Ctrl *t = q; t; t=q->ChildFromPoint(p)) q = t; q->Pen(p, pen, GetMouseFlags()); SyncCaret(); Image m = CursorOverride(); if(IsNull(m)) SetMouseCursor(q->CursorImage(p,GetMouseFlags())); else SetMouseCursor(m); }; UINT32 pointerId = GET_POINTERID_WPARAM(wParam); if(GetPointerType(pointerId, &pointerType) && pointerType == PT_PEN) { UINT32 hc = 256; Buffer ppit(hc); if(message == WM_POINTERUPDATE && GetPointerPenInfoHistory(pointerId, &hc, ppit)) { for(int i = hc - 1; i >= 0; i--) { static Point lastp=Null; ProcessPenInfo(ppit[i]); pen.history = (bool)i; POINT hp = ppit[i].pointerInfo.ptPixelLocation; if(!pen.history || hp!=lastp){ lastp = CurrentMousePos = hp; ScreenToClient(hwnd, &hp); DoPen(hp); } } break; } POINTER_PEN_INFO ppi; if(GetPointerPenInfo(pointerId, &ppi)) ProcessPenInfo(ppi); switch(message) { case WM_POINTERDOWN: pendown=true; pen.action = PEN_DOWN; ClickActivate(); break; case WM_POINTERUP: pendown=false; pen.action = PEN_UP; break; } DoPen(p); break; } } break; case WM_PALETTECHANGED: if((HWND)wParam == hwnd) break; case WM_QUERYNEWPALETTE: if(!SystemDraw::AutoPalette()) break; { HDC hDC = GetDC(hwnd); HPALETTE hOldPal = SelectPalette(hDC, GetQlibPalette(), FALSE); int i = RealizePalette(hDC); SelectPalette(hDC, hOldPal, TRUE); RealizePalette(hDC); ReleaseDC(hwnd, hDC); LLOG("Realized " << i << " colors"); if(i) InvalidateRect(hwnd, NULL, TRUE); return i; } case WM_ERASEBKGND: if(erasebg) { HDC hdc = (HDC)(wParam); RECT rc; GetClientRect(hwnd, &rc); Color c = SColorFace(); HBRUSH brush = CreateSolidBrush(RGB(c.GetR(), c.GetG(), c.GetB())); FillRect(hdc, &rc, brush); DeleteObject(brush); erasebg = false; } return 1L; case WM_PAINT: ASSERT_(!painting || IsPanicMode(), "WM_PAINT invoked for " + Name() + " while in Paint routine"); ASSERT(hwnd); if(hwnd) { PAINTSTRUCT ps; if(IsVisible()) SyncScroll(); HDC dc = BeginPaint(hwnd, &ps); fullrefresh = false; if(IsVisible()) { SystemDraw draw(dc); HPALETTE hOldPal; if(draw.PaletteMode() && SystemDraw::AutoPalette()) { hOldPal = SelectPalette(dc, GetQlibPalette(), TRUE); int n = RealizePalette(dc); LLOG("In paint realized " << n << " colors"); } painting = true; UpdateArea(draw, Rect(ps.rcPaint)); if(draw.PaletteMode() && SystemDraw::AutoPalette()) SelectPalette(dc, hOldPal, TRUE); painting = false; } EndPaint(hwnd, &ps); UpdateDHCtrls(); // so that they are displayed withing the same WM_PAINT - looks better } return 0L; case WM_NCCALCSIZE: if(custom_titlebar && wParam) { // custom titlebar based on https://github.com/grassator/win32-window-custom-titlebar/blob/main/main.c UINT dpi = GetDpiForWindow(hwnd); int frame_x = GetSystemMetricsForDpi(SM_CXFRAME, dpi); int frame_y = GetSystemMetricsForDpi(SM_CYFRAME, dpi); int padding = GetSystemMetricsForDpi(92 /*SM_CXPADDEDBORDER*/, dpi); NCCALCSIZE_PARAMS *params = (NCCALCSIZE_PARAMS *)lParam; RECT *requested_client_rect = params->rgrc; requested_client_rect->right -= frame_x + padding; requested_client_rect->left += frame_x + padding; requested_client_rect->bottom -= frame_y + padding; if(IsMaximized(hwnd)) requested_client_rect->top += padding; return 0; } break; case WM_NCHITTEST: CheckMouseCtrl(); if(ignoremouse) return HTTRANSPARENT; if(custom_titlebar) { LRESULT hit = DefWindowProc(hwnd, message, wParam, lParam); if(findarg(hit, HTNOWHERE, HTRIGHT, HTLEFT, HTTOPLEFT, HTTOP, HTTOPRIGHT, HTBOTTOMRIGHT, HTBOTTOM, HTBOTTOMLEFT) >= 0) return hit; if(active_titlebar_button == 1) return HTMAXBUTTON; // Looks like adjustment happening in NCCALCSIZE is messing with the detection // of the top hit area so manually fixing that. UINT dpi = GetDpiForWindow(hwnd); int frame_y = GetSystemMetricsForDpi(SM_CYFRAME, dpi); int padding = GetSystemMetricsForDpi(92 /*SM_CXPADDEDBORDER*/, dpi); Point cursor_point((LONG)lParam); ScreenToClient(hwnd, cursor_point); // We should not return HTTOP when hit-testing a maximized window if(!IsMaximized(hwnd) && cursor_point.y > 0 && cursor_point.y < frame_y + padding && topwin->IsSizeable()) return HTTOP; // Since we are drawing our own caption, this needs to be a custom test if(cursor_point.y < getTitleBarRect(hwnd).bottom && topwin->IsCustomTitleBarDragArea(cursor_point)) return HTCAPTION; return HTCLIENT; } break; case WM_NCLBUTTONUP: // Map button clicks to the right messages for the window switch(active_titlebar_button) { case 0: PostMessageW(hwnd, WM_CLOSE, 0, 0); return 0; case 1: ShowWindow(hwnd, IsMaximized(hwnd) ? SW_NORMAL : SW_MAXIMIZE); return 0; case 2: ShowWindow(hwnd, SW_MINIMIZE); return 0; } break; case WM_NCRBUTTONUP: if(wParam == HTCAPTION) { BOOL isMaximized = IsMaximized(hwnd); MENUITEMINFO menu_item_info; menu_item_info.cbSize = sizeof(menu_item_info); menu_item_info.fMask = MIIM_STATE; HMENU sys_menu = GetSystemMenu(hwnd, false); auto Menu = [&](UINT item, bool enabled) { menu_item_info.fState = enabled ? MF_ENABLED : MF_DISABLED; SetMenuItemInfo(sys_menu, item, false, &menu_item_info); }; Menu(SC_RESTORE, isMaximized); Menu(SC_MOVE, !isMaximized); Menu(SC_SIZE, !isMaximized); Menu(SC_MINIMIZE, true); Menu(SC_MAXIMIZE, !isMaximized); Menu(SC_CLOSE, true); Point p((LONG)lParam); BOOL result = TrackPopupMenu(sys_menu, TPM_RETURNCMD, p.x, p.y, 0, hwnd, NULL); if(result) PostMessage(hwnd, WM_SYSCOMMAND, result, 0); } break; case WM_LBUTTONDOWN: ClickActivate(); if(ignoreclick) return 0L; DoMouse(LEFTDOWN, MousePos(), 0); if(_this) PostInput(); return 0L; case WM_LBUTTONUP: if(ignoreclick) EndIgnore(); else DoMouse(LEFTUP, MousePos(), 0); if(_this) PostInput(); return 0L; case WM_LBUTTONDBLCLK: ClickActivate(); if(ignoreclick) return 0L; DoMouse(LEFTDOUBLE, MousePos(), 0); if(_this) PostInput(); return 0L; case WM_RBUTTONDOWN: ClickActivate(); if(ignoreclick) return 0L; DoMouse(RIGHTDOWN, MousePos()); if(_this) PostInput(); return 0L; case WM_RBUTTONUP: if(ignoreclick) EndIgnore(); else DoMouse(RIGHTUP, MousePos()); if(_this) PostInput(); return 0L; case WM_RBUTTONDBLCLK: ClickActivate(); if(ignoreclick) return 0L; DoMouse(RIGHTDOUBLE, MousePos()); if(_this) PostInput(); return 0L; case WM_MBUTTONDOWN: ClickActivate(); if(ignoreclick) return 0L; DoMouse(MIDDLEDOWN, MousePos()); if(_this) PostInput(); return 0L; case WM_MBUTTONUP: if(ignoreclick) EndIgnore(); else DoMouse(MIDDLEUP, MousePos()); if(_this) PostInput(); return 0L; case WM_MBUTTONDBLCLK: ClickActivate(); if(ignoreclick) return 0L; DoMouse(MIDDLEDOUBLE, MousePos()); if(_this) PostInput(); return 0L; case WM_NCLBUTTONDOWN: ClickActivate(); IgnoreMouseUp(); if(active_titlebar_button >= 0) return 0; break; case WM_NCRBUTTONDOWN: case WM_NCMBUTTONDOWN: ClickActivate(); IgnoreMouseUp(); break; case WM_MOUSEMOVE: LLOG("WM_MOUSEMOVE: ignoreclick = " << ignoreclick); if(ignoreclick) EndIgnore(); else { if(_this) DoMouse(MOUSEMOVE, MousePos()); DoCursorShape(); } return 0L; case 0x20a: // WM_MOUSEWHEEL: case 0x20e: // WM_MOUSEHWHEEL: if(ignoreclick) { EndIgnore(); return 0L; } if(_this) { Point p(0, 0); ::ClientToScreen(hwnd, p); DoMouse(message == 0x20e ? MOUSEHWHEEL : MOUSEWHEEL, Point((dword)lParam) - p, (short)HIWORD(wParam)); CurrentMousePos = Point((dword)lParam); } if(_this) PostInput(); return 0L; case WM_SETCURSOR: if((HWND)wParam == hwnd && LOWORD((dword)lParam) == HTCLIENT) { if(hCursor) SetCursor(hCursor); return TRUE; } break; // case WM_MENUCHAR: // return MAKELONG(0, MNC_SELECT); case WM_KEYDOWN: case WM_SYSKEYDOWN: case WM_CHAR: ignorekeyup = false; case WM_KEYUP: case WM_SYSKEYUP: { #if 0 String msgdump; switch(message) { case WM_KEYDOWN: msgdump << "WM_KEYDOWN"; break; case WM_KEYUP: msgdump << "WM_KEYUP"; break; case WM_SYSKEYDOWN: msgdump << "WM_SYSKEYDOWN"; break; case WM_SYSKEYUP: msgdump << "WM_SYSKEYUP"; break; case WM_CHAR: msgdump << "WM_CHAR"; break; } msgdump << " wParam = 0x" << FormatIntHex(wParam, 8) << ", lParam = 0x" << FormatIntHex(lParam, 8) << ", ignorekeyup = " << (ignorekeyup ? "true" : "false"); LLOG(msgdump); #endif dword keycode = 0; if(message == WM_KEYDOWN) { keycode = KEYtoK((dword)wParam); if(keycode == K_SPACE) keycode = 0; } else if(message == WM_KEYUP) keycode = KEYtoK((dword)wParam) | K_KEYUP; else if(message == WM_SYSKEYDOWN /*&& ((lParam & 0x20000000) || wParam == VK_F10)*/) keycode = KEYtoK((dword)wParam); else if(message == WM_SYSKEYUP /*&& ((lParam & 0x20000000) || wParam == VK_F10)*/) keycode = KEYtoK((dword)wParam) | K_KEYUP; else if(message == WM_CHAR && wParam != 127 && wParam > 32 || wParam == 32 && KEYtoK(VK_SPACE) == K_SPACE) { if(IsWindowUnicode(hwnd)) { // TRC 04/10/17: ActiveX Unicode patch static WCHAR surr[2]; keycode = (dword)wParam; if((keycode & 0XFC00) == 0xD800) { // covert UTF16 surrogate pair to UTF32 codepoint surr[0] = (WCHAR)keycode; return 0L; } if((keycode & 0xFC00) == 0xDC00) { surr[1] = (WCHAR)keycode; keycode = ReadSurrogatePair(surr, surr + 2); surr[0] = 0; if(!keycode) return 0L; } else surr[0] = 0; } else { char b[20]; ::GetLocaleInfo(MAKELCID(LOWORD(GetKeyboardLayout(0)), SORT_DEFAULT), LOCALE_IDEFAULTANSICODEPAGE, b, 20); int codepage = atoi(b); if(codepage >= 1250 && codepage <= 1258) keycode = ToUnicode((dword)wParam, codepage - 1250 + CHARSET_WIN1250); else keycode = (dword)wParam; } } bool b = false; if(keycode) { b = DispatchKey(keycode, LOWORD(lParam)); SyncCaret(); if(_this) PostInput(); } // LOG("key processed = " << b); if(b || (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP) && wParam != VK_F4 && !PassWindowsKey((dword)wParam)) // 17.11.2003 Mirek -> invoke system menu return 0L; break; } break; // case WM_GETDLGCODE: // return wantfocus ? 0 : DLGC_STATIC; case WM_IME_STARTCOMPOSITION: case WM_IME_COMPOSITION: if(has_preedit) { HIMC himc = ImmGetContext(GetHWND()); if(!himc) break; Rect pr = GetPreeditScreenRect(); Point p = pr.TopLeft() - GetScreenRect().TopLeft(); CANDIDATEFORM cf; cf.dwIndex = 0; cf.dwStyle = CFS_EXCLUDE; cf.ptCurrentPos.x = p.x; cf.ptCurrentPos.y = p.y; cf.rcArea.left = p.x; cf.rcArea.top = p.y; cf.rcArea.right = p.x + DPI(20); // DPI(20) is sort of hack, but candidate windows are above or bellow anyway... cf.rcArea.bottom = p.y + pr.GetHeight(); ImmSetCandidateWindow(himc, &cf); /* // todo: SetCaretPos too Rect r; ::CreateCaret(hwnd, NULL, 1, pr.Height()); ::ShowCaret(hwnd); ::SetCaretPos(p.x, p.y); */ auto ReadString = [&](int type) -> WString { int len = ImmGetCompositionStringW (himc, type, NULL, 0); if(len > 0) { Buffer sw(len / 2); ImmGetCompositionStringW(himc, type, sw, len); return ToUtf32(sw, len / 2); } return Null; }; if(lParam & GCS_COMPSTR) { ShowPreedit(ReadString(GCS_COMPSTR), ImmGetCompositionString(himc, GCS_CURSORPOS, 0, 0)); } if(lParam & GCS_RESULTSTR) { WString h = ReadString(GCS_RESULTSTR); for(wchar c : h) DispatchKey(c, 1); HidePreedit(); SyncCaret(); } ImmReleaseContext(GetHWND(), himc); return 0L; } break; case WM_IME_ENDCOMPOSITION: if(has_preedit) { HidePreedit(); return 0L; } break; case WM_XBUTTONDOWN: { UINT button = GET_XBUTTON_WPARAM(wParam); if(button == XBUTTON2) DispatchKey(K_MOUSE_FORWARD, 1); if(button == XBUTTON1) DispatchKey(K_MOUSE_BACKWARD, 1); return 0L; } case WM_XBUTTONUP: { UINT button = GET_XBUTTON_WPARAM(wParam); if(button == XBUTTON2) DispatchKey(K_MOUSE_FORWARD|K_KEYUP, 1); if(button == XBUTTON1) DispatchKey(K_MOUSE_BACKWARD|K_KEYUP, 1); return 0L; } case WM_DESTROY: PreDestroy(); break; case WM_NCDESTROY: if(!hwnd) break; if(HasChildDeep(mouseCtrl) || this == ~mouseCtrl) mouseCtrl = NULL; if(HasChildDeep(focusCtrl) || this == ~focusCtrl) focusCtrl = NULL; if(HasChildDeep(focusCtrlWnd) || this == ~focusCtrlWnd) { LLOG("WM_NCDESTROY: clearing focusCtrlWnd = " << UPP::Name(focusCtrlWnd)); focusCtrlWnd = NULL; focusCtrl = NULL; } if(::GetFocus() == NULL) { Ctrl *owner = GetOwner(); if(owner && (owner->IsForeground() || IsForeground()) && !owner->SetWantFocus()) IterateFocusForward(owner, owner); } if(IsWindowUnicode(hwnd)) DefWindowProcW(hwnd, message, wParam, lParam); else DefWindowProc(hwnd, message, wParam, lParam); hwnd = NULL; return 0L; case WM_CANCELMODE: if(this == ~captureCtrl || HasChildDeep(captureCtrl)) ReleaseCtrlCapture(); break; case WM_SHOWWINDOW: visible = (BOOL) wParam; StateH(SHOW); break; case WM_MOUSEACTIVATE: LLOG("WM_MOUSEACTIVATE " << Name() << ", focusCtrlWnd = " << UPP::Name(focusCtrlWnd) << ", raw = " << (void *)::GetFocus()); if(!IsEnabled()) { if(lastActiveWnd && lastActiveWnd->IsEnabled()) { if(focusCtrl) { // this closes popup LLOG("WM_MOUSEACTIVATE -> ClickActivate for " << UPP::Name(lastActiveWnd)); StopPreedit(); lastActiveWnd->ClickActivateWnd(); } else { // this makes child dialog active when clicked on disabled parent LLOG("WM_MOUSEACTIVATE -> ::SetFocus for " << UPP::Name(lastActiveWnd)); ::SetFocus(lastActiveWnd->GetHWND()); } } else MessageBeep(MB_OK); return MA_NOACTIVATEANDEAT; } if(IsPopUp()) return MA_NOACTIVATE; break; case WM_SIZE: case WM_MOVE: if(hwnd) { Rect rect; if(activex) { WINDOWPLACEMENT wp; wp.length = sizeof(WINDOWINFO); ::GetWindowPlacement(hwnd, &wp); rect = wp.rcNormalPosition; } else rect = GetScreenClient(hwnd); LLOG("WM_MOVE / WM_SIZE: screen client = " << rect); if(GetRect() != rect) SetWndRect(rect); #if WINCARET WndDestroyCaret(); caretCtrl = NULL; SyncCaret(); #endif SyncPreedit(); } return 0L; case WM_HELP: return TRUE; case WM_ACTIVATE: LLOG("WM_ACTIVATE " << Name() << ", wParam = " << (int)wParam << ", focusCtrlWnd = " << UPP::Name(focusCtrlWnd) << ", raw = " << (void *)::GetFocus()); ignorekeyup = true; case WM_SETFOCUS: LLOG("WM_SETFOCUS " << Name() << ", focusCtrlWnd = " << UPP::Name(focusCtrlWnd) << ", raw = " << (void *)::GetFocus()); if(this != focusCtrlWnd || focusCtrl && focusCtrlWnd != focusCtrl->GetTopCtrl()) { // second condition fixes popup issue when clicking dialog parent if(IsEnabled()) { LLOG("WM_SETFOCUS -> ActivateWnd: this != focusCtrlWnd, this = " << Name() << ", focusCtrlWnd = " << UPP::Name(focusCtrlWnd)); ActivateWnd(); } else { if(focusCtrlWnd && focusCtrlWnd->IsEnabled()) { // if(!IsEnabled()) // MessageBeep(MB_OK); LLOG("WM_SETFOCUS -> ::SetFocus for " << UPP::Name(focusCtrlWnd) << ", this: " << UPP::Name(this)); ::SetFocus(focusCtrlWnd->GetHWND()); } else if(lastActiveWnd && lastActiveWnd->IsEnabled()) { LLOG("WM_SETFOCUS -> ::SetFocus for " << UPP::Name(lastActiveWnd)); ::SetFocus(lastActiveWnd->GetHWND()); } else { LLOG("WM_SETFOCUS - ::SetFocus(NULL)"); ::SetFocus(NULL); } } } LLOG("//WM_SETFOCUS " << (void *)hwnd << ", focusCtrlWnd = " << UPP::Name(focusCtrlWnd) << ", raw = " << (void *)::GetFocus()); return 0L; case WM_KILLFOCUS: LLOG("WM_KILLFOCUS " << (void *)(HWND)wParam << ", this = " << UPP::Name(this) << ", focusCtrlWnd = " << UPP::Name(focusCtrlWnd) << ", raw = " << (void *)::GetFocus()); LLOG("Kill " << UPP::Name(CtrlFromHWND((HWND)wParam))); if(!CtrlFromHWND((HWND)wParam)) { LLOG("WM_KILLFOCUS -> KillFocusWnd: " << UPP::Name(this)); KillFocusWnd(); } LLOG("//WM_KILLFOCUS " << (void *)(HWND)wParam << ", focusCtrlWnd = " << UPP::Name(focusCtrlWnd) << ", raw = " << (void *)::GetFocus()); StopPreedit(); return 0L; case WM_ENABLE: if(!!wParam != enabled) { enabled = !!wParam; RefreshFrame(); StateH(ENABLE); } return 0L; #ifndef PLATFORM_WINCE case WM_GETMINMAXINFO: { MINMAXINFO *mmi = (MINMAXINFO *)lParam; Rect frmrc = Size(200, 200); ::AdjustWindowRect(frmrc, WS_OVERLAPPEDWINDOW, FALSE); // Size msz = Ctrl::GetWorkArea().Deflated(-frmrc.left, -frmrc.top, // frmrc.right - 200, frmrc.bottom - 200).GetSize(); // Rect minr(Point(50, 50), min(msz, GetMinSize())); // Rect maxr(Point(50, 50), min(msz, GetMaxSize())); // Removed cxl&nixnixnix 2012-6-12 Rect minr(Point(50, 50), GetMinSize()); Rect maxr(Point(50, 50), GetMaxSize()); dword style = ::GetWindowLong(hwnd, GWL_STYLE); dword exstyle = ::GetWindowLong(hwnd, GWL_EXSTYLE); AdjustWindowRectEx(minr, style, FALSE, exstyle); AdjustWindowRectEx(maxr, style, FALSE, exstyle); mmi->ptMinTrackSize = Point(minr.Size()); mmi->ptMaxTrackSize = Point(maxr.Size()); LLOG("WM_GETMINMAXINFO: MinTrackSize = " << Point(mmi->ptMinTrackSize) << ", MaxTrackSize = " << Point(mmi->ptMaxTrackSize)); LLOG("ptMaxSize = " << Point(mmi->ptMaxSize) << ", ptMaxPosition = " << Point(mmi->ptMaxPosition)); } return 0L; #endif case WM_SETTINGCHANGE: case 0x031A: // WM_THEMECHANGED PostReSkin(); break; /* case WM_IME_COMPOSITION: HIMC himc = ImmGetContext(hwnd); if(!himc) break; CANDIDATEFORM cf; Rect r = GetScreenRect(); cf.dwIndex = 0; cf.dwStyle = CFS_CANDIDATEPOS; cf.ptCurrentPos.x = r.left + caretx; cf.ptCurrentPos.y = r.top + carety + caretcy; ImmSetCandidateWindow (himc, &cf); break; */ } if(hwnd) { LRESULT r; int level = LeaveGuiMutexAll(); if(IsWindowUnicode(hwnd)) r = DefWindowProcW(hwnd, message, wParam, lParam); else r = DefWindowProc(hwnd, message, wParam, lParam); EnterGuiMutex(level); return r; } return 0L; } void Ctrl::PreDestroy() {} } #endif