#include "CtrlCore.h" #ifdef GUI_WIN namespace Upp { #define LLOG(x) // DLOG(x) #define LOGTIMING 0 #ifdef _DEBUG // #define LOGMESSAGES 1 #endif #define ELOGW(x) // RLOG(GetSysTime() << ": " << x) // Only activate in MT! #define ELOG(x) // RLOG(GetSysTime() << ": " << x) template<> unsigned GetHashValue(const HWND& h) { return (unsigned)(intptr_t)h; } bool Ctrl::GetMsg(MSG& msg) { if(!PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) return false; return IsWindowUnicode(msg.hwnd) ? PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE) : PeekMessage(&msg, NULL, 0, 0, PM_REMOVE); } static bool sFinished; bool IsExiting() { return sFinished; } static BOOL CALLBACK sDumpWindow(HWND hwnd, LPARAM lParam) { String dump; dump << (IsWindowEnabled(hwnd) ? "ena" : "dis") << ' ' << (IsWindowVisible(hwnd) ? "vis" : "hid") << ' ' << Sprintf("owner=0x%x ", GetWindow(hwnd, GW_OWNER)); Ctrl *ctrl = Ctrl::CtrlFromHWND(hwnd); if(ctrl) { #ifdef _DEBUG dump << "Ctrl: " << UPP::Name(ctrl); #endif } else if(!lParam) return TRUE; else { #ifdef PLATFORM_WINCE wchar clsname[256], title[1024]; #else char clsname[256], title[1024]; #endif ::GetClassName(hwnd, clsname, __countof(clsname)); ::GetWindowText(hwnd, title, __countof(title)); dump << "HWND: " << Sprintf("0x%x", hwnd) << ", class = " << clsname << ", title = " << title; } LLOG(dump); return TRUE; } void DumpWindowOrder(bool aliens) { #ifndef PLATFORM_WINCE LLOG("DumpWindowOrder" << LOG_BEGIN); EnumChildWindows(NULL, &sDumpWindow, (LPARAM)(aliens ? 1 : 0)); LLOG(LOG_END << "//DumpWindowOrder"); #endif } #ifndef PLATFORM_WINCE Point GetMousePos() { Point p; return ::GetCursorPos(p) ? p : Null; ::GetCursorPos(p); return p; } #endif HCURSOR Ctrl::hCursor; HINSTANCE Ctrl::hInstance; #ifndef flagDLL #ifndef PLATFORM_WINCE HANDLE Ctrl::OverwatchThread; HWND Ctrl::OverwatchHWND; Win32Event Ctrl::OverwatchEndSession; Win32Event Ctrl::ExitLoopEvent; #endif #endif bool Ctrl::endsession; void Ctrl::EndSession() { GuiLock __; EndSessionLoopNo = EventLoopNo; endsession = true; } template void AutoCast(U& a, V b) { a = (U)b; } #ifndef flagDLL #ifndef PLATFORM_WINCE LRESULT CALLBACK Ctrl::OverwatchWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { if(msg == WM_USER) { ELOGW("WM_USER"); PostQuitMessage(0); } if(msg == WM_QUERYENDSESSION) { static BOOL (WINAPI *ShutdownBlockReasonCreate)(HWND hWnd, LPCWSTR pwszReason); static BOOL (WINAPI *ShutdownBlockReasonDestroy)(HWND hWnd); ONCELOCK { if(HMODULE hDLL = LoadLibrary ("user32")) { AutoCast(ShutdownBlockReasonCreate, GetProcAddress(hDLL, "ShutdownBlockReasonCreate")); AutoCast(ShutdownBlockReasonDestroy, GetProcAddress(hDLL, "ShutdownBlockReasonCreate")); } } if(ShutdownBlockReasonCreate) ShutdownBlockReasonCreate(hwnd, ~WString(t_("waiting for user response"))); EndSession(); ELOGW("WM_QUERYENDSESSION 1"); OverwatchEndSession.Wait(); if(ShutdownBlockReasonDestroy) ShutdownBlockReasonDestroy(hwnd); ELOGW("WM_QUERYENDSESSION 2"); return TRUE; } if(msg == WM_ENDSESSION) { EndSession(); ELOGW("WM_ENDSESSION 1"); ExitLoopEvent.Set(); ELOGW("WM_ENDSESSION 2"); } return DefWindowProc(hwnd, msg, wParam, lParam); } DWORD WINAPI Ctrl::Win32OverwatchThread(LPVOID) { WNDCLASS wc; Zero(wc); wc.style = CS_DBLCLKS|CS_HREDRAW|CS_VREDRAW; wc.lpfnWndProc = (WNDPROC)OverwatchWndProc; wc.hInstance = hInstance; wc.hCursor = NULL; wc.hbrBackground = NULL; wc.lpszClassName = "UPP-OVERWATCH"; RegisterClass(&wc); OverwatchHWND = CreateWindowEx(0, "UPP-OVERWATCH", "", WS_OVERLAPPEDWINDOW, -1000, -1000, 50, 50, NULL, NULL, hInstance, NULL); ELOGW("OverWatch 1"); ExitLoopEvent.Set(); ELOGW("OverWatch 2"); MSG Msg; while(GetMessage(&Msg, NULL, 0, 0) > 0) { TranslateMessage(&Msg); if(IsWindowUnicode(Msg.hwnd)) DispatchMessageW(&Msg); else DispatchMessage(&Msg); } ELOGW("OverWatch 3"); return 0; } #endif #endif HWND utilityHWND = 0; extern VectorMap& sClipMap(); INITBLOCK { sClipMap(); } EXITBLOCK { if(utilityHWND) DestroyWindow(utilityHWND); } LRESULT CALLBACK Ctrl::UtilityProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { sClipMap(); switch(message) { case WM_TIMER: TimerProc(GetTickCount()); AnimateCaret(); break; case WM_RENDERFORMAT: RenderFormat((dword)wParam); return 0; case WM_DESTROYCLIPBOARD: DestroyClipboard(); return 0; } return ::DefWindowProc(hWnd, message, wParam, lParam); } #ifdef PLATFORM_WINCE #define L_(x) L##x #else #define L_(x) x #endif static DWORD sMainThreadId; void WakeUpGuiThread() { ::PostThreadMessage(sMainThreadId, WM_NULL, 0, 0); } void AvoidPaintingCheck__(); static void Win32PanicMessageBox(const char *title, const char *text) { AvoidPaintingCheck__(); #ifdef PLATFORM_WINCE static wchar wtext[256], wtitle[256]; ToUnicode(wtext, text, strlen(text), CHARSET_DEFAULT); ToUnicode(wtitle, title, strlen(title), CHARSET_DEFAULT); MessageBox(::GetActiveWindow(), wtext, wtitle, MB_ICONSTOP | MB_OK | MB_APPLMODAL); #else MessageBox(::GetActiveWindow(), text, title, MB_ICONSTOP | MB_OK | MB_APPLMODAL); #endif } void Ctrl::InstallPanicBox() { InstallPanicMessageBox(&Win32PanicMessageBox); } void Ctrl::InitWin32(HINSTANCE hInstance) { GuiLock __; LLOG("InitWin32"); InstallPanicMessageBox(&Win32PanicMessageBox); // RLOGBLOCK("Ctrl::InitWin32"); sMainThreadId = GetCurrentThreadId(); #define ILOG(x) // RLOG(x) Ctrl::hInstance = hInstance; ILOG("RegisterClassW"); #ifndef PLATFORM_WINCE if(IsWinNT()) #endif { WNDCLASSW wc; Zero(wc); wc.style = CS_DBLCLKS|CS_HREDRAW|CS_VREDRAW; wc.lpfnWndProc = (WNDPROC)Ctrl::WndProc; wc.hInstance = hInstance; wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = IsWinVista() ? (HBRUSH)(COLOR_WINDOW+1) : (HBRUSH)NULL; wc.lpszClassName = L"UPP-CLASS-W"; RegisterClassW(&wc); wc.style = 0x20000|CS_DBLCLKS|CS_HREDRAW|CS_VREDRAW; wc.lpszClassName = L"UPP-CLASS-DS-W"; RegisterClassW(&wc); wc.style = CS_SAVEBITS|CS_DBLCLKS|CS_HREDRAW|CS_VREDRAW; wc.lpszClassName = L"UPP-CLASS-SB-W"; RegisterClassW(&wc); wc.style = 0x20000|CS_DBLCLKS|CS_HREDRAW|CS_VREDRAW|CS_SAVEBITS; wc.lpszClassName = L"UPP-CLASS-SB-DS-W"; RegisterClassW(&wc); } ILOG("RegisterClassA"); WNDCLASS wc; Zero(wc); wc.style = CS_DBLCLKS|CS_HREDRAW|CS_VREDRAW; wc.lpfnWndProc = (WNDPROC)Ctrl::WndProc; wc.hInstance = hInstance; wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = IsWinVista() ? (HBRUSH)(COLOR_WINDOW+1) : (HBRUSH)NULL; wc.lpszClassName = L_("UPP-CLASS-A"); RegisterClass(&wc); if(IsWinXP()) { wc.style = 0x20000|CS_DBLCLKS|CS_HREDRAW|CS_VREDRAW; wc.lpszClassName = L_("UPP-CLASS-DS-A"); RegisterClass(&wc); } wc.style = CS_SAVEBITS|CS_DBLCLKS|CS_HREDRAW|CS_VREDRAW; wc.lpszClassName = L_("UPP-CLASS-SB-A"); RegisterClass(&wc); if(IsWinXP()) { wc.style = 0x20000|CS_DBLCLKS|CS_HREDRAW|CS_VREDRAW|CS_SAVEBITS; wc.lpszClassName = L_("UPP-CLASS-SB-DS-A"); RegisterClass(&wc); } wc.style = 0; wc.lpszClassName = L_("UPP-TIMER"); wc.hCursor = NULL; wc.lpfnWndProc = &Ctrl::UtilityProc; RegisterClass(&wc); ILOG("InitTimer"); InitTimer(); ILOG("SetTimer"); utilityHWND = CreateWindow(L_("UPP-TIMER"), L_(""), WS_OVERLAPPED, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL); SetTimer(utilityHWND, 1, 10, NULL); ILOG("Windows"); Windows(); //?? TRC: what's the use of this? ReSkin(); OleInitialize(NULL); /* TRC 05/11/14: moved to GuiSleep to avoid thread creation in OCX DllMain DWORD dummy; OverwatchThread = CreateThread(NULL, 0x100000, Win32OverwatchThread, NULL, 0, &dummy); ExitLoopEvent().Wait(); */ // TRC 05/11/18: pSetLayeredWindowAttributes moved to GLOBAL_VAR (see below) to make OCX initialization simpler Csizeinit(); #undef ILOG if(IsWin7()) GlobalBackPaint(); } typedef DWORD (WINAPI *PSLWA)(HWND, DWORD, BYTE, DWORD); static PSLWA SetLayeredWindowAttributes() { static PSLWA pSet; #ifndef PLATFORM_WINCE static bool inited = false; if(!inited) { inited = true; if(HMODULE hDLL = LoadLibrary ("user32")) pSet = (PSLWA) GetProcAddress(hDLL, "SetLayeredWindowAttributes"); } #endif return pSet; } bool Ctrl::IsAlphaSupported() { return SetLayeredWindowAttributes(); } bool Ctrl::IsCompositedGui() { return false; } void Ctrl::ExitWin32() { RenderAllFormats(); TopWindow::ShutdownWindows(); CloseTopCtrls(); OleUninitialize(); sFinished = true; for(int i = 0; i < hotkey.GetCount(); i++) if(hotkey[i]) UnregisterHotKey(NULL, i); for(int i = 0; i < Windows().GetCount(); i++) { HWND hwnd = Windows().GetKey(i); if(hwnd) ::DestroyWindow(hwnd); } MSG msg; while(GetMsg(msg)) if(msg.message != WM_QUIT) ::PostQuitMessage(0); #ifndef flagDLL #ifndef PLATFORM_WINCE ELOG("ExitWin32 1"); OverwatchEndSession.Set(); ELOG("ExitWin32 2"); PostMessage(OverwatchHWND, WM_USER, 0, 0); ELOG("ExitWin32 3"); LLOG("Waiting for overwatch thread to finish..."); WaitForSingleObject(OverwatchThread, INFINITE); ELOG("ExitWin32 4"); LLOG("...overwatch thread finished"); #endif #endif } void Ctrl::SetTimerGranularity(int ms) { if(ms > 0) SetTimer(utilityHWND, 1, ms, NULL); else KillTimer(utilityHWND, 1); } VectorMap< HWND, Ptr >& Ctrl::Windows() { static VectorMap< HWND, Ptr > map; return map; } Vector Ctrl::GetTopCtrls() { Vector v; VectorMap< HWND, Ptr >& w = Windows(); for(int i = 0; i < w.GetCount(); i++) if(w.GetKey(i) && w[i] && !w[i]->parent) v.Add(w[i]); return v; } void Ctrl::SetMouseCursor(const Image& image) { GuiLock __; #ifndef PLATFORM_WINCE static Image img; if(image.GetSerialId() != img.GetSerialId()) { img = image; HCURSOR hc = SystemDraw::IconWin32(img, true); SetCursor(hc); if(hCursor) DestroyCursor(hCursor); hCursor = hc; } #endif } Ctrl *Ctrl::CtrlFromHWND(HWND hwnd) { GuiLock __; return hwnd ? Windows().Get(hwnd, NULL) : NULL; } HWND Ctrl::GetOwnerHWND() const { GuiLock __; HWND hwnd = GetHWND(); if(!hwnd) return NULL; return GetWindow(hwnd, GW_OWNER); } Ctrl *Ctrl::GetOwner() { GuiLock __; HWND hwnd = GetOwnerHWND(); return hwnd ? CtrlFromHWND(hwnd) : NULL; } Ctrl *Ctrl::GetActiveCtrl() { GuiLock __; if(focusCtrl) return focusCtrl->GetTopCtrl(); HWND actwnd = ::GetActiveWindow(); Vector top = GetTopCtrls(); for(int i = 0; i < top.GetCount(); i++) if(top[i]->IsActiveX() && top[i]->GetHWND()) { LLOG("-> top[" << i << "] = " << FormatIntHex(top[i]->GetHWND())); for(HWND hwnd = top[i]->GetHWND(); hwnd; hwnd = ::GetParent(hwnd)) if(hwnd == actwnd) { LLOG("-> match for " < not found (NULL)"); return NULL; } UDropTarget *NewUDropTarget(Ctrl *); String WindowStyleAsString(dword style, dword exstyle); void Ctrl::Create(HWND parent, DWORD style, DWORD exstyle, bool savebits, int show, bool dropshadow) { GuiLock __; ASSERT_(IsMainThread(), "Window creation can only happen in the main thread"); LLOG("Ctrl::Create(parent = " << (void *)parent << ") in " <hwnd = CreateWindowExW(exstyle, savebits ? dropshadow ? L"UPP-CLASS-SB-DS-W" : L"UPP-CLASS-SB-W" : dropshadow ? L"UPP-CLASS-DS-W" : L"UPP-CLASS-W", L"", style, 0, 0, 0, 0, parent, NULL, hInstance, this); else top->hwnd = CreateWindowW(L"UPP-CLASS-W", L"", WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, parent, NULL, hInstance, this); #else if(IsWinNT() && (!parent || IsWindowUnicode(parent))) top->hwnd = CreateWindowExW(exstyle, savebits ? dropshadow ? L"UPP-CLASS-SB-DS-W" : L"UPP-CLASS-SB-W" : dropshadow ? L"UPP-CLASS-DS-W" : L"UPP-CLASS-W", L"", style, 0, 0, 0, 0, parent, NULL, hInstance, this); else top->hwnd = CreateWindowEx(exstyle, savebits ? dropshadow ? "UPP-CLASS-SB-DS-A" : "UPP-CLASS-SB-A" : dropshadow ? "UPP-CLASS-DS-A" : "UPP-CLASS-A", "", style, 0, 0, 0, 0, parent, NULL, hInstance, this); #endif inloop = false; ASSERT(top->hwnd); ::MoveWindow(top->hwnd, r.left, r.top, r.Width(), r.Height(), false); // To avoid "black corners" artifact effect ::ShowWindow(top->hwnd, visible ? show : SW_HIDE); // ::UpdateWindow(hwnd); StateH(OPEN); LLOG(LOG_END << "//Ctrl::Create in " <hwnd, (LPDROPTARGET) (top->dndtgt = NewUDropTarget(this))); CancelMode(); RefreshLayoutDeep(); } void ReleaseUDropTarget(UDropTarget *dt); void Ctrl::WndFree() { GuiLock __; if(!top) return; RevokeDragDrop(GetHWND()); ReleaseUDropTarget(top->dndtgt); isopen = false; if(!top) return; HWND owner = GetWindow(top->hwnd, GW_OWNER);// CXL 31.10.2003 z DoRemove bool focus = ::GetFocus() == top->hwnd; LLOG("Ctrl::WndDestroy owner " << (void *)owner << " focus " << focus << " ::GetFocus() " << (void *)::GetFocus()); if(owner && focus) { // CXL 7.11.2003 presun - melo by to fungovat take a neblikat... LLOG("Ctrl::WndFree->SetFocus " << UPP::Name(Ctrl::CtrlFromHWND(owner))); ::SetFocus(owner); } LLOG(LOG_END << "//Ctrl::WndFree() in " <hwnd) { HWND hwnd = top->hwnd; WndFree(); // CXL 2007-06-04 to avoid loosing focus with maximize box owned dialogs bool result = ::DestroyWindow(hwnd); } } Image Ctrl::DoMouse(int e, Point p, int zd) { // LLOG("Ctrl::DoMouse(" << p << ", " << e << ")"); GuiLock __; eventCtrl = this; Image img = DispatchMouse(e, p, zd); SyncCaret(); return img; } #ifdef _DEBUG bool Ctrl::LogMessages; #define x_MSG(x) { x, #x }, struct WinMsg { int ID; const char *name; } sWinMsg[] = { #include "Win32Msg.i" {0, NULL} }; #endif void Ctrl::NcCreate(HWND hwnd) { GuiLock __; if(!parent) top->hwnd = hwnd; } void Ctrl::NcDestroy() { GuiLock __; if(!parent) WndFree(); } bool Ctrl::PreprocessMessage(MSG& msg) { return false; } LRESULT CALLBACK Ctrl::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { GuiLock __; if(sFinished) return DefWindowProc(hWnd, message, wParam, lParam); #ifdef PLATFORM_WINCE if(message == WM_CREATE) #else if(message == WM_NCCREATE) #endif { Ctrl *w = (Ctrl *)((LPCREATESTRUCT) lParam)->lpCreateParams; if(w) { w->NcCreate(hWnd); int i = Windows().Find(NULL); if(i >= 0) { Windows().SetKey(i, hWnd); Windows()[i] = w; } else Windows().Add(hWnd) = w; } } Ctrl *w = Windows().Get(hWnd, NULL); #ifdef PLATFORM_WINCE if(message == WM_DESTROY) #else if(message == WM_NCDESTROY) #endif { if(w) w->NcDestroy(); int i = Windows().Find(hWnd); if(i >= 0) Windows().SetKey(i, NULL); } #if LOGMESSAGES bool logblk = false; if(message != WM_SETCURSOR && message != WM_CTLCOLORBTN && message != WM_TIMER && #ifndef PLATFORM_WINCE message != WM_NCHITTEST && message != WM_ENTERIDLE && #endif message != WM_CTLCOLORDLG && message != WM_CTLCOLOREDIT && message != WM_CTLCOLORLISTBOX && message != WM_CTLCOLORMSGBOX && message != WM_CTLCOLORSCROLLBAR && message != WM_CTLCOLORSTATIC && message != WM_CANCELMODE && message != 0x0118) for(WinMsg *m = sWinMsg; m->ID; m++) if(m->ID == message) { RLOG(m->name << ' ' << UPP::Name(w) << Sprintf(", wParam = %d (0x%x), lParam = %d (0x%x)", wParam, wParam, lParam, lParam)); VppLog() << LOG_BEGIN; logblk = true; break; } #endif LRESULT l = 0; if(w) { #if defined(_DEBUG) && LOGTIMING int ticks = msecs(); String wname = w->Name(); #endif Ptr pw = w; l = w->WindowProc(message, wParam, lParam); if(pw) pw->SyncMoves(); #if defined(_DEBUG) && LOGTIMING String msgname; for(WinMsg *m = sWinMsg; m->ID; m++) if(m->ID == message) { msgname = m->name; break; } if(IsNull(msgname)) msgname = NFormat("0x%04x", (int)message); LLOG(NFormat("T+%d %s 0x%08x 0x%08x -> %s", msecs(ticks), msgname, (int)wParam, (int)lParam, wname)); #endif } else l = DefWindowProc(hWnd, message, wParam, lParam); #if LOGMESSAGES if(logblk) VppLog() << LOG_END; #endif return l; } bool PassWindowsKey(int wParam) { return wParam >= VK_NUMPAD0 && wParam <= VK_NUMPAD9 || wParam == VK_INSERT || wParam == VK_DELETE || wParam == VK_HOME || wParam == VK_END || wParam == VK_PRIOR || wParam == VK_NEXT || wParam == VK_UP || wParam == VK_DOWN || wParam == VK_LEFT || wParam == VK_RIGHT || wParam == VK_CLEAR || wParam == VK_SPACE || wParam >= 0x90; // OEM keys } Vector > Ctrl::hotkey; int Ctrl::RegisterSystemHotKey(dword key, Function cb) { ASSERT(key >= K_DELTA); int q = hotkey.GetCount(); for(int i = 0; i < hotkey.GetCount(); i++) if(!hotkey[i]) { q = i; break; } hotkey.At(q) = Event<> () << cb; dword mod = 0; if(key & K_ALT) mod |= MOD_ALT; if(key & K_SHIFT) mod |= MOD_SHIFT; if(key & K_CTRL) mod |= MOD_CONTROL; return RegisterHotKey(NULL, q, mod, key & 0xffff) ? q : -1; } void Ctrl::UnregisterSystemHotKey(int id) { if(id >= 0 && id < hotkey.GetCount()) { UnregisterHotKey(NULL, id); hotkey[id].Clear(); } } void Ctrl::sProcessMSG(MSG& msg) { if (msg.message == WM_HOTKEY) { if((int)msg.wParam >= 0 && (int)msg.wParam < Ctrl::hotkey.GetCount()) Ctrl::hotkey[(int)msg.wParam](); return; } if(!DHCtrl::PreprocessMessageAll(msg)) if(msg.message != WM_SYSKEYDOWN && msg.message != WM_SYSKEYUP || PassWindowsKey((dword)msg.wParam) || msg.wParam == VK_MENU) //17.11 Mirek - fix to get windows menu invoked on Alt+Space TranslateMessage(&msg); // 04/09/07: TRC fix to make barcode reader going better #if 0 DDUMP(msg.hwnd); for(WinMsg *m = sWinMsg; m->ID; m++) if(m->ID == msg.message) { RLOG(m->name << ' ' << Sprintf(", wParam = %d (0x%x), lParam = %d (0x%x)", msg.wParam, msg.wParam, msg.lParam, msg.lParam)); break; } char cls[200]; GetClassName(msg.hwnd, cls, 200); DDUMP(cls); #endif if(IsWindowUnicode(msg.hwnd)) DispatchMessageW(&msg); else DispatchMessage(&msg); } bool Ctrl::IsWaitingEvent() { ASSERT_(IsMainThread(), "IsWaitingEvent can only run in the main thread"); MSG msg; return PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE); } bool Ctrl::ProcessEvent(bool *quit) { ASSERT_(IsMainThread(), "ProcessEvent can only run in the main thread"); if(!GetMouseLeft() && !GetMouseRight() && !GetMouseMiddle()) ReleaseCtrlCapture(); MSG msg; if(GetMsg(msg)) { if(msg.message == WM_QUIT && quit) *quit = true; // LLOG(GetSysTime() << " % " << (unsigned)msecs() % 10000 << ": sProcessMSG " << FormatIntHex(msg.message)); sProcessMSG(msg); // LLOG(GetSysTime() << " % " << (unsigned)msecs() % 10000 << ": //sProcessMSG " << FormatIntHex(msg.message)); DefferedFocusSync(); SyncCaret(); return true; } return false; } void Ctrl::SysEndLoop() { } bool Ctrl::ProcessEvents(bool *quit) { ASSERT_(IsMainThread(), "ProcessEvents can only run in the main thread"); if(ProcessEvent(quit)) { while(ProcessEvent(quit) && (!LoopCtrl || LoopCtrl->InLoop())); // LoopCtrl-MF 071008 SweepMkImageCache(); return true; } SweepMkImageCache(); return false; } void Ctrl::EventLoop(Ctrl *ctrl) { GuiLock __; ASSERT_(IsMainThread(), "EventLoop can only run in the main thread"); ASSERT(LoopLevel == 0 || ctrl); LoopLevel++; LLOG("Entering event loop at level " << LoopLevel << LOG_BEGIN); Ptr ploop; if(ctrl) { ploop = LoopCtrl; LoopCtrl = ctrl; ctrl->inloop = true; } bool quit = false; int64 loopno = ++EventLoopNo; ProcessEvents(&quit); while(loopno > EndSessionLoopNo && !quit && (ctrl ? ctrl->IsOpen() && ctrl->InLoop() : GetTopCtrls().GetCount())) { // LLOG(GetSysTime() << " % " << (unsigned)msecs() % 10000 << ": EventLoop / GuiSleep"); SyncCaret(); GuiSleep(1000); // LLOG(GetSysTime() << " % " << (unsigned)msecs() % 10000 << ": EventLoop / ProcessEvents"); ProcessEvents(&quit); // LLOG(GetSysTime() << " % " << (unsigned)msecs() % 10000 << ": EventLoop / after ProcessEvents"); } if(ctrl) LoopCtrl = ploop; LoopLevel--; LLOG(LOG_END << "Leaving event loop "); } void Ctrl::GuiSleep(int ms) { GuiLock __; ASSERT_(IsMainThread(), "Only the main thread can perform GuiSleep"); ELOG("GuiSleep"); int level = LeaveGuiMutexAll(); #if !defined(flagDLL) && !defined(PLATFORM_WINCE) if(!OverwatchThread) { DWORD dummy; OverwatchThread = CreateThread(NULL, 0x100000, Win32OverwatchThread, NULL, 0, &dummy); ELOG("ExitLoopEventWait 1"); ExitLoopEvent.Wait(); } HANDLE h[1]; *h = ExitLoopEvent.GetHandle(); ELOG("ExitLoopEventWait 2 " << (void *)*h); MsgWaitForMultipleObjects(1, h, FALSE, ms, QS_ALLINPUT); #else MsgWaitForMultipleObjects(0, NULL, FALSE, ms, QS_ALLINPUT); #endif EnterGuiMutex(level); } #if 0 void Ctrl::WndDestroyCaret() { DLOG("Ctrl::WndDestroyCaret()"); ::DestroyCaret(); } void Ctrl::WndCreateCaret(const Rect& cr) { GuiLock __; DLOG("Ctrl::WndCreateCaret(" << cr << ") in " << UPP::Name(this)); HWND hwnd = GetHWND(); if(!hwnd) return; Rect r; ::GetClientRect(hwnd, r); Point p = r.TopLeft(); ::ClientToScreen(hwnd, p); ::CreateCaret(hwnd, NULL, cr.Width(), cr.Height()); ::SetCaretPos(cr.left - p.x, cr.top - p.y); ::ShowCaret(hwnd); } #else int Ctrl::WndCaretTime; bool Ctrl::WndCaretVisible; void Ctrl::AnimateCaret() { GuiLock __; bool v = !(((GetTickCount() - WndCaretTime) / GetCaretBlinkTime()) & 1); if(v != WndCaretVisible) { WndCaretVisible = v; RefreshCaret(); } } void Ctrl::PaintCaret(SystemDraw& w) { GuiLock __; LLOG("PaintCaret " << Name() << ", caretCtrl: " << caretCtrl << ", WndCaretVisible: " << WndCaretVisible); if(this == caretCtrl && WndCaretVisible) w.DrawRect(caretx, carety, caretcx, caretcy, InvertColor); } void Ctrl::SetCaret(int x, int y, int cx, int cy) { GuiLock __; LLOG("SetCaret " << Name() << " " << RectC(x, y, cx, cy)); if(this == caretCtrl) RefreshCaret(); caretx = x; carety = y; caretcx = cx; caretcy = cy; if(this == caretCtrl) { WndCaretTime = GetTickCount(); RefreshCaret(); AnimateCaret(); } } void Ctrl::SyncCaret() { GuiLock __; LLOG("SyncCaret"); if(focusCtrl != caretCtrl) { LLOG("SyncCaret DO " << Upp::Name(caretCtrl) << " -> " << Upp::Name(focusCtrl)); RefreshCaret(); caretCtrl = focusCtrl; RefreshCaret(); } } #endif Rect Ctrl::GetWndScreenRect() const { GuiLock __; HWND hwnd = GetHWND(); if(!hwnd) return Null; Rect r; ::GetWindowRect(hwnd, r); return r; } void Ctrl::WndShow(bool b) { GuiLock __; HWND hwnd = GetHWND(); if(hwnd) ::ShowWindow(hwnd, b ? SW_SHOW : SW_HIDE); } void Ctrl::WndUpdate() { GuiLock __; HWND hwnd = GetHWND(); if(hwnd) ::UpdateWindow(hwnd); } bool Ctrl::IsWndOpen() const { GuiLock __; return GetHWND(); } void Ctrl::SetAlpha(byte alpha) { GuiLock __; HWND hwnd = GetHWND(); if(!IsAlphaSupported() || parent || !top || !hwnd) return; if(alpha == 255) { SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) & ~0x80000); return; } SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) | 0x80000); SetLayeredWindowAttributes() (hwnd, 0, alpha, 2); } #define DLLFILENAME "User32.dll" #define DLIMODULE MultiMon #define DLIHEADER #include Rect MonitorRectForHWND(HWND hwnd) { if(hwnd && MultiMon()) if(HMONITOR monitor = MultiMon().MonitorFromWindow(hwnd, 2/*MONITOR_DEFAULTTONEAREST*/)) { MONITORINFO moninfo; Zero(moninfo); moninfo.cbSize = sizeof(moninfo); MultiMon().GetMonitorInfo(monitor, &moninfo); return Rect(moninfo.rcWork); } return Ctrl::GetPrimaryWorkArea(); } Rect Ctrl::GetWorkArea() const { // return MonitorRectForHWND(GetHWND()); // mst:2008-12-08, hack for better multimonitor support. GuiLock __; const Ctrl *topctl = GetTopCtrl(); HWND hwnd = topctl->GetHWND(); if(!hwnd && !((topctl = topctl->GetOwnerCtrl()) && (hwnd = topctl->GetHWND()))) hwnd = ::GetFocus(); return MonitorRectForHWND(hwnd); } static BOOL CALLBACK sMonEnumProc(HMONITOR monitor, HDC hdc, LPRECT lprcMonitor, LPARAM data) { MONITORINFO moninfo; Zero(moninfo); moninfo.cbSize = sizeof(moninfo); MultiMon().GetMonitorInfo(monitor, &moninfo); ((Array *)data)->Add(Rect(moninfo.rcWork)); return TRUE; } void Ctrl::GetWorkArea(Array& rc) { GuiLock __; MultiMon().EnumDisplayMonitors(NULL, NULL, &sMonEnumProc, (LPARAM)&rc); } Rect Ctrl::GetVirtualWorkArea() { Rect out = GetPrimaryWorkArea(); Array rc; GetWorkArea(rc); for(int i = 0; i < rc.GetCount(); i++) out |= rc[i]; return out; } Rect Ctrl::GetVirtualScreenArea() { GuiLock __; return RectC(GetSystemMetrics(SM_XVIRTUALSCREEN), GetSystemMetrics(SM_YVIRTUALSCREEN), GetSystemMetrics(SM_CXVIRTUALSCREEN), GetSystemMetrics(SM_CYVIRTUALSCREEN)); } Rect Ctrl::GetPrimaryWorkArea() { Rect r; SystemParametersInfo(SPI_GETWORKAREA, 0, &r, 0); LLOG("Ctrl::GetWorkArea -> " << r); return r; } Rect Ctrl::GetPrimaryScreenArea() { return Size(GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN)); } int Ctrl::GetKbdDelay() { GuiLock __; #ifdef PLATFORM_WINCE return 500; #else int a; SystemParametersInfo(SPI_GETKEYBOARDDELAY, 0, &a, 0); return 250 + a * 750 / 4; #endif } int Ctrl::GetKbdSpeed() { GuiLock __; #ifdef PLATFORM_WINCE return 1000 / 32; #else int a; SystemParametersInfo(SPI_GETKEYBOARDSPEED, 0, &a, 0); return 1000 / (a + 2); #endif } void Ctrl::SetWndForeground() { GuiLock __; LLOG("Ctrl::SetWndForeground() in " << UPP::Name(this)); HWND hwnd = GetHWND(); if(hwnd) ::SetForegroundWindow(hwnd); } bool Ctrl::IsWndForeground() const { GuiLock __; HWND hwnd = GetHWND(); if(!hwnd) return false; HWND fore = ::GetForegroundWindow(); LLOG("Ctrl::IsWndForeground(): hwnd = " << (void *)hwnd << ", fore = " << (void *)fore << " - " << UPP::Name(CtrlFromHWND(fore))); if(IsActiveX()) { while(hwnd && hwnd != fore && !!::GetParent(hwnd)) hwnd = ::GetParent(hwnd); } return hwnd == fore; } void Ctrl::WndEnable(bool b) { GuiLock __; LLOG("Ctrl::WndEnable(" << (b && *b) << ") in " << UPP::Name(this) << ", focusCtrlWnd = " << UPP::Name(~focusCtrlWnd) << ", raw = " << (void *)::GetFocus()); if(b) ReleaseCapture(); LLOG("//Ctrl::WndEnable(" << (b && *b) << ") -> false " < ::SetFocus(" << (void *)hwnd << ")"); // ::SetActiveWindow(hwnd); ::SetFocus(hwnd); return true; } LLOG("//Ctrl::SetWndFocus() in " <IsOpen()) { HWND hwnd = top->GetHWND(); HDC hdc = GetDC(hwnd); HRGN hrgn = CreateRectRgn(0, 0, 0, 0); if(GetUpdateRgn(hwnd, hrgn, FALSE) != NULLREGION) { SelectClipRgn(hdc, hrgn); SystemDraw draw(hdc); bool hcr = focusCtrl && focusCtrl->GetTopCtrl() == top && caretRect.Intersects(r + top->GetRect().TopLeft()); if(hcr) ::HideCaret(hwnd); draw.Clip(r); top->UpdateArea(draw, r); ValidateRect(hwnd, r); SelectClipRgn(hdc, NULL); if(hcr) ::ShowCaret(hwnd); } ReleaseDC(hwnd, hdc); DeleteObject(hrgn); } } void Ctrl::WndScrollView(const Rect& r, int dx, int dy) { GuiLock __; LLOG("WndScrollView " << UPP::Name(this)); if(caretCtrl && caretCtrl->GetTopCtrl() == this) { #if WINCARET WndDestroyCaret(); #else RefreshCaret(); #endif caretRect.Clear(); } #ifdef PLATFORM_WINCE ::ScrollWindowEx(GetHWND(), dx, dy, r, r, NULL, NULL, 0); #else ::ScrollWindow(GetHWND(), dx, dy, r, r); #endif SyncCaret(); } void Ctrl::PopUpHWND(HWND owner, bool savebits, bool activate, bool dropshadow, bool topmost) { LLOG("PopoUp " << UPP::Name(this)); popup = false; Create(owner, WS_POPUP, topmost ? WS_EX_TOPMOST : 0, savebits, owner || !activate ? SW_SHOWNOACTIVATE : SW_SHOW, dropshadow); HWND hwnd = GetHWND(); if(hwnd) popup = true; if(activate) SetFocus(); } void Ctrl::PopUp(Ctrl *owner, bool savebits, bool activate, bool dropshadow, bool topmost) { popup = false; Ctrl *q = owner ? owner->GetTopCtrl() : GetActiveCtrl(); PopUpHWND(q ? q->GetHWND() : NULL, savebits, activate, dropshadow, topmost); if(top) top->owner = owner; } Rect Ctrl::GetScreenClient(HWND hwnd) { Rect r; ::GetClientRect(hwnd, r); Point tl = r.TopLeft(); Point br = r.BottomRight(); ::ClientToScreen(hwnd, tl); ::ClientToScreen(hwnd, br); LLOG("Ctrl::GetScreenClient: hwnd = " << FormatPtr(hwnd) << ", client = " << r << ", screen(tl) = " << tl << ", screen(br) = " << br); return Rect(tl, br); } Rect Ctrl::GetDefaultWindowRect() { #ifdef PLATFORM_WINCE return Rect(0, 0, 100, 100); #else HWND hwnd = ::CreateWindow("UPP-CLASS-A", "", WS_OVERLAPPED, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, NULL, NULL); Rect sr; if(hwnd) { ::ShowWindow(hwnd, SW_HIDE); sr = GetScreenClient(hwnd); ::DestroyWindow(hwnd); } else sr = RectC(20, 20, 500, 350); return sr; #endif } ViewDraw::ViewDraw(Ctrl *ctrl) { EnterGuiMutex(); Ctrl *top = ctrl->GetTopCtrl(); hwnd = top->GetHWND(); ASSERT(hwnd); Attach(GetDC(hwnd)); Clipoff(ctrl->GetScreenView() - top->GetScreenRect().TopLeft()); } ViewDraw::~ViewDraw() { End(); HDC hdc = Detach(); if(hwnd && hdc) ReleaseDC(hwnd, hdc); LeaveGuiMutex(); } Vector SplitCmdLine__(const char *cmd) { Vector out; while(*cmd) if((byte)*cmd <= ' ') cmd++; else if(*cmd == '\"') { WString quoted; while(*++cmd && (*cmd != '\"' || *++cmd == '\"')) quoted.Cat(FromSystemCharset(String(cmd, 1)).ToWString()); out.Add(quoted); } else { const char *begin = cmd; while((byte)*cmd > ' ') cmd++; out.Add(String(begin, cmd).ToWString()); } return out; } } #endif