diff 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
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/foosdk/sdk/foobar2000/helpers/atl-misc.h	Mon Jan 05 02:15:46 2026 -0500
@@ -0,0 +1,344 @@
+#pragma once
+
+#include "win32_misc.h"
+
+#ifdef _WIN32
+#include <SDK/ui_element.h>
+#include <SDK/ui.h>
+#include <SDK/contextmenu_manager.h>
+#include <SDK/preferences_page.h>
+#include <libPPUI/WTL-PP.h>
+#include <utility>
+
+class CMenuSelectionReceiver : public CWindowImpl<CMenuSelectionReceiver> {
+public:
+	CMenuSelectionReceiver(HWND p_parent) {
+		WIN32_OP( Create(p_parent) != NULL );
+	}
+	~CMenuSelectionReceiver() {
+		if (m_hWnd != NULL) DestroyWindow();
+	}
+	typedef CWindowImpl<CMenuSelectionReceiver> _baseClass;
+	DECLARE_WND_CLASS_EX(TEXT("{DF0087DB-E765-4283-BBAB-6AB2E8AB64A1}"),0,0);
+
+	BEGIN_MSG_MAP(CMenuSelectionReceiver)
+		MESSAGE_HANDLER(WM_MENUSELECT,OnMenuSelect)
+	END_MSG_MAP()
+protected:
+	virtual bool QueryHint(unsigned p_id,pfc::string_base & p_out) {
+		return false;
+	}
+private:
+	LRESULT OnMenuSelect(UINT,WPARAM p_wp,LPARAM p_lp,BOOL&) {
+		if (p_lp != 0) {
+			if (HIWORD(p_wp) & MF_POPUP) {
+				m_status.release();
+			} else {
+				pfc::string8 msg;
+				UINT cmd = LOWORD(p_wp);
+				if ( cmd == 0 || !QueryHint(cmd,msg)) {
+					m_status.release();
+				} else {
+					if (m_status.is_empty()) {
+						if (!static_api_ptr_t<ui_control>()->override_status_text_create(m_status)) m_status.release();
+					}
+					if (m_status.is_valid()) {
+						m_status->override_text(msg);
+					}
+				}
+			}
+		} else {
+			m_status.release();
+		}
+		return 0;
+	}
+
+	service_ptr_t<ui_status_text_override> m_status;
+
+	PFC_CLASS_NOT_COPYABLE(CMenuSelectionReceiver,CMenuSelectionReceiver);
+};
+
+class CMenuDescriptionMap : public CMenuSelectionReceiver {
+public:
+	CMenuDescriptionMap(HWND p_parent) : CMenuSelectionReceiver(p_parent) {}
+	void Set(unsigned p_id,const char * p_description) {m_content.set(p_id,p_description);}
+protected:
+	bool QueryHint(unsigned p_id,pfc::string_base & p_out) {
+		return m_content.query(p_id,p_out);
+	}
+private:
+	pfc::map_t<unsigned,pfc::string8> m_content;
+};
+
+class CMenuDescriptionHybrid : public CMenuSelectionReceiver {
+public:
+	CMenuDescriptionHybrid(HWND parent) : CMenuSelectionReceiver(parent) {}
+	void Set(unsigned id, const char * desc) {m_content.set(id, desc);}
+
+	void SetCM(contextmenu_manager::ptr mgr, unsigned base, unsigned max) {
+		m_cmMgr = mgr; m_cmMgr_base = base; m_cmMgr_max = max;
+	}
+protected:
+	bool QueryHint(unsigned p_id,pfc::string_base & p_out) {
+		if (m_cmMgr.is_valid() && p_id >= m_cmMgr_base && p_id < m_cmMgr_max) {
+			return m_cmMgr->get_description_by_id(p_id - m_cmMgr_base,p_out);
+		}
+		return m_content.query(p_id,p_out);
+	}
+private:
+	pfc::map_t<unsigned,pfc::string8> m_content;
+	contextmenu_manager::ptr m_cmMgr; unsigned m_cmMgr_base, m_cmMgr_max;
+};
+
+inline pfc::string_base & operator<<(pfc::string_base & p_fmt,const CPoint & p_point) {
+	return p_fmt << "(" << p_point.x << "," << p_point.y << ")";
+}
+
+inline pfc::string_base & operator<<(pfc::string_base & p_fmt,const CRect & p_rect) {
+	return p_fmt << "(" << p_rect.left << "," << p_rect.top << "," << p_rect.right << "," << p_rect.bottom << ")";
+}
+
+template<typename TClass>
+class CAddDummyMessageMap : public TClass {
+public:
+	BEGIN_MSG_MAP(CAddDummyMessageMap<TClass>)
+	END_MSG_MAP()
+};
+
+template<typename _parentClass> class CWindowFixSEH : public _parentClass { public:
+	BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lResult, DWORD dwMsgMapID = 0) {
+		__try {
+			return _parentClass::ProcessWindowMessage(hWnd, uMsg, wParam, lParam, lResult, dwMsgMapID);
+		} __except(uExceptFilterProc(GetExceptionInformation())) { return FALSE; /* should not get here */ }
+	}
+	template<typename ... arg_t> CWindowFixSEH( arg_t && ... arg ) : _parentClass( std::forward<arg_t>(arg) ... ) {}
+};
+
+template<typename TClass>
+class CWindowAutoLifetime : public CWindowFixSEH<TClass> {
+public:
+	typedef CWindowFixSEH<TClass> TBase;
+	template<typename ... arg_t> CWindowAutoLifetime(HWND parent, arg_t && ... arg) : TBase( std::forward<arg_t>(arg) ... ) {Init(parent);}
+private:
+	void Init(HWND parent) {WIN32_OP(this->Create(parent) != NULL);}
+	void OnFinalMessage(HWND wnd) {PFC_ASSERT_NO_EXCEPTION( TBase::OnFinalMessage(wnd) ); PFC_ASSERT_NO_EXCEPTION(delete this);}
+};
+
+template<typename TClass>
+class ImplementModalTracking : public TClass {
+public:
+	template<typename ... arg_t> ImplementModalTracking(arg_t && ... arg) : TClass(std::forward<arg_t>(arg) ...) {}
+
+	BEGIN_MSG_MAP_EX(ImplementModalTracking)
+		MSG_WM_INITDIALOG(OnInitDialog)
+		MSG_WM_DESTROY(OnDestroy)
+		CHAIN_MSG_MAP(TClass)
+	END_MSG_MAP()
+private:
+	void OnDestroy() {
+		m_modal.deinitialize();
+		SetMsgHandled(FALSE);
+	}
+	BOOL OnInitDialog(CWindow, LPARAM) {
+		m_modal.initialize(this->m_hWnd);
+		SetMsgHandled(FALSE);
+		return FALSE;
+	}
+	modal_dialog_scope m_modal;
+
+};
+
+template<typename TClass>
+class ImplementModelessTracking : public TClass {
+public:
+	template<typename ... arg_t> ImplementModelessTracking(arg_t && ... arg ) : TClass(std::forward<arg_t>(arg) ... ) {}
+	
+	BEGIN_MSG_MAP_EX(ImplementModelessTracking)
+		MSG_WM_INITDIALOG(OnInitDialog)
+		MSG_WM_DESTROY(OnDestroy)
+		CHAIN_MSG_MAP(TClass)
+	END_MSG_MAP_HOOK()
+private:
+	BOOL OnInitDialog(CWindow, LPARAM) {m_modeless.Set( this->m_hWnd ); SetMsgHandled(FALSE); return FALSE; }
+	void OnDestroy() {m_modeless.Set(NULL); SetMsgHandled(FALSE); }
+	CModelessDialogEntry m_modeless;
+};
+
+namespace fb2k {
+	template<typename dialog_t, typename ... arg_t> dialog_t * newDialogEx( HWND parent, arg_t && ... arg ) {
+		return new CWindowAutoLifetime<ImplementModelessTracking< dialog_t > > ( parent, std::forward<arg_t>(arg) ... );
+	}
+	template<typename dialog_t, typename ... arg_t> dialog_t * newDialog(arg_t && ... arg) {
+		return new CWindowAutoLifetime<ImplementModelessTracking< dialog_t > > (core_api::get_main_window(), std::forward<arg_t>(arg) ...);
+	}
+}
+
+class CMenuSelectionReceiver_UiElement : public CMenuSelectionReceiver {
+public:
+	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) {}
+protected:
+	bool QueryHint(unsigned p_id,pfc::string_base & p_out) {
+		return m_owner->edit_mode_context_menu_get_description(p_id,m_id_base,p_out);
+	}
+private:
+	const unsigned m_id_base;
+	const service_ptr_t<ui_element_instance> m_owner;
+};
+
+static bool window_service_trait_defer_destruction(const service_base *) {return true;}
+
+
+//! Special service_impl_t replacement for service classes that also implement ATL/WTL windows.
+template<typename _t_base>
+class window_service_impl_t : public implement_service_query< CWindowFixSEH<_t_base> > {
+private:
+	typedef window_service_impl_t<_t_base> t_self;
+	typedef implement_service_query< CWindowFixSEH<_t_base> > t_base;
+public:
+	BEGIN_MSG_MAP_EX(window_service_impl_t)
+		MSG_WM_DESTROY(OnDestroyPassThru)
+		CHAIN_MSG_MAP(__super)
+	END_MSG_MAP_HOOK()
+
+	int FB2KAPI service_release() throw() {
+		int ret = --m_counter; 
+		if (ret == 0) {
+			if (window_service_trait_defer_destruction(this) && !InterlockedExchange(&m_delayedDestroyInProgress,1)) {
+				PFC_ASSERT_NO_EXCEPTION( service_impl_helper::release_object_delayed(this); );
+			} else if (this->m_hWnd != NULL) {
+				if (!InterlockedExchange(&m_destroyWindowInProgress, 1)) {// don't double-destroy in weird scenarios
+					service_ptr_t<service_base> bump(this); // prevent delete this from occurring in mid-DestroyWindow
+					PFC_ASSERT_NO_EXCEPTION(::DestroyWindow(this->m_hWnd));
+					// We don't know what else happened inside DestroyWindow() due to message queue flush
+					// Safely retry destruction by bump object destructor
+					// m_hWnd doesn't have to be null here - we'll possibly get cleaned up by OnFinalMessage() instead
+				}
+			} else { // m_hWnd is NULL
+				PFC_ASSERT_NO_EXCEPTION( delete this );
+			}
+		}
+		return ret;
+	}
+	int FB2KAPI service_add_ref() throw() {return ++m_counter;}
+
+	template<typename ... arg_t>
+	window_service_impl_t( arg_t && ... arg ) : t_base( std::forward<arg_t>(arg) ... ) {};
+
+	~window_service_impl_t() {
+		PFC_ASSERT(this->m_hWnd == NULL);
+	}
+private:
+	void OnDestroyPassThru() {
+		SetMsgHandled(FALSE); m_destroyWindowInProgress = 1;
+	}
+	void OnFinalMessage(HWND p_wnd) override {
+		t_base::OnFinalMessage(p_wnd);
+		service_ptr_t<service_base> bump(this);
+	}
+	volatile LONG m_destroyWindowInProgress = 0;
+	volatile LONG m_delayedDestroyInProgress = 0;
+	pfc::refcounter m_counter;
+};
+
+namespace fb2k {
+	template<typename obj_t, typename ... arg_t> 
+	service_ptr_t<obj_t> service_new_window(arg_t && ... arg) {
+		return new window_service_impl_t< obj_t > ( std::forward<arg_t> (arg) ... );
+	}
+}
+
+static void AppendMenuPopup(HMENU menu, UINT flags, CMenu & popup, const TCHAR * label) {
+	PFC_ASSERT( flags & MF_POPUP );
+	WIN32_OP_D( CMenuHandle(menu).AppendMenu(flags, popup, label) );
+	popup.Detach();
+}
+
+class CMessageMapDummy : public CMessageMap { 
+public:
+	BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam,
+		LRESULT& lResult, DWORD dwMsgMapID) {return FALSE;}
+};
+
+
+
+
+
+
+
+template<typename TDialog> class preferences_page_instance_impl : public TDialog {
+public:
+	preferences_page_instance_impl(HWND parent, preferences_page_callback::ptr callback) : TDialog(callback) {
+		WIN32_OP(this->Create(parent) != NULL);
+
+		// complain early if what we created isn't a child window
+		PFC_ASSERT( (this->GetStyle() & (WS_POPUP|WS_CHILD)) == WS_CHILD );
+	}
+	HWND get_wnd() {return this->m_hWnd;}
+};
+static bool window_service_trait_defer_destruction(const preferences_page_instance *) {return false;}
+template<typename TDialog> class preferences_page_impl : public preferences_page_v3 {
+public:
+	preferences_page_instance::ptr instantiate(HWND parent, preferences_page_callback::ptr callback) {
+		return fb2k::service_new_window<preferences_page_instance_impl<TDialog> >(parent, callback);
+	}
+};
+
+class CEmbeddedDialog : public CDialogImpl<CEmbeddedDialog> {
+public:
+	CEmbeddedDialog(CMessageMap * owner, DWORD msgMapID, UINT dialogID) : m_owner(*owner), IDD(dialogID), m_msgMapID(msgMapID) {}
+	
+	BEGIN_MSG_MAP(CEmbeddedDialog)
+		CHAIN_MSG_MAP_ALT_MEMBER(m_owner, m_msgMapID)
+	END_MSG_MAP()
+
+	const DWORD m_msgMapID;
+	const UINT IDD;
+	CMessageMap & m_owner;
+};
+
+
+// ui_element stuff here because of window_service_impl_t
+
+template<typename instance_t>
+class ui_element_instance_impl_helper : public instance_t {
+public:
+	template<typename ... args_t>
+	ui_element_instance_impl_helper(args_t && ... args) : instance_t(std::forward<args_t>(args) ...) {}
+
+	GUID get_guid() override { return instance_t::g_get_guid(); }
+	GUID get_subclass() override { return instance_t::g_get_subclass(); }
+	HWND get_wnd() override { return *this; }
+};
+
+
+namespace fb2k {
+	template<typename TImpl, typename ... args_t>
+	ui_element_instance::ptr newUIElement(HWND parent, args_t && ... args) {
+		auto item = fb2k::service_new_window < ui_element_instance_impl_helper < TImpl > >(std::forward<args_t>(args) ...);
+		item->initialize_window(parent);
+		return item;
+	}
+}
+
+template<typename TImpl, typename TInterface = ui_element> class ui_element_impl : public TInterface {
+public:
+	GUID get_guid() { return TImpl::g_get_guid(); }
+	GUID get_subclass() { return TImpl::g_get_subclass(); }
+	void get_name(pfc::string_base & out) { TImpl::g_get_name(out); }
+
+	template<typename ... args_t>
+	ui_element_instance::ptr instantiate_helper(HWND parent, args_t && ... args) {
+		return fb2k::newUIElement<TImpl>(parent, std::forward<args_t>(args) ...);
+	}
+
+	ui_element_instance::ptr instantiate(HWND parent, ui_element_config::ptr cfg, ui_element_instance_callback::ptr callback) {
+		PFC_ASSERT(cfg->get_guid() == get_guid());
+		return instantiate_helper(parent, cfg, callback);
+	}
+	ui_element_config::ptr get_default_configuration() { return TImpl::g_get_default_configuration(); }
+	ui_element_children_enumerator_ptr enumerate_children(ui_element_config::ptr cfg) { return NULL; }
+	bool get_description(pfc::string_base & out) { out = TImpl::g_get_description(); return true; }
+};
+
+
+#endif // _WIN32