/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ // Original file Copyright Crytek GMBH or its affiliates, used under license. #include "StdAfx.h" #include "Win32GUI.h" #include #include #include #include #include "commctrl.h" #include "StringHelpers.h" namespace Win32GUI { const int WM_REFLECT_BASE = WM_USER + 0x1c00; const int WM_COMMAND_REFLECT = WM_REFLECT_BASE + WM_COMMAND; const int WM_NOTIFY_REFLECT = WM_REFLECT_BASE + WM_NOTIFY; class Window { public: typedef LRESULT (Window::* WindowProcMethod)(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); Window(WindowProcMethod windowProc); ~Window(); static LRESULT CALLBACK StaticWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); LRESULT WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); LRESULT FrameWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); void Subclass(HWND window); void Unsubclass(HWND window); WNDPROC m_oldWndProc; WindowProcMethod m_windowProc; typedef std::multimap CallbackMap; CallbackMap m_callbackMap; }; } void Win32GUI::Initialize() { InitCommonControls(); LoadLibrary(_T("riched20.dll")); } void Win32GUI::RegisterFrameClass(const TCHAR* name) { WNDCLASS cls; std::memset(&cls, 0, sizeof(cls)); cls.style = 0; cls.lpfnWndProc = &Window::StaticWindowProc; cls.cbClsExtra = 0; cls.cbWndExtra = 0; cls.hInstance = GetModuleHandle(0); cls.hIcon = 0; cls.hCursor = LoadCursor(0, IDC_ARROW); cls.hbrBackground = GetSysColorBrush(COLOR_BTNFACE); cls.lpszMenuName = 0; cls.lpszClassName = name; RegisterClass(&cls); } HWND Win32GUI::CreateFrame(const TCHAR* className, unsigned style, int width, int height) { Window* window = new Window(&Window::FrameWindowProc); HWND hwnd = CreateWindow( className, // LPCTSTR lpClassName, TEXT(""), // LPCTSTR lpWindowName, style, // DWORD dwStyle, CW_USEDEFAULT, // int x, CW_USEDEFAULT, // int y, width, // int nWidth, height, // int nHeight, 0, // HWND hWndParent, 0, // HMENU hMenu, GetModuleHandle(0), // HINSTANCE hInstance, window); // LPVOID lpParam return hwnd; } HWND Win32GUI::CreateControl(const TCHAR* className, unsigned style, HWND parent, int left, int top, int width, int height) { Window* window = new Window(&Window::WindowProc); HWND hwnd = CreateWindow( className, // LPCTSTR lpClassName, 0, // LPCTSTR lpWindowName, style | WS_CHILD | WS_VISIBLE, // DWORD dwStyle, left, // int x, top, // int y, width, // int nWidth, height, // int nHeight, parent, // HWND hWndParent, 0, // HMENU hMenu, GetModuleHandle(0), // HINSTANCE hInstance, 0); // LPVOID lpParam window->Subclass(hwnd); return hwnd; } int Win32GUI::Run() { MSG msg; BOOL status; while ((status = GetMessage(&msg, HWND(0), UINT(0), UINT(0))) != 0) { if (status == -1) { return -1; } else { TranslateMessage(&msg); DispatchMessage(&msg); } } return (int)msg.wParam; } Win32GUI::Window::Window(WindowProcMethod windowProc) : m_windowProc(windowProc) , m_oldWndProc(0) { } Win32GUI::Window::~Window() { for (CallbackMap::iterator callbackPos = m_callbackMap.begin(); callbackPos != m_callbackMap.end(); ++callbackPos) { (*callbackPos).second->Release(); } } LRESULT CALLBACK Win32GUI::Window::StaticWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { Window* window = 0; { if (uMsg == WM_CREATE) { CREATESTRUCT* create_struct = (CREATESTRUCT*)lParam; window = (Window*)create_struct->lpCreateParams; #if defined(_WIN64) SetWindowLongPtr(hwnd, GWLP_USERDATA, LONG_PTR(window)); #else //defined(_WIN64) SetWindowLong(hwnd, GWL_USERDATA, PtrToLong(window)); #endif //defined(_WIN64) } else { #if defined(_WIN64) window = (Window*)GetWindowLongPtr(hwnd, GWLP_USERDATA); #else //defined(_WIN64) window = (Window*)LongToPtr(GetWindowLong(hwnd, GWL_USERDATA)); #endif //defined(_WIN64) } } LRESULT result = 0; if (window) { result = (window->*(window->m_windowProc))(hwnd, uMsg, wParam, lParam); } else { result = DefWindowProc(hwnd, uMsg, wParam, lParam); } if (uMsg == WM_DESTROY) { delete window; #if defined(_WIN64) SetWindowLongPtr(hwnd, GWLP_USERDATA, 0); #else //defined(_WIN64) SetWindowLong(hwnd, GWL_USERDATA, 0); #endif //defined(_WIN64) } return result; } LRESULT Win32GUI::Window::WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_COMMAND: { HWND control = (HWND)lParam; if (control) { SendMessage(control, WM_COMMAND_REFLECT, wParam, lParam); } } break; case WM_NOTIFY: { NMHDR* notify_header = reinterpret_cast(lParam); if (notify_header->hwndFrom) { SendMessage(notify_header->hwndFrom, WM_NOTIFY_REFLECT, wParam, lParam); } } break; case WM_COMMAND_REFLECT: switch (HIWORD(wParam)) { case EN_CHANGE: { std::string text = GetWindowString(hwnd); std::pair range = m_callbackMap.equal_range(EventCallbacks::TextChanged::ID); for (CallbackMap::iterator callbackPos = range.first; callbackPos != range.second; ++callbackPos) { EventCallbacks::IStringCallback* callback = static_cast((*callbackPos).second); callback->Call(text); } } break; case BN_CLICKED: { if (GetWindowLong(hwnd, GWL_STYLE) & BS_CHECKBOX) { bool checked = (0 != SendMessage(hwnd, BM_GETCHECK, 0, 0)); std::pair range = m_callbackMap.equal_range(EventCallbacks::Checked::ID); for (CallbackMap::iterator callbackPos = range.first; callbackPos != range.second; ++callbackPos) { EventCallbacks::IBoolCallback* callback = static_cast((*callbackPos).second); callback->Call(checked); } } else { std::pair range = m_callbackMap.equal_range(EventCallbacks::Pushed::ID); for (CallbackMap::iterator callbackPos = range.first; callbackPos != range.second; ++callbackPos) { EventCallbacks::IVoidCallback* callback = static_cast((*callbackPos).second); callback->Call(); } } } break; } break; case WM_GETMINMAXINFO: { int minW = 0, maxW = 0, minH = 100000, maxH = 100000; std::pair range = m_callbackMap.equal_range(EventCallbacks::GetDimensions::ID); for (CallbackMap::iterator callbackPos = range.first; callbackPos != range.second; ++callbackPos) { EventCallbacks::IGetDimensionsCallback* callback = static_cast((*callbackPos).second); callback->Call(minW, maxW, minH, maxH); } MINMAXINFO* minMaxInfo = (MINMAXINFO*)lParam; minMaxInfo->ptMinTrackSize.x = minW; minMaxInfo->ptMaxTrackSize.x = maxW; minMaxInfo->ptMinTrackSize.y = minH; minMaxInfo->ptMaxTrackSize.y = maxH; } break; case WM_SIZE: { int width = LOWORD(lParam); int height = HIWORD(lParam); std::pair range = m_callbackMap.equal_range(EventCallbacks::SizeChanged::ID); for (CallbackMap::iterator callbackPos = range.first; callbackPos != range.second; ++callbackPos) { EventCallbacks::ISizeCallback* callback = static_cast((*callbackPos).second); callback->Call(width, height); } } break; case WM_NOTIFY_REFLECT: break; } LRESULT result = 0; if (m_oldWndProc) { result = CallWindowProc(m_oldWndProc, hwnd, uMsg, wParam, lParam); } else { result = DefWindowProc(hwnd, uMsg, wParam, lParam); } return result; } LRESULT Win32GUI::Window::FrameWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_CLOSE: PostQuitMessage(0); break; } return WindowProc(hwnd, uMsg, wParam, lParam); } void Win32GUI::Window::Subclass(HWND window) { #if defined(_WIN64) SetWindowLongPtr(window, GWLP_USERDATA, LONG_PTR(this)); m_oldWndProc = (WNDPROC)GetWindowLongPtr(window, GWLP_WNDPROC); SetWindowLongPtr(window, GWLP_WNDPROC, LONG_PTR(&Window::StaticWindowProc)); #else //defined(_WIN64) SetWindowLong(window, GWL_USERDATA, PtrToLong(this)); m_oldWndProc = (WNDPROC)LongToPtr(GetWindowLong(window, GWL_WNDPROC)); SetWindowLong(window, GWL_WNDPROC, PtrToLong(&Window::StaticWindowProc)); #endif //defined(_WIN64) } void Win32GUI::Window::Unsubclass(HWND window) { #if defined(_WIN64) SetWindowLongPtr(window, GWLP_USERDATA, 0); SetWindowLongPtr(window, GWLP_WNDPROC, LONG_PTR(m_oldWndProc)); #else //defined(_WIN64) SetWindowLongPtr(window, GWL_USERDATA, 0); SetWindowLong(window, GWLP_WNDPROC, PtrToLong(m_oldWndProc)); #endif //defined(_WIN64) m_oldWndProc = 0; } std::string Win32GUI::GetWindowString(HWND hwnd) { LRESULT length = SendMessage(hwnd, WM_GETTEXTLENGTH, 0, 0); std::wstring wtext(length, 0); SendMessageW(hwnd, WM_GETTEXT, length + 1, (LPARAM)&wtext[0]); return StringHelpers::ConvertUtf16ToAnsi(wtext.c_str(), '?'); } void Win32GUI::SetWindowString(HWND hwnd, const std::string& text) { std::wstring wtext = StringHelpers::ConvertAnsiToUtf16(text.c_str()); SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)&wtext[0]); } HFONT Win32GUI::CreateFont() { HFONT hFont = 0; HFONT hGuiFont = static_cast(::GetStockObject(DEFAULT_GUI_FONT)); LOGFONT lfGuiFont = { 0 }; if (::GetObject(hGuiFont, sizeof(LOGFONT), &lfGuiFont) == sizeof(LOGFONT)) { _tcsncpy(lfGuiFont.lfFaceName, _T("MS Shell Dlg 2"), sizeof(lfGuiFont.lfFaceName) / sizeof(TCHAR)); lfGuiFont.lfFaceName[(sizeof(lfGuiFont.lfFaceName) / sizeof(TCHAR)) - 1] = '\0'; hFont = ::CreateFontIndirect(&lfGuiFont); return hFont; } return 0; } void Win32GUI::SetCallbackObject(HWND hwnd, unsigned eventID, EventCallbacks::ICallback* callback) { #if defined(_WIN64) Window* window = (Window*)GetWindowLongPtr(hwnd, GWLP_USERDATA); #else //defined(_WIN64) Window* window = (Window*)LongToPtr(GetWindowLong(hwnd, GWL_USERDATA)); #endif //defined(_WIN64) window->m_callbackMap.insert(std::make_pair(eventID, callback)); }