Mercurial > foo_out_sdl
comparison 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 |
comparison
equal
deleted
inserted
replaced
| 0:e9bb126753e7 | 1:20d02a178406 |
|---|---|
| 1 #include "stdafx.h" | |
| 2 | |
| 3 #if FOOBAR2000_TARGET_VERSION >= 79 | |
| 4 | |
| 5 #include "ui_element_helpers.h" | |
| 6 #include <libPPUI/IDataObjectUtils.h> | |
| 7 #include "atl-misc.h" | |
| 8 | |
| 9 namespace ui_element_helpers { | |
| 10 | |
| 11 ui_element_instance_ptr instantiate_dummy(HWND p_parent,ui_element_config::ptr cfg, ui_element_instance_callback_ptr p_callback) { | |
| 12 service_ptr_t<ui_element> ptr; | |
| 13 if (!find(ptr,pfc::guid_null)) uBugCheck(); | |
| 14 return ptr->instantiate(p_parent,cfg,p_callback); | |
| 15 } | |
| 16 ui_element_instance_ptr instantiate(HWND p_parent,ui_element_config::ptr cfg,ui_element_instance_callback_ptr p_callback) { | |
| 17 try { | |
| 18 service_ptr_t<ui_element> ptr; | |
| 19 if (!find(ptr,cfg->get_guid())) throw exception_io_data("UI Element Not Found"); | |
| 20 auto ret = ptr->instantiate(p_parent,cfg,p_callback); | |
| 21 if (ret.is_empty()) throw std::runtime_error("Null UI Element returned"); | |
| 22 return ret; | |
| 23 } catch(std::exception const & e) { | |
| 24 console::complain("UI Element instantiation failure",e); | |
| 25 return instantiate_dummy(p_parent,cfg,p_callback); | |
| 26 } | |
| 27 } | |
| 28 | |
| 29 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) { | |
| 30 if (p_element.is_valid() && cfg->get_guid() == p_element->get_guid()) { | |
| 31 p_element->set_configuration(cfg); | |
| 32 return p_element; | |
| 33 } else { | |
| 34 return instantiate(p_parent,cfg,p_callback); | |
| 35 } | |
| 36 } | |
| 37 | |
| 38 bool find(service_ptr_t<ui_element> & p_out,const GUID & p_guid) { | |
| 39 return service_by_guid(p_out,p_guid); | |
| 40 } | |
| 41 | |
| 42 ui_element_children_enumerator_ptr enumerate_children(ui_element_config::ptr cfg) { | |
| 43 service_ptr_t<ui_element> ptr; | |
| 44 if (!find(ptr,cfg->get_guid())) return NULL; | |
| 45 try { | |
| 46 return ptr->enumerate_children(cfg); | |
| 47 } catch(exception_io_data const &) { | |
| 48 return NULL; | |
| 49 } | |
| 50 } | |
| 51 }; | |
| 52 | |
| 53 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) { | |
| 54 ui_element_config::ptr cfg; | |
| 55 try { | |
| 56 if (p_item.is_empty()) { | |
| 57 service_ptr_t<ui_element> ptr; | |
| 58 if (!find(ptr,p_guid)) throw exception_io_data("UI Element Not Found"); | |
| 59 cfg = ptr->get_default_configuration(); | |
| 60 p_item = ptr->instantiate(p_parent,cfg,p_callback); | |
| 61 } else if (p_item->get_guid() != p_guid) { | |
| 62 service_ptr_t<ui_element> ptr; | |
| 63 if (!find(ptr,p_guid)) throw exception_io_data("UI Element Not Found"); | |
| 64 cfg = ptr->import(p_item->get_configuration()); | |
| 65 //p_item.release(); | |
| 66 if (cfg.is_empty()) cfg = ptr->get_default_configuration(); | |
| 67 p_item = ptr->instantiate(p_parent,cfg,p_callback); | |
| 68 } | |
| 69 } catch(std::exception const & e) { | |
| 70 console::complain("UI Element instantiation failure",e); | |
| 71 if (cfg.is_empty()) cfg = ui_element_config::g_create_empty(); | |
| 72 p_item = instantiate_dummy(p_parent,cfg,p_callback); | |
| 73 } | |
| 74 } | |
| 75 | |
| 76 | |
| 77 | |
| 78 namespace { | |
| 79 class CMyMenuSelectionReceiver : public CMenuSelectionReceiver { | |
| 80 public: | |
| 81 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) {} | |
| 82 bool QueryHint(unsigned p_id,pfc::string_base & p_out) { | |
| 83 if (p_id >= m_client_base) { | |
| 84 return m_client->edit_mode_context_menu_get_description(p_id,m_client_base,p_out); | |
| 85 } else if (p_id >= m_host_base) { | |
| 86 return m_host->host_edit_mode_context_menu_get_description(m_client_id,p_id,m_host_base,p_out); | |
| 87 } else { | |
| 88 const char * msg = ui_element_helpers::ui_element_edit_tools::description_from_menu_command(p_id); | |
| 89 if (msg == NULL) return false; | |
| 90 p_out = msg; | |
| 91 return true; | |
| 92 } | |
| 93 } | |
| 94 private: | |
| 95 ui_element_helpers::ui_element_edit_tools * const m_host; | |
| 96 ui_element_instance_ptr const m_client; | |
| 97 unsigned const m_client_id,m_host_base,m_client_base; | |
| 98 }; | |
| 99 }; | |
| 100 | |
| 101 namespace HostHelperIDs { | |
| 102 enum {ID_LABEL, ID_REPLACE = 1, ID_ADD_NEW, ID_CUT, ID_COPY, ID_PASTE, ID_CUSTOM_BASE}; | |
| 103 } | |
| 104 | |
| 105 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) { | |
| 106 using namespace HostHelperIDs; | |
| 107 static_api_ptr_t<ui_element_common_methods> api; | |
| 108 POINT pt; | |
| 109 bool fromkeyboard = false; | |
| 110 if (p_point == (LPARAM)(-1)) { | |
| 111 fromkeyboard = true; | |
| 112 if (!p_item->edit_mode_context_menu_get_focus_point(pt)) { | |
| 113 CRect rect; | |
| 114 WIN32_OP_D( CWindow(p_item->get_wnd()).GetWindowRect(&rect) ); | |
| 115 pt = rect.CenterPoint(); | |
| 116 } | |
| 117 } else { | |
| 118 pt.x = (short)LOWORD(p_point); | |
| 119 pt.y = (short)HIWORD(p_point); | |
| 120 } | |
| 121 | |
| 122 CMenu menu; | |
| 123 WIN32_OP_D( menu.CreatePopupMenu() ); | |
| 124 | |
| 125 const GUID sourceItemGuid = p_item->get_guid(); | |
| 126 const bool sourceItemEmpty = !!(sourceItemGuid == pfc::guid_null); | |
| 127 | |
| 128 if (sourceItemEmpty) { | |
| 129 WIN32_OP_D( menu.AppendMenu(MF_STRING,ID_ADD_NEW,TEXT(AddNewUIElementCommand)) ); | |
| 130 WIN32_OP_D( menu.SetMenuDefaultItem(ID_ADD_NEW) ); | |
| 131 } else { | |
| 132 service_ptr_t<ui_element> elem; | |
| 133 pfc::string8 name; | |
| 134 if (find(elem,sourceItemGuid)) { | |
| 135 elem->get_name(name); | |
| 136 } else { | |
| 137 name = "<unknown element>"; | |
| 138 } | |
| 139 WIN32_OP_D( menu.AppendMenu(MF_STRING | MF_DISABLED,ID_LABEL,pfc::stringcvt::string_os_from_utf8(name)) ); | |
| 140 WIN32_OP_D( menu.AppendMenu(MF_SEPARATOR,(UINT_PTR)0,TEXT("")) ); | |
| 141 WIN32_OP_D( menu.AppendMenu(MF_STRING,ID_REPLACE,TEXT(ReplaceUIElementCommand)) ); | |
| 142 } | |
| 143 WIN32_OP_D( menu.AppendMenu(MF_SEPARATOR,(UINT_PTR)0,TEXT("")) ); | |
| 144 | |
| 145 //menu.AppendMenu(MF_STRING,ID_REPLACE,TEXT(ReplaceUIElementCommand)); | |
| 146 WIN32_OP_D( menu.AppendMenu(MF_STRING | (sourceItemEmpty ? (MF_DISABLED|MF_GRAYED) : 0),ID_CUT,TEXT(CutUIElementCommand)) ); | |
| 147 WIN32_OP_D( menu.AppendMenu(MF_STRING | (sourceItemEmpty ? (MF_DISABLED|MF_GRAYED) : 0),ID_COPY,TEXT(CopyUIElementCommand)) ); | |
| 148 WIN32_OP_D( menu.AppendMenu(MF_STRING | (api->is_paste_available() ? 0 : (MF_DISABLED|MF_GRAYED)),ID_PASTE,TEXT(PasteUIElementCommand)) ); | |
| 149 | |
| 150 unsigned custom_walk = ID_CUSTOM_BASE; | |
| 151 unsigned custom_base_host = ~0, custom_base_client = ~0; | |
| 152 | |
| 153 if (host_edit_mode_context_menu_test(p_id,pt,fromkeyboard)) { | |
| 154 menu.AppendMenu(MF_SEPARATOR,(UINT_PTR)0,TEXT("")); | |
| 155 custom_base_host = custom_walk; | |
| 156 host_edit_mode_context_menu_build(p_id,pt,fromkeyboard,menu,custom_walk); | |
| 157 } | |
| 158 | |
| 159 { | |
| 160 if (p_item->edit_mode_context_menu_test(pt,fromkeyboard)) { | |
| 161 WIN32_OP_D( menu.AppendMenu(MF_SEPARATOR,(UINT_PTR)0,TEXT("")) ); | |
| 162 custom_base_client = custom_walk; | |
| 163 p_item->edit_mode_context_menu_build(pt,fromkeyboard,menu,custom_walk); | |
| 164 } | |
| 165 } | |
| 166 int cmd; | |
| 167 | |
| 168 { | |
| 169 ui_element_highlight_scope s(p_item->get_wnd()); | |
| 170 CMyMenuSelectionReceiver receiver(p_parent,this,p_item,p_id,custom_base_host,custom_base_client); | |
| 171 cmd = menu.TrackPopupMenu(TPM_RIGHTBUTTON|TPM_NONOTIFY|TPM_RETURNCMD,pt.x,pt.y,receiver); | |
| 172 } | |
| 173 | |
| 174 if (cmd > 0) { | |
| 175 switch(cmd) { | |
| 176 case ID_REPLACE: | |
| 177 case ID_ADD_NEW: | |
| 178 replace_dialog(p_item->get_wnd(),p_id,p_item->get_guid()); | |
| 179 break; | |
| 180 case ID_COPY: | |
| 181 api->copy(p_item); | |
| 182 break; | |
| 183 case ID_CUT: | |
| 184 api->copy(p_item); | |
| 185 p_item.release(); | |
| 186 host_replace_element(p_id,pfc::guid_null); | |
| 187 break; | |
| 188 case ID_PASTE: | |
| 189 host_paste_element(p_id); | |
| 190 break; | |
| 191 default: | |
| 192 if ((unsigned)cmd >= custom_base_client) { | |
| 193 p_item->edit_mode_context_menu_command(pt,fromkeyboard,(unsigned)cmd,custom_base_client); | |
| 194 } else if ((unsigned)cmd >= custom_base_host) { | |
| 195 host_edit_mode_context_menu_command(p_id,pt,fromkeyboard,(unsigned)cmd,custom_base_host); | |
| 196 } | |
| 197 break; | |
| 198 } | |
| 199 } | |
| 200 } | |
| 201 | |
| 202 void ui_element_helpers::ui_element_edit_tools::on_elem_replace(unsigned p_id, GUID const & newGuid) { | |
| 203 m_replace_dialog.Detach(); | |
| 204 | |
| 205 if ( newGuid != pfc::guid_null ) { | |
| 206 host_replace_element(p_id,newGuid); | |
| 207 } | |
| 208 } | |
| 209 | |
| 210 void ui_element_helpers::ui_element_edit_tools::replace_dialog(HWND p_parent,unsigned p_id,const GUID & p_current) { | |
| 211 _release_replace_dialog(); | |
| 212 | |
| 213 auto ks = m_killSwitch; | |
| 214 | |
| 215 auto reply = ui_element_replace_dialog_notify::create( [=] ( GUID newGUID ) { | |
| 216 if ( !*ks) { | |
| 217 on_elem_replace(p_id, newGUID ); | |
| 218 } | |
| 219 } ); | |
| 220 | |
| 221 HWND dlg = ui_element_common_methods_v3::get()->replace_element_dialog_start( p_parent, p_current, reply ); | |
| 222 | |
| 223 m_replace_dialog.Attach( dlg ); | |
| 224 } | |
| 225 | |
| 226 const char * ui_element_helpers::ui_element_edit_tools::description_from_menu_command(unsigned p_id) { | |
| 227 using namespace HostHelperIDs; | |
| 228 switch(p_id) { | |
| 229 case ID_REPLACE: | |
| 230 return ReplaceUIElementDescription; | |
| 231 case ID_CUT: | |
| 232 return CutUIElementDescription; | |
| 233 case ID_COPY: | |
| 234 return CopyUIElementDescription; | |
| 235 case ID_PASTE: | |
| 236 return PasteUIElementDescription; | |
| 237 case ID_ADD_NEW: | |
| 238 return AddNewUIElementDescription; | |
| 239 default: | |
| 240 return NULL; | |
| 241 } | |
| 242 } | |
| 243 | |
| 244 void ui_element_helpers::ui_element_edit_tools::_release_replace_dialog() { | |
| 245 if (m_replace_dialog.m_hWnd != NULL) { | |
| 246 m_replace_dialog.DestroyWindow(); | |
| 247 } | |
| 248 } | |
| 249 | |
| 250 bool ui_element_helpers::enforce_min_max_info(CWindow p_window,ui_element_min_max_info const & p_info) { | |
| 251 CRect rect; | |
| 252 WIN32_OP_D( p_window.GetWindowRect(&rect) ); | |
| 253 t_uint32 width = (t_uint32) rect.Width(); | |
| 254 t_uint32 height = (t_uint32) rect.Height(); | |
| 255 bool changed = false; | |
| 256 if (width < p_info.m_min_width) {changed = true; width = p_info.m_min_width;} | |
| 257 if (width > p_info.m_max_width) {changed = true; width = p_info.m_max_width;} | |
| 258 if (height < p_info.m_min_height) {changed = true; height = p_info.m_min_height;} | |
| 259 if (height > p_info.m_max_height) {changed = true; height = p_info.m_max_height;} | |
| 260 if (changed) { | |
| 261 WIN32_OP_D( p_window.SetWindowPos(NULL,0,0,width,height,SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER) ); | |
| 262 } | |
| 263 return changed; | |
| 264 } | |
| 265 | |
| 266 | |
| 267 | |
| 268 void ui_element_helpers::handle_WM_GETMINMAXINFO(LPARAM p_lp,const ui_element_min_max_info & p_myinfo) { | |
| 269 MINMAXINFO * info = reinterpret_cast<MINMAXINFO *>(p_lp); | |
| 270 /*console::formatter() << "handle_WM_GETMINMAXINFO"; | |
| 271 console::formatter() << p_myinfo.m_min_width << ", " << p_myinfo.m_min_height; | |
| 272 console::formatter() << info->ptMinTrackSize << ", " << info->ptMaxTrackSize;*/ | |
| 273 pfc::max_acc(info->ptMinTrackSize.x,(LONG)p_myinfo.m_min_width); | |
| 274 pfc::max_acc(info->ptMinTrackSize.y,(LONG)p_myinfo.m_min_height); | |
| 275 if ((LONG) p_myinfo.m_max_width >= 0) pfc::min_acc(info->ptMaxTrackSize.x, (LONG) p_myinfo.m_max_width); | |
| 276 if ((LONG) p_myinfo.m_max_height >= 0) pfc::min_acc(info->ptMaxTrackSize.y, (LONG) p_myinfo.m_max_height); | |
| 277 //console::formatter() << info->ptMinTrackSize << ", " << info->ptMaxTrackSize; | |
| 278 } | |
| 279 | |
| 280 bool ui_element_helpers::ui_element_edit_tools::host_paste_element(unsigned p_id) { | |
| 281 pfc::com_ptr_t<IDataObject> obj; | |
| 282 if (SUCCEEDED(OleGetClipboard(obj.receive_ptr()))) { | |
| 283 DWORD effect; | |
| 284 ui_element_config::ptr cfg; | |
| 285 if (static_api_ptr_t<ui_element_common_methods>()->parse_dataobject(obj,cfg,effect)) { | |
| 286 host_replace_element(p_id, cfg); | |
| 287 IDataObjectUtils::SetDataObjectDWORD(obj, RegisterClipboardFormat(CFSTR_PERFORMEDDROPEFFECT), effect); | |
| 288 IDataObjectUtils::PasteSucceeded(obj,effect); | |
| 289 if (effect == DROPEFFECT_MOVE) OleSetClipboard(NULL); | |
| 290 return true; | |
| 291 } | |
| 292 } | |
| 293 return false; | |
| 294 } | |
| 295 | |
| 296 | |
| 297 bool ui_element_helpers::recurse_for_elem_config(ui_element_config::ptr root, ui_element_config::ptr & out, const GUID & toFind) { | |
| 298 const GUID rootID = root->get_guid(); | |
| 299 if (rootID == toFind) { | |
| 300 out = root; return true; | |
| 301 } | |
| 302 ui_element::ptr elem; | |
| 303 if (!find(elem, rootID)) return false; | |
| 304 ui_element_children_enumerator::ptr children; | |
| 305 try { | |
| 306 children = elem->enumerate_children(root); | |
| 307 } catch(exception_io_data const &) {return false;} | |
| 308 if (children.is_empty()) return false; | |
| 309 const t_size childrenTotal = children->get_count(); | |
| 310 for(t_size walk = 0; walk < childrenTotal; ++walk) { | |
| 311 if (recurse_for_elem_config(children->get_item(walk), out, toFind)) return true; | |
| 312 } | |
| 313 return false; | |
| 314 } | |
| 315 | |
| 316 bool ui_element_helpers::ui_element_instance_host_base::grabTopPriorityVisibleChild(ui_element_instance_ptr & out, t_size & outWhich, double & outPriority) { | |
| 317 double bestPriority = 0; | |
| 318 ui_element_instance_ptr best; | |
| 319 t_size bestWhich = ~0; | |
| 320 const t_size count = host_get_children_count(); | |
| 321 for (t_size walk = 0; walk < count; ++walk) if (this->host_is_child_visible(walk) ) { | |
| 322 ui_element_instance_ptr item = host_get_child(walk); | |
| 323 if (item.is_valid() ) { | |
| 324 const double priority = item->get_focus_priority(); | |
| 325 if (best.is_empty() || childPriorityCompare(walk, priority, bestPriority)) { | |
| 326 best = item; bestPriority = priority; bestWhich = walk; | |
| 327 } | |
| 328 } | |
| 329 } | |
| 330 if (best.is_empty()) return false; | |
| 331 out = best; outPriority = bestPriority; outWhich = bestWhich; return true; | |
| 332 } | |
| 333 bool ui_element_helpers::ui_element_instance_host_base::grabTopPriorityChild(ui_element_instance_ptr & out, t_size & outWhich, double & outPriority, const GUID & subclass) { | |
| 334 double bestPriority = 0; | |
| 335 ui_element_instance_ptr best; | |
| 336 t_size bestWhich = ~0; | |
| 337 const t_size count = host_get_children_count(); | |
| 338 for (t_size walk = 0; walk < count; ++walk) { | |
| 339 ui_element_instance_ptr item = host_get_child(walk); | |
| 340 if (item.is_valid()) { | |
| 341 double priority; | |
| 342 if (item->get_focus_priority_subclass(priority, subclass)) { | |
| 343 if (best.is_empty() || childPriorityCompare(walk, priority, bestPriority)) { | |
| 344 best = item; bestPriority = priority; bestWhich = walk; | |
| 345 } | |
| 346 } | |
| 347 } | |
| 348 } | |
| 349 if (best.is_empty()) return false; | |
| 350 out = best; outPriority = bestPriority; outWhich = bestWhich; return true; | |
| 351 } | |
| 352 | |
| 353 void ui_element_instance_standard_context_menu(service_ptr_t<ui_element_instance> p_elem, LPARAM p_pt) { | |
| 354 CPoint pt; | |
| 355 bool fromKeyboard; | |
| 356 if (p_pt == -1) { | |
| 357 fromKeyboard = true; | |
| 358 if (!p_elem->edit_mode_context_menu_get_focus_point(pt)) { | |
| 359 CRect rc; | |
| 360 WIN32_OP_D(GetWindowRect(p_elem->get_wnd(), rc)); | |
| 361 pt = rc.CenterPoint(); | |
| 362 } | |
| 363 } else { | |
| 364 fromKeyboard = false; | |
| 365 pt = p_pt; | |
| 366 } | |
| 367 if (p_elem->edit_mode_context_menu_test(pt, fromKeyboard)) { | |
| 368 const unsigned idBase = 1; | |
| 369 CMenu menu; | |
| 370 WIN32_OP_D(menu.CreatePopupMenu()); | |
| 371 p_elem->edit_mode_context_menu_build(pt, fromKeyboard, menu, idBase); | |
| 372 | |
| 373 int cmd; | |
| 374 { | |
| 375 CMenuSelectionReceiver_UiElement receiver(p_elem, idBase); | |
| 376 cmd = menu.TrackPopupMenu(TPM_RIGHTBUTTON | TPM_NONOTIFY | TPM_RETURNCMD, pt.x, pt.y, receiver); | |
| 377 } | |
| 378 if (cmd > 0) p_elem->edit_mode_context_menu_command(pt, fromKeyboard, cmd, idBase); | |
| 379 } | |
| 380 } | |
| 381 void ui_element_instance_standard_context_menu_eh(service_ptr_t<ui_element_instance> p_elem, LPARAM p_pt) { | |
| 382 try { | |
| 383 ui_element_instance_standard_context_menu(p_elem, p_pt); | |
| 384 } catch (std::exception const & e) { | |
| 385 console::complain("Context menu failure", e); | |
| 386 } | |
| 387 } | |
| 388 | |
| 389 #endif // FOOBAR2000_TARGET_VERSION >= 79 |
