Mercurial > foo_out_sdl
comparison foosdk/sdk/libPPUI/wtl-pp.h @ 1:20d02a178406 default tip
*: check in everything else
yay
| author | Paper <paper@tflc.us> |
|---|---|
| date | Mon, 05 Jan 2026 02:15:46 -0500 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 0:e9bb126753e7 | 1:20d02a178406 |
|---|---|
| 1 #pragma once | |
| 2 // Various WTL extensions that are not fb2k specific and can be reused in other WTL based software | |
| 3 | |
| 4 #include <Uxtheme.h> | |
| 5 #include <functional> | |
| 6 | |
| 7 #define ATLASSERT_SUCCESS(X) {auto RetVal = (X); ATLASSERT( RetVal ); (void) RetVal; } | |
| 8 | |
| 9 #ifdef SubclassWindow // mitigate windowsx.h clash | |
| 10 #undef SubclassWindow | |
| 11 #endif | |
| 12 | |
| 13 class NoRedrawScope { | |
| 14 public: | |
| 15 NoRedrawScope(HWND p_wnd) throw() : m_wnd(p_wnd) { | |
| 16 m_wnd.SetRedraw(FALSE); | |
| 17 } | |
| 18 ~NoRedrawScope() throw() { | |
| 19 m_wnd.SetRedraw(TRUE); | |
| 20 } | |
| 21 private: | |
| 22 CWindow m_wnd; | |
| 23 }; | |
| 24 | |
| 25 class NoRedrawScopeEx { | |
| 26 public: | |
| 27 NoRedrawScopeEx(HWND p_wnd) throw() : m_wnd(p_wnd) { | |
| 28 if (m_wnd.IsWindowVisible()) { | |
| 29 m_active = true; | |
| 30 m_wnd.SetRedraw(FALSE); | |
| 31 } | |
| 32 } | |
| 33 ~NoRedrawScopeEx() throw() { | |
| 34 if (m_active) { | |
| 35 m_wnd.SetRedraw(TRUE); | |
| 36 m_wnd.RedrawWindow(NULL,NULL,RDW_INVALIDATE|RDW_ERASE|RDW_ALLCHILDREN); | |
| 37 } | |
| 38 } | |
| 39 NoRedrawScopeEx(const NoRedrawScopeEx&) = delete; | |
| 40 void operator=(const NoRedrawScopeEx&) = delete; | |
| 41 private: | |
| 42 bool m_active = false; | |
| 43 CWindow m_wnd; | |
| 44 }; | |
| 45 | |
| 46 class NoRedrawControl { | |
| 47 public: | |
| 48 CWindow m_wnd; | |
| 49 | |
| 50 void operator++() { | |
| 51 m_count++; | |
| 52 if (m_wnd.IsWindowVisible()) { | |
| 53 m_active = true; | |
| 54 m_wnd.SetRedraw(FALSE); | |
| 55 } | |
| 56 } | |
| 57 void operator--() { | |
| 58 if (--m_count == 0 && m_active) { | |
| 59 m_wnd.SetRedraw(TRUE); | |
| 60 m_wnd.RedrawWindow(NULL, NULL, RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN); | |
| 61 m_active = false; | |
| 62 } | |
| 63 } | |
| 64 int m_count = 0; | |
| 65 bool m_active = false; | |
| 66 | |
| 67 NoRedrawControl(HWND wnd = NULL) : m_wnd(wnd) {} | |
| 68 void operator=(const NoRedrawControl&) = delete; | |
| 69 NoRedrawControl(const NoRedrawControl&) = delete; | |
| 70 }; | |
| 71 | |
| 72 LRESULT RelayEraseBkgnd(HWND p_from, HWND p_to, HDC p_dc); | |
| 73 void InjectParentEraseHandler(HWND); | |
| 74 void InjectEraseHandler(HWND, HWND sendTo); | |
| 75 void InjectParentCtlColorHandler(HWND); | |
| 76 void BounceNextDlgCtl(HWND wnd, HWND wndTo); | |
| 77 | |
| 78 | |
| 79 | |
| 80 #define MSG_WM_ERASEBKGND_PARENT() \ | |
| 81 if (uMsg == WM_ERASEBKGND) { \ | |
| 82 lResult = ::RelayEraseBkgnd(hWnd, ::GetParent(hWnd), (HDC)wParam); \ | |
| 83 return TRUE; \ | |
| 84 } | |
| 85 | |
| 86 #define MSG_WM_ERASEBKGND_TO(wndTarget) \ | |
| 87 if (uMsg == WM_ERASEBKGND) { \ | |
| 88 lResult = ::RelayEraseBkgnd(hWnd, wndTarget, (HDC)wParam); \ | |
| 89 return TRUE; \ | |
| 90 } | |
| 91 | |
| 92 #define MSG_WM_TIMER_EX(timerId, func) \ | |
| 93 if (uMsg == WM_TIMER && (UINT_PTR)wParam == timerId) \ | |
| 94 { \ | |
| 95 SetMsgHandled(TRUE); \ | |
| 96 func(); \ | |
| 97 lResult = 0; \ | |
| 98 if(IsMsgHandled()) \ | |
| 99 return TRUE; \ | |
| 100 } | |
| 101 | |
| 102 #define MESSAGE_HANDLER_SIMPLE(msg, func) \ | |
| 103 if(uMsg == msg) \ | |
| 104 { \ | |
| 105 SetMsgHandled(TRUE); \ | |
| 106 func(); \ | |
| 107 lResult = 0; \ | |
| 108 if(IsMsgHandled()) \ | |
| 109 return TRUE; \ | |
| 110 } | |
| 111 | |
| 112 // void OnSysCommandHelp() | |
| 113 #define MSG_WM_SYSCOMMAND_HELP(func) \ | |
| 114 if (uMsg == WM_SYSCOMMAND && wParam == SC_CONTEXTHELP) \ | |
| 115 { \ | |
| 116 SetMsgHandled(TRUE); \ | |
| 117 func(); \ | |
| 118 lResult = 0; \ | |
| 119 if(IsMsgHandled()) \ | |
| 120 return TRUE; \ | |
| 121 } | |
| 122 | |
| 123 //BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lResult, DWORD dwMsgMapID) | |
| 124 #define END_MSG_MAP_HOOK() \ | |
| 125 break; \ | |
| 126 default: \ | |
| 127 return __super::ProcessWindowMessage(hWnd, uMsg, wParam, lParam, lResult, dwMsgMapID); \ | |
| 128 } \ | |
| 129 return FALSE; \ | |
| 130 } | |
| 131 | |
| 132 | |
| 133 // Obsolete, use CImageListManaged instead | |
| 134 class CImageListContainer : public CImageList { | |
| 135 public: | |
| 136 CImageListContainer() {} | |
| 137 ~CImageListContainer() {Destroy();} | |
| 138 | |
| 139 void operator=(const CImageListContainer&) = delete; | |
| 140 CImageListContainer(const CImageListContainer&) = delete; | |
| 141 }; | |
| 142 | |
| 143 | |
| 144 template<bool managed> class CThemeT { | |
| 145 public: | |
| 146 CThemeT(HTHEME source = NULL) : m_theme(source) {} | |
| 147 | |
| 148 ~CThemeT() { | |
| 149 Release(); | |
| 150 } | |
| 151 | |
| 152 HTHEME OpenThemeData(HWND wnd,LPCWSTR classList) { | |
| 153 Release(); | |
| 154 return m_theme = ::OpenThemeData(wnd, classList); | |
| 155 } | |
| 156 | |
| 157 void Release() { | |
| 158 HTHEME releaseme = pfc::replace_null_t(m_theme); | |
| 159 if (managed && releaseme != NULL) CloseThemeData(releaseme); | |
| 160 } | |
| 161 | |
| 162 operator HTHEME() const {return m_theme;} | |
| 163 HTHEME m_theme; | |
| 164 }; | |
| 165 typedef CThemeT<false> CThemeHandle; | |
| 166 typedef CThemeT<true> CTheme; | |
| 167 | |
| 168 | |
| 169 class CCheckBox : public CButton { | |
| 170 public: | |
| 171 void ToggleCheck(bool state) {SetCheck(state ? BST_CHECKED : BST_UNCHECKED);} | |
| 172 bool IsChecked() const {return GetCheck() == BST_CHECKED;} | |
| 173 | |
| 174 CCheckBox(HWND hWnd = NULL) : CButton(hWnd) { } | |
| 175 CCheckBox & operator=(HWND wnd) {m_hWnd = wnd; return *this; } | |
| 176 }; | |
| 177 | |
| 178 class CEditPPHooks : public CWindowImpl<CEditPPHooks, CEdit> { | |
| 179 public: | |
| 180 bool HandleCtrlA = true, NoEscSteal = false, NoEnterSteal = false, WantAllKeys = false; | |
| 181 | |
| 182 std::function<void ()> onEnterKey; | |
| 183 std::function<void ()> onEscKey; | |
| 184 | |
| 185 CEditPPHooks(CMessageMap * hookMM = nullptr, int hookMMID = 0) : m_hookMM(hookMM), m_hookMMID(hookMMID) {} | |
| 186 | |
| 187 BEGIN_MSG_MAP_EX(CEditPPHooks) | |
| 188 MSG_WM_KEYDOWN(OnKeyDown) | |
| 189 MSG_WM_CHAR(OnChar) | |
| 190 MSG_WM_GETDLGCODE(OnEditGetDlgCode) | |
| 191 | |
| 192 if ( m_hookMM != nullptr ) { | |
| 193 | |
| 194 CHAIN_MSG_MAP_ALT_MEMBER( ( * m_hookMM ), m_hookMMID ); | |
| 195 | |
| 196 } | |
| 197 | |
| 198 END_MSG_MAP() | |
| 199 | |
| 200 static void DeleteLastWord( CEdit wnd, bool bForward = false ); | |
| 201 private: | |
| 202 static bool isSpecial( wchar_t c ) { | |
| 203 return (unsigned) c < ' '; | |
| 204 } | |
| 205 static bool isWordDelimiter( wchar_t c ) { | |
| 206 return c == ' ' || c == ',' || c == '.' || c == ';' || c == ':'; | |
| 207 } | |
| 208 void OnChar(UINT nChar, UINT, UINT nFlags) { | |
| 209 if (m_suppressChar != 0) { | |
| 210 if (nChar == m_suppressChar) return; | |
| 211 } | |
| 212 if (m_suppressScanCode != 0) { | |
| 213 UINT code = nFlags & 0xFF; | |
| 214 if (code == m_suppressScanCode) return; | |
| 215 } | |
| 216 SetMsgHandled(FALSE); | |
| 217 } | |
| 218 void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { | |
| 219 (void)nRepCnt; | |
| 220 m_suppressChar = 0; | |
| 221 m_suppressScanCode = 0; | |
| 222 if (HandleCtrlA) { | |
| 223 if (nChar == 'A') { | |
| 224 if (GetHotkeyModifierFlags() == MOD_CONTROL) { | |
| 225 m_suppressScanCode = nFlags & 0xFF; | |
| 226 this->SetSelAll(); return; | |
| 227 } | |
| 228 } | |
| 229 if ( nChar == VK_BACK ) { | |
| 230 if (GetHotkeyModifierFlags() == MOD_CONTROL) { | |
| 231 m_suppressScanCode = nFlags & 0xFF; | |
| 232 DeleteLastWord( *this ) ; return; | |
| 233 } | |
| 234 } | |
| 235 if ( nChar == VK_DELETE ) { | |
| 236 if (GetHotkeyModifierFlags() == MOD_CONTROL) { | |
| 237 m_suppressScanCode = nFlags & 0xFF; | |
| 238 DeleteLastWord( *this, true ) ; return; | |
| 239 } | |
| 240 } | |
| 241 if ( nChar == VK_RETURN && onEnterKey ) { | |
| 242 m_suppressChar = nChar; | |
| 243 onEnterKey(); return; | |
| 244 } | |
| 245 if ( nChar == VK_ESCAPE && onEscKey ) { | |
| 246 m_suppressChar = nChar; | |
| 247 onEscKey(); return; | |
| 248 } | |
| 249 } | |
| 250 SetMsgHandled(FALSE); | |
| 251 } | |
| 252 UINT OnEditGetDlgCode(LPMSG lpMsg) { | |
| 253 if (WantAllKeys) return DLGC_WANTALLKEYS; | |
| 254 if (lpMsg == NULL) { | |
| 255 SetMsgHandled(FALSE); return 0; | |
| 256 } else { | |
| 257 switch(lpMsg->message) { | |
| 258 case WM_KEYDOWN: | |
| 259 case WM_SYSKEYDOWN: | |
| 260 switch(lpMsg->wParam) { | |
| 261 case VK_ESCAPE: | |
| 262 if (onEscKey) { | |
| 263 return DLGC_WANTMESSAGE; | |
| 264 } | |
| 265 SetMsgHandled(!!NoEscSteal); | |
| 266 return 0; | |
| 267 case VK_RETURN: | |
| 268 if (onEnterKey) { | |
| 269 return DLGC_WANTMESSAGE; | |
| 270 } | |
| 271 SetMsgHandled(!!NoEnterSteal); | |
| 272 return 0; | |
| 273 default: | |
| 274 SetMsgHandled(FALSE); return 0; | |
| 275 } | |
| 276 default: | |
| 277 SetMsgHandled(FALSE); return 0; | |
| 278 | |
| 279 } | |
| 280 } | |
| 281 } | |
| 282 UINT m_suppressChar = 0, m_suppressScanCode = 0; | |
| 283 CMessageMap * const m_hookMM; | |
| 284 const int m_hookMMID; | |
| 285 }; | |
| 286 | |
| 287 | |
| 288 class CEditNoEscSteal : public CEdit { | |
| 289 public: | |
| 290 void SubclassWindow(HWND wnd) { | |
| 291 this->Attach(wnd); | |
| 292 SubclassThisWindow(wnd); | |
| 293 } | |
| 294 static void SubclassThisWindow(HWND wnd) { | |
| 295 SetWindowSubclass(wnd, proc, 0, 0); | |
| 296 } | |
| 297 private: | |
| 298 static LRESULT CALLBACK proc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR, DWORD_PTR) { | |
| 299 if ( uMsg == WM_GETDLGCODE ) { | |
| 300 auto lpMsg = reinterpret_cast<LPMSG>(lParam); | |
| 301 if (lpMsg != NULL) { | |
| 302 switch(lpMsg->message) { | |
| 303 case WM_KEYDOWN: | |
| 304 case WM_SYSKEYDOWN: | |
| 305 switch(lpMsg->wParam) { | |
| 306 case VK_ESCAPE: | |
| 307 return 0; | |
| 308 } | |
| 309 } | |
| 310 } | |
| 311 } | |
| 312 return DefSubclassProc(hWnd, uMsg, wParam, lParam); | |
| 313 } | |
| 314 }; | |
| 315 | |
| 316 class CEditNoEnterEscSteal : public CEdit { | |
| 317 public: | |
| 318 void SubclassWindow(HWND wnd) { | |
| 319 this->Attach(wnd); | |
| 320 SubclassThisWindow(wnd); | |
| 321 } | |
| 322 static void SubclassThisWindow(HWND wnd) { | |
| 323 SetWindowSubclass(wnd, proc, 0, 0); | |
| 324 } | |
| 325 private: | |
| 326 static LRESULT CALLBACK proc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR, DWORD_PTR) { | |
| 327 if ( uMsg == WM_GETDLGCODE ) { | |
| 328 auto lpMsg = reinterpret_cast<LPMSG>(lParam); | |
| 329 if (lpMsg != NULL) { | |
| 330 switch(lpMsg->message) { | |
| 331 case WM_KEYDOWN: | |
| 332 case WM_SYSKEYDOWN: | |
| 333 switch(lpMsg->wParam) { | |
| 334 case VK_RETURN: | |
| 335 case VK_ESCAPE: | |
| 336 return 0; | |
| 337 } | |
| 338 } | |
| 339 } | |
| 340 } | |
| 341 return DefSubclassProc(hWnd, uMsg, wParam, lParam); | |
| 342 } | |
| 343 }; | |
| 344 | |
| 345 | |
| 346 | |
| 347 class CWindowClassUnregisterScope { | |
| 348 public: | |
| 349 CWindowClassUnregisterScope() : name() {} | |
| 350 const TCHAR * name; | |
| 351 void Set(const TCHAR * n) {ATLASSERT( name == NULL ); name = n; } | |
| 352 bool IsActive() const {return name != NULL;} | |
| 353 void CleanUp() { | |
| 354 const TCHAR * n = name; name = NULL; | |
| 355 if (n != NULL) ATLASSERT_SUCCESS( UnregisterClass(n, (HINSTANCE)&__ImageBase) ); | |
| 356 } | |
| 357 ~CWindowClassUnregisterScope() {CleanUp();} | |
| 358 }; | |
| 359 | |
| 360 | |
| 361 // CWindowRegisteredT | |
| 362 // Minimalistic wrapper for registering own window classes that can be created by class name, included in dialogs and such. | |
| 363 // Usage: | |
| 364 // class myClass : public CWindowRegisteredT<myClass> {...}; | |
| 365 // Call myClass::Register() before first use | |
| 366 template<typename TClass, typename TBaseClass = CWindow> | |
| 367 class CWindowRegisteredT : public TBaseClass, public CMessageMap { | |
| 368 public: | |
| 369 static UINT GetClassStyle() { | |
| 370 return CS_VREDRAW | CS_HREDRAW; | |
| 371 } | |
| 372 static HCURSOR GetCursor() { | |
| 373 return ::LoadCursor(NULL, IDC_ARROW); | |
| 374 } | |
| 375 | |
| 376 BEGIN_MSG_MAP_EX(CWindowRegisteredT) | |
| 377 END_MSG_MAP() | |
| 378 | |
| 379 | |
| 380 static void Register() { | |
| 381 static CWindowClassUnregisterScope scope; | |
| 382 if (!scope.IsActive()) { | |
| 383 WNDCLASS wc = {}; | |
| 384 wc.style = TClass::GetClassStyle(); | |
| 385 wc.cbWndExtra = sizeof(void*); | |
| 386 wc.lpszClassName = TClass::GetClassName(); | |
| 387 wc.lpfnWndProc = myWindowProc; | |
| 388 wc.hInstance = (HINSTANCE)&__ImageBase; | |
| 389 wc.hCursor = TClass::GetCursor(); | |
| 390 ATLASSERT_SUCCESS( RegisterClass(&wc) != 0 ); | |
| 391 scope.Set(wc.lpszClassName); | |
| 392 } | |
| 393 } | |
| 394 protected: | |
| 395 virtual ~CWindowRegisteredT() {} | |
| 396 private: | |
| 397 static LRESULT CALLBACK myWindowProc(HWND wnd, UINT msg, WPARAM wp, LPARAM lp) { | |
| 398 TClass * i = NULL; | |
| 399 if (msg == WM_NCCREATE) { | |
| 400 i = new TClass; | |
| 401 i->Attach(wnd); | |
| 402 ::SetWindowLongPtr(wnd, 0, reinterpret_cast<LONG_PTR>(i)); | |
| 403 } else { | |
| 404 i = reinterpret_cast<TClass*>( ::GetWindowLongPtr(wnd, 0) ); | |
| 405 } | |
| 406 LRESULT r; | |
| 407 if (i == NULL || !i->ProcessWindowMessage(wnd, msg, wp, lp, r)) r = ::DefWindowProc(wnd, msg, wp, lp); | |
| 408 if (msg == WM_NCDESTROY) { | |
| 409 ::SetWindowLongPtr(wnd, 0, 0); | |
| 410 delete i; | |
| 411 } | |
| 412 return r; | |
| 413 } | |
| 414 }; | |
| 415 | |
| 416 | |
| 417 | |
| 418 | |
| 419 class CSRWlock { | |
| 420 public: | |
| 421 CSRWlock() { } | |
| 422 | |
| 423 static bool HaveAPI() { return true; } | |
| 424 | |
| 425 void EnterShared() { | |
| 426 AcquireSRWLockShared( & theLock ); | |
| 427 } | |
| 428 void EnterExclusive() { | |
| 429 AcquireSRWLockExclusive( & theLock ); | |
| 430 } | |
| 431 void LeaveShared() { | |
| 432 ReleaseSRWLockShared( & theLock ); | |
| 433 } | |
| 434 void LeaveExclusive() { | |
| 435 ReleaseSRWLockExclusive( &theLock ); | |
| 436 } | |
| 437 | |
| 438 private: | |
| 439 CSRWlock(const CSRWlock&) = delete; | |
| 440 void operator=(const CSRWlock&) = delete; | |
| 441 | |
| 442 SRWLOCK theLock = {}; | |
| 443 }; |
