Mercurial > foo_out_sdl
diff foosdk/sdk/foobar2000/helpers/ui_element_helpers.cpp @ 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/ui_element_helpers.cpp Mon Jan 05 02:15:46 2026 -0500 @@ -0,0 +1,389 @@ +#include "stdafx.h" + +#if FOOBAR2000_TARGET_VERSION >= 79 + +#include "ui_element_helpers.h" +#include <libPPUI/IDataObjectUtils.h> +#include "atl-misc.h" + +namespace ui_element_helpers { + + ui_element_instance_ptr instantiate_dummy(HWND p_parent,ui_element_config::ptr cfg, ui_element_instance_callback_ptr p_callback) { + service_ptr_t<ui_element> ptr; + if (!find(ptr,pfc::guid_null)) uBugCheck(); + return ptr->instantiate(p_parent,cfg,p_callback); + } + ui_element_instance_ptr instantiate(HWND p_parent,ui_element_config::ptr cfg,ui_element_instance_callback_ptr p_callback) { + try { + service_ptr_t<ui_element> ptr; + if (!find(ptr,cfg->get_guid())) throw exception_io_data("UI Element Not Found"); + auto ret = ptr->instantiate(p_parent,cfg,p_callback); + if (ret.is_empty()) throw std::runtime_error("Null UI Element returned"); + return ret; + } catch(std::exception const & e) { + console::complain("UI Element instantiation failure",e); + return instantiate_dummy(p_parent,cfg,p_callback); + } + } + + ui_element_instance_ptr update(ui_element_instance_ptr p_element,HWND p_parent,ui_element_config::ptr cfg,ui_element_instance_callback_ptr p_callback) { + if (p_element.is_valid() && cfg->get_guid() == p_element->get_guid()) { + p_element->set_configuration(cfg); + return p_element; + } else { + return instantiate(p_parent,cfg,p_callback); + } + } + + bool find(service_ptr_t<ui_element> & p_out,const GUID & p_guid) { + return service_by_guid(p_out,p_guid); + } + + ui_element_children_enumerator_ptr enumerate_children(ui_element_config::ptr cfg) { + service_ptr_t<ui_element> ptr; + if (!find(ptr,cfg->get_guid())) return NULL; + try { + return ptr->enumerate_children(cfg); + } catch(exception_io_data const &) { + return NULL; + } + } +}; + +void ui_element_helpers::replace_with_new_element(ui_element_instance_ptr & p_item,const GUID & p_guid,HWND p_parent,ui_element_instance_callback_ptr p_callback) { + ui_element_config::ptr cfg; + try { + if (p_item.is_empty()) { + service_ptr_t<ui_element> ptr; + if (!find(ptr,p_guid)) throw exception_io_data("UI Element Not Found"); + cfg = ptr->get_default_configuration(); + p_item = ptr->instantiate(p_parent,cfg,p_callback); + } else if (p_item->get_guid() != p_guid) { + service_ptr_t<ui_element> ptr; + if (!find(ptr,p_guid)) throw exception_io_data("UI Element Not Found"); + cfg = ptr->import(p_item->get_configuration()); + //p_item.release(); + if (cfg.is_empty()) cfg = ptr->get_default_configuration(); + p_item = ptr->instantiate(p_parent,cfg,p_callback); + } + } catch(std::exception const & e) { + console::complain("UI Element instantiation failure",e); + if (cfg.is_empty()) cfg = ui_element_config::g_create_empty(); + p_item = instantiate_dummy(p_parent,cfg,p_callback); + } +} + + + +namespace { + class CMyMenuSelectionReceiver : public CMenuSelectionReceiver { + public: + CMyMenuSelectionReceiver(HWND p_wnd,ui_element_helpers::ui_element_edit_tools * p_host,ui_element_instance_ptr p_client,unsigned p_client_id,unsigned p_host_base,unsigned p_client_base) : CMenuSelectionReceiver(p_wnd), m_host(p_host), m_client(p_client), m_client_id(p_client_id), m_host_base(p_host_base), m_client_base(p_client_base) {} + bool QueryHint(unsigned p_id,pfc::string_base & p_out) { + if (p_id >= m_client_base) { + return m_client->edit_mode_context_menu_get_description(p_id,m_client_base,p_out); + } else if (p_id >= m_host_base) { + return m_host->host_edit_mode_context_menu_get_description(m_client_id,p_id,m_host_base,p_out); + } else { + const char * msg = ui_element_helpers::ui_element_edit_tools::description_from_menu_command(p_id); + if (msg == NULL) return false; + p_out = msg; + return true; + } + } + private: + ui_element_helpers::ui_element_edit_tools * const m_host; + ui_element_instance_ptr const m_client; + unsigned const m_client_id,m_host_base,m_client_base; + }; +}; + +namespace HostHelperIDs { + enum {ID_LABEL, ID_REPLACE = 1, ID_ADD_NEW, ID_CUT, ID_COPY, ID_PASTE, ID_CUSTOM_BASE}; +} + +void ui_element_helpers::ui_element_edit_tools::standard_edit_context_menu(LPARAM p_point,ui_element_instance_ptr p_item,unsigned p_id,HWND p_parent) { + using namespace HostHelperIDs; + static_api_ptr_t<ui_element_common_methods> api; + POINT pt; + bool fromkeyboard = false; + if (p_point == (LPARAM)(-1)) { + fromkeyboard = true; + if (!p_item->edit_mode_context_menu_get_focus_point(pt)) { + CRect rect; + WIN32_OP_D( CWindow(p_item->get_wnd()).GetWindowRect(&rect) ); + pt = rect.CenterPoint(); + } + } else { + pt.x = (short)LOWORD(p_point); + pt.y = (short)HIWORD(p_point); + } + + CMenu menu; + WIN32_OP_D( menu.CreatePopupMenu() ); + + const GUID sourceItemGuid = p_item->get_guid(); + const bool sourceItemEmpty = !!(sourceItemGuid == pfc::guid_null); + + if (sourceItemEmpty) { + WIN32_OP_D( menu.AppendMenu(MF_STRING,ID_ADD_NEW,TEXT(AddNewUIElementCommand)) ); + WIN32_OP_D( menu.SetMenuDefaultItem(ID_ADD_NEW) ); + } else { + service_ptr_t<ui_element> elem; + pfc::string8 name; + if (find(elem,sourceItemGuid)) { + elem->get_name(name); + } else { + name = "<unknown element>"; + } + WIN32_OP_D( menu.AppendMenu(MF_STRING | MF_DISABLED,ID_LABEL,pfc::stringcvt::string_os_from_utf8(name)) ); + WIN32_OP_D( menu.AppendMenu(MF_SEPARATOR,(UINT_PTR)0,TEXT("")) ); + WIN32_OP_D( menu.AppendMenu(MF_STRING,ID_REPLACE,TEXT(ReplaceUIElementCommand)) ); + } + WIN32_OP_D( menu.AppendMenu(MF_SEPARATOR,(UINT_PTR)0,TEXT("")) ); + + //menu.AppendMenu(MF_STRING,ID_REPLACE,TEXT(ReplaceUIElementCommand)); + WIN32_OP_D( menu.AppendMenu(MF_STRING | (sourceItemEmpty ? (MF_DISABLED|MF_GRAYED) : 0),ID_CUT,TEXT(CutUIElementCommand)) ); + WIN32_OP_D( menu.AppendMenu(MF_STRING | (sourceItemEmpty ? (MF_DISABLED|MF_GRAYED) : 0),ID_COPY,TEXT(CopyUIElementCommand)) ); + WIN32_OP_D( menu.AppendMenu(MF_STRING | (api->is_paste_available() ? 0 : (MF_DISABLED|MF_GRAYED)),ID_PASTE,TEXT(PasteUIElementCommand)) ); + + unsigned custom_walk = ID_CUSTOM_BASE; + unsigned custom_base_host = ~0, custom_base_client = ~0; + + if (host_edit_mode_context_menu_test(p_id,pt,fromkeyboard)) { + menu.AppendMenu(MF_SEPARATOR,(UINT_PTR)0,TEXT("")); + custom_base_host = custom_walk; + host_edit_mode_context_menu_build(p_id,pt,fromkeyboard,menu,custom_walk); + } + + { + if (p_item->edit_mode_context_menu_test(pt,fromkeyboard)) { + WIN32_OP_D( menu.AppendMenu(MF_SEPARATOR,(UINT_PTR)0,TEXT("")) ); + custom_base_client = custom_walk; + p_item->edit_mode_context_menu_build(pt,fromkeyboard,menu,custom_walk); + } + } + int cmd; + + { + ui_element_highlight_scope s(p_item->get_wnd()); + CMyMenuSelectionReceiver receiver(p_parent,this,p_item,p_id,custom_base_host,custom_base_client); + cmd = menu.TrackPopupMenu(TPM_RIGHTBUTTON|TPM_NONOTIFY|TPM_RETURNCMD,pt.x,pt.y,receiver); + } + + if (cmd > 0) { + switch(cmd) { + case ID_REPLACE: + case ID_ADD_NEW: + replace_dialog(p_item->get_wnd(),p_id,p_item->get_guid()); + break; + case ID_COPY: + api->copy(p_item); + break; + case ID_CUT: + api->copy(p_item); + p_item.release(); + host_replace_element(p_id,pfc::guid_null); + break; + case ID_PASTE: + host_paste_element(p_id); + break; + default: + if ((unsigned)cmd >= custom_base_client) { + p_item->edit_mode_context_menu_command(pt,fromkeyboard,(unsigned)cmd,custom_base_client); + } else if ((unsigned)cmd >= custom_base_host) { + host_edit_mode_context_menu_command(p_id,pt,fromkeyboard,(unsigned)cmd,custom_base_host); + } + break; + } + } +} + +void ui_element_helpers::ui_element_edit_tools::on_elem_replace(unsigned p_id, GUID const & newGuid) { + m_replace_dialog.Detach(); + + if ( newGuid != pfc::guid_null ) { + host_replace_element(p_id,newGuid); + } +} + +void ui_element_helpers::ui_element_edit_tools::replace_dialog(HWND p_parent,unsigned p_id,const GUID & p_current) { + _release_replace_dialog(); + + auto ks = m_killSwitch; + + auto reply = ui_element_replace_dialog_notify::create( [=] ( GUID newGUID ) { + if ( !*ks) { + on_elem_replace(p_id, newGUID ); + } + } ); + + HWND dlg = ui_element_common_methods_v3::get()->replace_element_dialog_start( p_parent, p_current, reply ); + + m_replace_dialog.Attach( dlg ); +} + +const char * ui_element_helpers::ui_element_edit_tools::description_from_menu_command(unsigned p_id) { + using namespace HostHelperIDs; + switch(p_id) { + case ID_REPLACE: + return ReplaceUIElementDescription; + case ID_CUT: + return CutUIElementDescription; + case ID_COPY: + return CopyUIElementDescription; + case ID_PASTE: + return PasteUIElementDescription; + case ID_ADD_NEW: + return AddNewUIElementDescription; + default: + return NULL; + } +} + +void ui_element_helpers::ui_element_edit_tools::_release_replace_dialog() { + if (m_replace_dialog.m_hWnd != NULL) { + m_replace_dialog.DestroyWindow(); + } +} + +bool ui_element_helpers::enforce_min_max_info(CWindow p_window,ui_element_min_max_info const & p_info) { + CRect rect; + WIN32_OP_D( p_window.GetWindowRect(&rect) ); + t_uint32 width = (t_uint32) rect.Width(); + t_uint32 height = (t_uint32) rect.Height(); + bool changed = false; + if (width < p_info.m_min_width) {changed = true; width = p_info.m_min_width;} + if (width > p_info.m_max_width) {changed = true; width = p_info.m_max_width;} + if (height < p_info.m_min_height) {changed = true; height = p_info.m_min_height;} + if (height > p_info.m_max_height) {changed = true; height = p_info.m_max_height;} + if (changed) { + WIN32_OP_D( p_window.SetWindowPos(NULL,0,0,width,height,SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER) ); + } + return changed; +} + + + +void ui_element_helpers::handle_WM_GETMINMAXINFO(LPARAM p_lp,const ui_element_min_max_info & p_myinfo) { + MINMAXINFO * info = reinterpret_cast<MINMAXINFO *>(p_lp); + /*console::formatter() << "handle_WM_GETMINMAXINFO"; + console::formatter() << p_myinfo.m_min_width << ", " << p_myinfo.m_min_height; + console::formatter() << info->ptMinTrackSize << ", " << info->ptMaxTrackSize;*/ + pfc::max_acc(info->ptMinTrackSize.x,(LONG)p_myinfo.m_min_width); + pfc::max_acc(info->ptMinTrackSize.y,(LONG)p_myinfo.m_min_height); + if ((LONG) p_myinfo.m_max_width >= 0) pfc::min_acc(info->ptMaxTrackSize.x, (LONG) p_myinfo.m_max_width); + if ((LONG) p_myinfo.m_max_height >= 0) pfc::min_acc(info->ptMaxTrackSize.y, (LONG) p_myinfo.m_max_height); + //console::formatter() << info->ptMinTrackSize << ", " << info->ptMaxTrackSize; +} + +bool ui_element_helpers::ui_element_edit_tools::host_paste_element(unsigned p_id) { + pfc::com_ptr_t<IDataObject> obj; + if (SUCCEEDED(OleGetClipboard(obj.receive_ptr()))) { + DWORD effect; + ui_element_config::ptr cfg; + if (static_api_ptr_t<ui_element_common_methods>()->parse_dataobject(obj,cfg,effect)) { + host_replace_element(p_id, cfg); + IDataObjectUtils::SetDataObjectDWORD(obj, RegisterClipboardFormat(CFSTR_PERFORMEDDROPEFFECT), effect); + IDataObjectUtils::PasteSucceeded(obj,effect); + if (effect == DROPEFFECT_MOVE) OleSetClipboard(NULL); + return true; + } + } + return false; +} + + +bool ui_element_helpers::recurse_for_elem_config(ui_element_config::ptr root, ui_element_config::ptr & out, const GUID & toFind) { + const GUID rootID = root->get_guid(); + if (rootID == toFind) { + out = root; return true; + } + ui_element::ptr elem; + if (!find(elem, rootID)) return false; + ui_element_children_enumerator::ptr children; + try { + children = elem->enumerate_children(root); + } catch(exception_io_data const &) {return false;} + if (children.is_empty()) return false; + const t_size childrenTotal = children->get_count(); + for(t_size walk = 0; walk < childrenTotal; ++walk) { + if (recurse_for_elem_config(children->get_item(walk), out, toFind)) return true; + } + return false; +} + +bool ui_element_helpers::ui_element_instance_host_base::grabTopPriorityVisibleChild(ui_element_instance_ptr & out, t_size & outWhich, double & outPriority) { + double bestPriority = 0; + ui_element_instance_ptr best; + t_size bestWhich = ~0; + const t_size count = host_get_children_count(); + for (t_size walk = 0; walk < count; ++walk) if (this->host_is_child_visible(walk) ) { + ui_element_instance_ptr item = host_get_child(walk); + if (item.is_valid() ) { + const double priority = item->get_focus_priority(); + if (best.is_empty() || childPriorityCompare(walk, priority, bestPriority)) { + best = item; bestPriority = priority; bestWhich = walk; + } + } + } + if (best.is_empty()) return false; + out = best; outPriority = bestPriority; outWhich = bestWhich; return true; +} +bool ui_element_helpers::ui_element_instance_host_base::grabTopPriorityChild(ui_element_instance_ptr & out, t_size & outWhich, double & outPriority, const GUID & subclass) { + double bestPriority = 0; + ui_element_instance_ptr best; + t_size bestWhich = ~0; + const t_size count = host_get_children_count(); + for (t_size walk = 0; walk < count; ++walk) { + ui_element_instance_ptr item = host_get_child(walk); + if (item.is_valid()) { + double priority; + if (item->get_focus_priority_subclass(priority, subclass)) { + if (best.is_empty() || childPriorityCompare(walk, priority, bestPriority)) { + best = item; bestPriority = priority; bestWhich = walk; + } + } + } + } + if (best.is_empty()) return false; + out = best; outPriority = bestPriority; outWhich = bestWhich; return true; +} + +void ui_element_instance_standard_context_menu(service_ptr_t<ui_element_instance> p_elem, LPARAM p_pt) { + CPoint pt; + bool fromKeyboard; + if (p_pt == -1) { + fromKeyboard = true; + if (!p_elem->edit_mode_context_menu_get_focus_point(pt)) { + CRect rc; + WIN32_OP_D(GetWindowRect(p_elem->get_wnd(), rc)); + pt = rc.CenterPoint(); + } + } else { + fromKeyboard = false; + pt = p_pt; + } + if (p_elem->edit_mode_context_menu_test(pt, fromKeyboard)) { + const unsigned idBase = 1; + CMenu menu; + WIN32_OP_D(menu.CreatePopupMenu()); + p_elem->edit_mode_context_menu_build(pt, fromKeyboard, menu, idBase); + + int cmd; + { + CMenuSelectionReceiver_UiElement receiver(p_elem, idBase); + cmd = menu.TrackPopupMenu(TPM_RIGHTBUTTON | TPM_NONOTIFY | TPM_RETURNCMD, pt.x, pt.y, receiver); + } + if (cmd > 0) p_elem->edit_mode_context_menu_command(pt, fromKeyboard, cmd, idBase); + } +} +void ui_element_instance_standard_context_menu_eh(service_ptr_t<ui_element_instance> p_elem, LPARAM p_pt) { + try { + ui_element_instance_standard_context_menu(p_elem, p_pt); + } catch (std::exception const & e) { + console::complain("Context menu failure", e); + } +} + +#endif // FOOBAR2000_TARGET_VERSION >= 79
