Mercurial > foo_out_sdl
comparison foosdk/sdk/foobar2000/helpers/atl-misc.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 | |
| 3 #include "win32_misc.h" | |
| 4 | |
| 5 #ifdef _WIN32 | |
| 6 #include <SDK/ui_element.h> | |
| 7 #include <SDK/ui.h> | |
| 8 #include <SDK/contextmenu_manager.h> | |
| 9 #include <SDK/preferences_page.h> | |
| 10 #include <libPPUI/WTL-PP.h> | |
| 11 #include <utility> | |
| 12 | |
| 13 class CMenuSelectionReceiver : public CWindowImpl<CMenuSelectionReceiver> { | |
| 14 public: | |
| 15 CMenuSelectionReceiver(HWND p_parent) { | |
| 16 WIN32_OP( Create(p_parent) != NULL ); | |
| 17 } | |
| 18 ~CMenuSelectionReceiver() { | |
| 19 if (m_hWnd != NULL) DestroyWindow(); | |
| 20 } | |
| 21 typedef CWindowImpl<CMenuSelectionReceiver> _baseClass; | |
| 22 DECLARE_WND_CLASS_EX(TEXT("{DF0087DB-E765-4283-BBAB-6AB2E8AB64A1}"),0,0); | |
| 23 | |
| 24 BEGIN_MSG_MAP(CMenuSelectionReceiver) | |
| 25 MESSAGE_HANDLER(WM_MENUSELECT,OnMenuSelect) | |
| 26 END_MSG_MAP() | |
| 27 protected: | |
| 28 virtual bool QueryHint(unsigned p_id,pfc::string_base & p_out) { | |
| 29 return false; | |
| 30 } | |
| 31 private: | |
| 32 LRESULT OnMenuSelect(UINT,WPARAM p_wp,LPARAM p_lp,BOOL&) { | |
| 33 if (p_lp != 0) { | |
| 34 if (HIWORD(p_wp) & MF_POPUP) { | |
| 35 m_status.release(); | |
| 36 } else { | |
| 37 pfc::string8 msg; | |
| 38 UINT cmd = LOWORD(p_wp); | |
| 39 if ( cmd == 0 || !QueryHint(cmd,msg)) { | |
| 40 m_status.release(); | |
| 41 } else { | |
| 42 if (m_status.is_empty()) { | |
| 43 if (!static_api_ptr_t<ui_control>()->override_status_text_create(m_status)) m_status.release(); | |
| 44 } | |
| 45 if (m_status.is_valid()) { | |
| 46 m_status->override_text(msg); | |
| 47 } | |
| 48 } | |
| 49 } | |
| 50 } else { | |
| 51 m_status.release(); | |
| 52 } | |
| 53 return 0; | |
| 54 } | |
| 55 | |
| 56 service_ptr_t<ui_status_text_override> m_status; | |
| 57 | |
| 58 PFC_CLASS_NOT_COPYABLE(CMenuSelectionReceiver,CMenuSelectionReceiver); | |
| 59 }; | |
| 60 | |
| 61 class CMenuDescriptionMap : public CMenuSelectionReceiver { | |
| 62 public: | |
| 63 CMenuDescriptionMap(HWND p_parent) : CMenuSelectionReceiver(p_parent) {} | |
| 64 void Set(unsigned p_id,const char * p_description) {m_content.set(p_id,p_description);} | |
| 65 protected: | |
| 66 bool QueryHint(unsigned p_id,pfc::string_base & p_out) { | |
| 67 return m_content.query(p_id,p_out); | |
| 68 } | |
| 69 private: | |
| 70 pfc::map_t<unsigned,pfc::string8> m_content; | |
| 71 }; | |
| 72 | |
| 73 class CMenuDescriptionHybrid : public CMenuSelectionReceiver { | |
| 74 public: | |
| 75 CMenuDescriptionHybrid(HWND parent) : CMenuSelectionReceiver(parent) {} | |
| 76 void Set(unsigned id, const char * desc) {m_content.set(id, desc);} | |
| 77 | |
| 78 void SetCM(contextmenu_manager::ptr mgr, unsigned base, unsigned max) { | |
| 79 m_cmMgr = mgr; m_cmMgr_base = base; m_cmMgr_max = max; | |
| 80 } | |
| 81 protected: | |
| 82 bool QueryHint(unsigned p_id,pfc::string_base & p_out) { | |
| 83 if (m_cmMgr.is_valid() && p_id >= m_cmMgr_base && p_id < m_cmMgr_max) { | |
| 84 return m_cmMgr->get_description_by_id(p_id - m_cmMgr_base,p_out); | |
| 85 } | |
| 86 return m_content.query(p_id,p_out); | |
| 87 } | |
| 88 private: | |
| 89 pfc::map_t<unsigned,pfc::string8> m_content; | |
| 90 contextmenu_manager::ptr m_cmMgr; unsigned m_cmMgr_base, m_cmMgr_max; | |
| 91 }; | |
| 92 | |
| 93 inline pfc::string_base & operator<<(pfc::string_base & p_fmt,const CPoint & p_point) { | |
| 94 return p_fmt << "(" << p_point.x << "," << p_point.y << ")"; | |
| 95 } | |
| 96 | |
| 97 inline pfc::string_base & operator<<(pfc::string_base & p_fmt,const CRect & p_rect) { | |
| 98 return p_fmt << "(" << p_rect.left << "," << p_rect.top << "," << p_rect.right << "," << p_rect.bottom << ")"; | |
| 99 } | |
| 100 | |
| 101 template<typename TClass> | |
| 102 class CAddDummyMessageMap : public TClass { | |
| 103 public: | |
| 104 BEGIN_MSG_MAP(CAddDummyMessageMap<TClass>) | |
| 105 END_MSG_MAP() | |
| 106 }; | |
| 107 | |
| 108 template<typename _parentClass> class CWindowFixSEH : public _parentClass { public: | |
| 109 BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lResult, DWORD dwMsgMapID = 0) { | |
| 110 __try { | |
| 111 return _parentClass::ProcessWindowMessage(hWnd, uMsg, wParam, lParam, lResult, dwMsgMapID); | |
| 112 } __except(uExceptFilterProc(GetExceptionInformation())) { return FALSE; /* should not get here */ } | |
| 113 } | |
| 114 template<typename ... arg_t> CWindowFixSEH( arg_t && ... arg ) : _parentClass( std::forward<arg_t>(arg) ... ) {} | |
| 115 }; | |
| 116 | |
| 117 template<typename TClass> | |
| 118 class CWindowAutoLifetime : public CWindowFixSEH<TClass> { | |
| 119 public: | |
| 120 typedef CWindowFixSEH<TClass> TBase; | |
| 121 template<typename ... arg_t> CWindowAutoLifetime(HWND parent, arg_t && ... arg) : TBase( std::forward<arg_t>(arg) ... ) {Init(parent);} | |
| 122 private: | |
| 123 void Init(HWND parent) {WIN32_OP(this->Create(parent) != NULL);} | |
| 124 void OnFinalMessage(HWND wnd) {PFC_ASSERT_NO_EXCEPTION( TBase::OnFinalMessage(wnd) ); PFC_ASSERT_NO_EXCEPTION(delete this);} | |
| 125 }; | |
| 126 | |
| 127 template<typename TClass> | |
| 128 class ImplementModalTracking : public TClass { | |
| 129 public: | |
| 130 template<typename ... arg_t> ImplementModalTracking(arg_t && ... arg) : TClass(std::forward<arg_t>(arg) ...) {} | |
| 131 | |
| 132 BEGIN_MSG_MAP_EX(ImplementModalTracking) | |
| 133 MSG_WM_INITDIALOG(OnInitDialog) | |
| 134 MSG_WM_DESTROY(OnDestroy) | |
| 135 CHAIN_MSG_MAP(TClass) | |
| 136 END_MSG_MAP() | |
| 137 private: | |
| 138 void OnDestroy() { | |
| 139 m_modal.deinitialize(); | |
| 140 SetMsgHandled(FALSE); | |
| 141 } | |
| 142 BOOL OnInitDialog(CWindow, LPARAM) { | |
| 143 m_modal.initialize(this->m_hWnd); | |
| 144 SetMsgHandled(FALSE); | |
| 145 return FALSE; | |
| 146 } | |
| 147 modal_dialog_scope m_modal; | |
| 148 | |
| 149 }; | |
| 150 | |
| 151 template<typename TClass> | |
| 152 class ImplementModelessTracking : public TClass { | |
| 153 public: | |
| 154 template<typename ... arg_t> ImplementModelessTracking(arg_t && ... arg ) : TClass(std::forward<arg_t>(arg) ... ) {} | |
| 155 | |
| 156 BEGIN_MSG_MAP_EX(ImplementModelessTracking) | |
| 157 MSG_WM_INITDIALOG(OnInitDialog) | |
| 158 MSG_WM_DESTROY(OnDestroy) | |
| 159 CHAIN_MSG_MAP(TClass) | |
| 160 END_MSG_MAP_HOOK() | |
| 161 private: | |
| 162 BOOL OnInitDialog(CWindow, LPARAM) {m_modeless.Set( this->m_hWnd ); SetMsgHandled(FALSE); return FALSE; } | |
| 163 void OnDestroy() {m_modeless.Set(NULL); SetMsgHandled(FALSE); } | |
| 164 CModelessDialogEntry m_modeless; | |
| 165 }; | |
| 166 | |
| 167 namespace fb2k { | |
| 168 template<typename dialog_t, typename ... arg_t> dialog_t * newDialogEx( HWND parent, arg_t && ... arg ) { | |
| 169 return new CWindowAutoLifetime<ImplementModelessTracking< dialog_t > > ( parent, std::forward<arg_t>(arg) ... ); | |
| 170 } | |
| 171 template<typename dialog_t, typename ... arg_t> dialog_t * newDialog(arg_t && ... arg) { | |
| 172 return new CWindowAutoLifetime<ImplementModelessTracking< dialog_t > > (core_api::get_main_window(), std::forward<arg_t>(arg) ...); | |
| 173 } | |
| 174 } | |
| 175 | |
| 176 class CMenuSelectionReceiver_UiElement : public CMenuSelectionReceiver { | |
| 177 public: | |
| 178 CMenuSelectionReceiver_UiElement(service_ptr_t<ui_element_instance> p_owner,unsigned p_id_base) : CMenuSelectionReceiver(p_owner->get_wnd()), m_owner(p_owner), m_id_base(p_id_base) {} | |
| 179 protected: | |
| 180 bool QueryHint(unsigned p_id,pfc::string_base & p_out) { | |
| 181 return m_owner->edit_mode_context_menu_get_description(p_id,m_id_base,p_out); | |
| 182 } | |
| 183 private: | |
| 184 const unsigned m_id_base; | |
| 185 const service_ptr_t<ui_element_instance> m_owner; | |
| 186 }; | |
| 187 | |
| 188 static bool window_service_trait_defer_destruction(const service_base *) {return true;} | |
| 189 | |
| 190 | |
| 191 //! Special service_impl_t replacement for service classes that also implement ATL/WTL windows. | |
| 192 template<typename _t_base> | |
| 193 class window_service_impl_t : public implement_service_query< CWindowFixSEH<_t_base> > { | |
| 194 private: | |
| 195 typedef window_service_impl_t<_t_base> t_self; | |
| 196 typedef implement_service_query< CWindowFixSEH<_t_base> > t_base; | |
| 197 public: | |
| 198 BEGIN_MSG_MAP_EX(window_service_impl_t) | |
| 199 MSG_WM_DESTROY(OnDestroyPassThru) | |
| 200 CHAIN_MSG_MAP(__super) | |
| 201 END_MSG_MAP_HOOK() | |
| 202 | |
| 203 int FB2KAPI service_release() throw() { | |
| 204 int ret = --m_counter; | |
| 205 if (ret == 0) { | |
| 206 if (window_service_trait_defer_destruction(this) && !InterlockedExchange(&m_delayedDestroyInProgress,1)) { | |
| 207 PFC_ASSERT_NO_EXCEPTION( service_impl_helper::release_object_delayed(this); ); | |
| 208 } else if (this->m_hWnd != NULL) { | |
| 209 if (!InterlockedExchange(&m_destroyWindowInProgress, 1)) {// don't double-destroy in weird scenarios | |
| 210 service_ptr_t<service_base> bump(this); // prevent delete this from occurring in mid-DestroyWindow | |
| 211 PFC_ASSERT_NO_EXCEPTION(::DestroyWindow(this->m_hWnd)); | |
| 212 // We don't know what else happened inside DestroyWindow() due to message queue flush | |
| 213 // Safely retry destruction by bump object destructor | |
| 214 // m_hWnd doesn't have to be null here - we'll possibly get cleaned up by OnFinalMessage() instead | |
| 215 } | |
| 216 } else { // m_hWnd is NULL | |
| 217 PFC_ASSERT_NO_EXCEPTION( delete this ); | |
| 218 } | |
| 219 } | |
| 220 return ret; | |
| 221 } | |
| 222 int FB2KAPI service_add_ref() throw() {return ++m_counter;} | |
| 223 | |
| 224 template<typename ... arg_t> | |
| 225 window_service_impl_t( arg_t && ... arg ) : t_base( std::forward<arg_t>(arg) ... ) {}; | |
| 226 | |
| 227 ~window_service_impl_t() { | |
| 228 PFC_ASSERT(this->m_hWnd == NULL); | |
| 229 } | |
| 230 private: | |
| 231 void OnDestroyPassThru() { | |
| 232 SetMsgHandled(FALSE); m_destroyWindowInProgress = 1; | |
| 233 } | |
| 234 void OnFinalMessage(HWND p_wnd) override { | |
| 235 t_base::OnFinalMessage(p_wnd); | |
| 236 service_ptr_t<service_base> bump(this); | |
| 237 } | |
| 238 volatile LONG m_destroyWindowInProgress = 0; | |
| 239 volatile LONG m_delayedDestroyInProgress = 0; | |
| 240 pfc::refcounter m_counter; | |
| 241 }; | |
| 242 | |
| 243 namespace fb2k { | |
| 244 template<typename obj_t, typename ... arg_t> | |
| 245 service_ptr_t<obj_t> service_new_window(arg_t && ... arg) { | |
| 246 return new window_service_impl_t< obj_t > ( std::forward<arg_t> (arg) ... ); | |
| 247 } | |
| 248 } | |
| 249 | |
| 250 static void AppendMenuPopup(HMENU menu, UINT flags, CMenu & popup, const TCHAR * label) { | |
| 251 PFC_ASSERT( flags & MF_POPUP ); | |
| 252 WIN32_OP_D( CMenuHandle(menu).AppendMenu(flags, popup, label) ); | |
| 253 popup.Detach(); | |
| 254 } | |
| 255 | |
| 256 class CMessageMapDummy : public CMessageMap { | |
| 257 public: | |
| 258 BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, | |
| 259 LRESULT& lResult, DWORD dwMsgMapID) {return FALSE;} | |
| 260 }; | |
| 261 | |
| 262 | |
| 263 | |
| 264 | |
| 265 | |
| 266 | |
| 267 | |
| 268 template<typename TDialog> class preferences_page_instance_impl : public TDialog { | |
| 269 public: | |
| 270 preferences_page_instance_impl(HWND parent, preferences_page_callback::ptr callback) : TDialog(callback) { | |
| 271 WIN32_OP(this->Create(parent) != NULL); | |
| 272 | |
| 273 // complain early if what we created isn't a child window | |
| 274 PFC_ASSERT( (this->GetStyle() & (WS_POPUP|WS_CHILD)) == WS_CHILD ); | |
| 275 } | |
| 276 HWND get_wnd() {return this->m_hWnd;} | |
| 277 }; | |
| 278 static bool window_service_trait_defer_destruction(const preferences_page_instance *) {return false;} | |
| 279 template<typename TDialog> class preferences_page_impl : public preferences_page_v3 { | |
| 280 public: | |
| 281 preferences_page_instance::ptr instantiate(HWND parent, preferences_page_callback::ptr callback) { | |
| 282 return fb2k::service_new_window<preferences_page_instance_impl<TDialog> >(parent, callback); | |
| 283 } | |
| 284 }; | |
| 285 | |
| 286 class CEmbeddedDialog : public CDialogImpl<CEmbeddedDialog> { | |
| 287 public: | |
| 288 CEmbeddedDialog(CMessageMap * owner, DWORD msgMapID, UINT dialogID) : m_owner(*owner), IDD(dialogID), m_msgMapID(msgMapID) {} | |
| 289 | |
| 290 BEGIN_MSG_MAP(CEmbeddedDialog) | |
| 291 CHAIN_MSG_MAP_ALT_MEMBER(m_owner, m_msgMapID) | |
| 292 END_MSG_MAP() | |
| 293 | |
| 294 const DWORD m_msgMapID; | |
| 295 const UINT IDD; | |
| 296 CMessageMap & m_owner; | |
| 297 }; | |
| 298 | |
| 299 | |
| 300 // ui_element stuff here because of window_service_impl_t | |
| 301 | |
| 302 template<typename instance_t> | |
| 303 class ui_element_instance_impl_helper : public instance_t { | |
| 304 public: | |
| 305 template<typename ... args_t> | |
| 306 ui_element_instance_impl_helper(args_t && ... args) : instance_t(std::forward<args_t>(args) ...) {} | |
| 307 | |
| 308 GUID get_guid() override { return instance_t::g_get_guid(); } | |
| 309 GUID get_subclass() override { return instance_t::g_get_subclass(); } | |
| 310 HWND get_wnd() override { return *this; } | |
| 311 }; | |
| 312 | |
| 313 | |
| 314 namespace fb2k { | |
| 315 template<typename TImpl, typename ... args_t> | |
| 316 ui_element_instance::ptr newUIElement(HWND parent, args_t && ... args) { | |
| 317 auto item = fb2k::service_new_window < ui_element_instance_impl_helper < TImpl > >(std::forward<args_t>(args) ...); | |
| 318 item->initialize_window(parent); | |
| 319 return item; | |
| 320 } | |
| 321 } | |
| 322 | |
| 323 template<typename TImpl, typename TInterface = ui_element> class ui_element_impl : public TInterface { | |
| 324 public: | |
| 325 GUID get_guid() { return TImpl::g_get_guid(); } | |
| 326 GUID get_subclass() { return TImpl::g_get_subclass(); } | |
| 327 void get_name(pfc::string_base & out) { TImpl::g_get_name(out); } | |
| 328 | |
| 329 template<typename ... args_t> | |
| 330 ui_element_instance::ptr instantiate_helper(HWND parent, args_t && ... args) { | |
| 331 return fb2k::newUIElement<TImpl>(parent, std::forward<args_t>(args) ...); | |
| 332 } | |
| 333 | |
| 334 ui_element_instance::ptr instantiate(HWND parent, ui_element_config::ptr cfg, ui_element_instance_callback::ptr callback) { | |
| 335 PFC_ASSERT(cfg->get_guid() == get_guid()); | |
| 336 return instantiate_helper(parent, cfg, callback); | |
| 337 } | |
| 338 ui_element_config::ptr get_default_configuration() { return TImpl::g_get_default_configuration(); } | |
| 339 ui_element_children_enumerator_ptr enumerate_children(ui_element_config::ptr cfg) { return NULL; } | |
| 340 bool get_description(pfc::string_base & out) { out = TImpl::g_get_description(); return true; } | |
| 341 }; | |
| 342 | |
| 343 | |
| 344 #endif // _WIN32 |
