From 62d3408cc4a32eab2cd3048d1eec3bc42dc56c84 Mon Sep 17 00:00:00 2001 From: Vas Crabb Date: Tue, 26 Jan 2021 02:51:54 +1100 Subject: win32 debugger: Added context menu with Copy Visible and Paste commands to debug views (partially addresses #6066). --- src/osd/modules/debugger/win/debugviewinfo.cpp | 202 +++++++++++++++++++++---- src/osd/modules/debugger/win/debugviewinfo.h | 11 ++ 2 files changed, 183 insertions(+), 30 deletions(-) diff --git a/src/osd/modules/debugger/win/debugviewinfo.cpp b/src/osd/modules/debugger/win/debugviewinfo.cpp index d40684c5f99..a6fa3308ecb 100644 --- a/src/osd/modules/debugger/win/debugviewinfo.cpp +++ b/src/osd/modules/debugger/win/debugviewinfo.cpp @@ -19,6 +19,8 @@ #include "winutil.h" +#include + // debugger view styles #define DEBUG_VIEW_STYLE WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN @@ -46,14 +48,15 @@ debugview_info::debugview_info(debugger_windows_interface &debugger, debugwin_in m_view(nullptr), m_wnd(nullptr), m_hscroll(nullptr), - m_vscroll(nullptr) + m_vscroll(nullptr), + m_contextmenu(nullptr) { register_window_class(); // create the child view m_wnd = CreateWindowEx(DEBUG_VIEW_STYLE_EX, TEXT("MAMEDebugView"), nullptr, DEBUG_VIEW_STYLE, 0, 0, 100, 100, parent, nullptr, GetModuleHandleUni(), this); - if (m_wnd == nullptr) + if (!m_wnd) goto cleanup; // create the scroll bars @@ -61,27 +64,27 @@ debugview_info::debugview_info(debugger_windows_interface &debugger, debugwin_in 0, 0, 100, CW_USEDEFAULT, m_wnd, nullptr, GetModuleHandleUni(), this); m_vscroll = CreateWindowEx(VSCROLL_STYLE_EX, TEXT("SCROLLBAR"), nullptr, VSCROLL_STYLE, 0, 0, CW_USEDEFAULT, 100, m_wnd, nullptr, GetModuleHandleUni(), this); - if ((m_hscroll == nullptr) || (m_vscroll == nullptr)) + if (!m_hscroll || !m_vscroll) goto cleanup; // create the debug view m_view = machine().debug_view().alloc_view(type, &debugview_info::static_update, this); - if (m_view == nullptr) + if (!m_view) goto cleanup; return; cleanup: - if (m_hscroll != nullptr) + if (m_hscroll) DestroyWindow(m_hscroll); m_hscroll = nullptr; - if (m_vscroll != nullptr) + if (m_vscroll) DestroyWindow(m_vscroll); m_vscroll = nullptr; - if (m_wnd != nullptr) + if (m_wnd) DestroyWindow(m_wnd); m_wnd = nullptr; - if (m_view != nullptr) + if (m_view) machine().debug_view().free_view(*m_view); m_view = nullptr; } @@ -89,7 +92,9 @@ cleanup: debugview_info::~debugview_info() { - if (m_wnd != nullptr) + if (m_contextmenu) + DestroyMenu(m_contextmenu); + if (m_wnd) DestroyWindow(m_wnd); if (m_view) machine().debug_view().free_view(*m_view); @@ -119,7 +124,7 @@ uint32_t debugview_info::maxwidth() if (max < chars) max = chars; } - if (cursource != nullptr) + if (cursource) m_view->set_source(*cursource); return (max * metrics().debug_font_width()) + metrics().vscroll_width(); } @@ -164,27 +169,23 @@ void debugview_info::send_vscroll(int delta) void debugview_info::send_pageup() { if (m_vscroll) - { SendMessage(m_wnd, WM_VSCROLL, SB_PAGELEFT, (LPARAM)m_vscroll); - } } void debugview_info::send_pagedown() { if (m_vscroll) - { SendMessage(m_wnd, WM_VSCROLL, SB_PAGERIGHT, (LPARAM)m_vscroll); - } } char const *debugview_info::source_name() const { - if (m_view != nullptr) + if (m_view) { debug_view_source const *const source = m_view->source(); - if (source != nullptr) + if (source) return source->name(); } return ""; @@ -193,10 +194,10 @@ char const *debugview_info::source_name() const device_t *debugview_info::source_device() const { - if (m_view != nullptr) + if (m_view) { debug_view_source const *const source = m_view->source(); - if (source != nullptr) + if (source) return source->device(); } return nullptr; @@ -205,10 +206,10 @@ device_t *debugview_info::source_device() const bool debugview_info::source_is_visible_cpu() const { - if (m_view != nullptr) + if (m_view) { const debug_view_source *const source = m_view->source(); - return (source != nullptr) && (machine().debugger().console().get_visible_cpu() == source->device()); + return source && (machine().debugger().console().get_visible_cpu() == source->device()); } return false; } @@ -216,10 +217,10 @@ bool debugview_info::source_is_visible_cpu() const bool debugview_info::set_source_index(int index) { - if (m_view != nullptr) + if (m_view) { const debug_view_source *const source = m_view->source(index); - if (source != nullptr) + if (source) { m_view->set_source(*source); return true; @@ -231,10 +232,10 @@ bool debugview_info::set_source_index(int index) bool debugview_info::set_source_for_device(device_t &device) { - if (m_view != nullptr) + if (m_view) { const debug_view_source *const source = m_view->source_for_device(&device); - if (source != nullptr) + if (source) { m_view->set_source(*source); return true; @@ -247,7 +248,7 @@ bool debugview_info::set_source_for_device(device_t &device) bool debugview_info::set_source_for_visible_cpu() { device_t *const curcpu = machine().debugger().console().get_visible_cpu(); - if (curcpu != nullptr) + if (curcpu) return set_source_for_device(*curcpu); else return false; @@ -273,7 +274,7 @@ HWND debugview_info::create_source_combobox(HWND parent, LONG_PTR userdata) auto t_name = osd::text::to_tstring(source->name()); SendMessage(result, CB_ADDSTRING, 0, (LPARAM)t_name.c_str()); } - if (cursource != nullptr) + if (cursource) { SendMessage(result, CB_SETCURSEL, m_view->source_index(*cursource), 0); SendMessage(result, CB_SETDROPPEDWIDTH, ((maxlength + 2) * metrics().debug_font_width()) + metrics().vscroll_width(), 0); @@ -283,6 +284,109 @@ HWND debugview_info::create_source_combobox(HWND parent, LONG_PTR userdata) } +void debugview_info::add_items_to_context_menu(HMENU menu) +{ + AppendMenu(menu, MF_ENABLED, ID_CONTEXT_COPY_VISIBLE, TEXT("Copy Visible")); + AppendMenu(menu, MF_ENABLED, ID_CONTEXT_PASTE, TEXT("Paste")); +} + + +void debugview_info::handle_context_menu(unsigned command) +{ + switch (command) + { + case ID_CONTEXT_COPY_VISIBLE: + { + // get visible text + debug_view_xy const visarea = m_view->visible_size(); + debug_view_char const *viewdata = m_view->viewdata(); + if (!viewdata) + { + PlaySound(TEXT("SystemAsterisk"), nullptr, SND_SYNC); + break; + } + + // turn into a plain string, trimming trailing whitespace + std::wstring text; + for (uint32_t row = 0; row < visarea.y; row++, viewdata += visarea.x) + { + std::wstring::size_type const start = text.length(); + for (uint32_t col = 0; col < visarea.x; ++col) + text += wchar_t(viewdata[col].byte); + std::wstring::size_type const nonblank = text.find_last_not_of(L"\t\n\v\r "); + if ((nonblank != std::wstring::npos) && (nonblank >= start)) + text.resize(nonblank + 1); + text += L"\r\n"; + } + + // copy to the clipboard + HGLOBAL const clip = GlobalAlloc(GMEM_MOVEABLE, (text.length() + 1) * sizeof(wchar_t)); + if (!clip) + { + PlaySound(TEXT("SystemAsterisk"), nullptr, SND_SYNC); + break; + } + if (!OpenClipboard(m_wnd)) + { + GlobalFree(clip); + PlaySound(TEXT("SystemAsterisk"), nullptr, SND_SYNC); + break; + } + EmptyClipboard(); + LPWSTR const lock = reinterpret_cast(GlobalLock(clip)); + std::copy_n(text.c_str(), text.length() + 1, lock); + GlobalUnlock(clip); + if (!SetClipboardData(CF_UNICODETEXT, clip)) + { + GlobalFree(clip); + PlaySound(TEXT("SystemAsterisk"), nullptr, SND_SYNC); + } + CloseClipboard(); + break; + } + + case ID_CONTEXT_PASTE: + if (!IsClipboardFormatAvailable(CF_UNICODETEXT) || !OpenClipboard(m_wnd)) + { + PlaySound(TEXT("SystemAsterisk"), nullptr, SND_SYNC); + } + else + { + HGLOBAL const clip = GetClipboardData(CF_UNICODETEXT); + LPCWSTR lock = clip ? reinterpret_cast(GlobalLock(clip)) : nullptr; + if (!clip || !lock) + { + PlaySound(TEXT("SystemAsterisk"), nullptr, SND_SYNC); + } + else + { + try + { + while (*lock) + { + if ((32 <= *lock) && (127 >= *lock)) + m_view->process_char(*lock); + ++lock; + } + } + catch (...) + { + GlobalUnlock(clip); + CloseClipboard(); + throw; + } + GlobalUnlock(clip); + } + CloseClipboard(); + } + break; + + default: + osd_printf_warning("debugview_info: unhandled context menu item %u\n", command); + } +} + + void debugview_info::draw_contents(HDC windc) { debug_view_char const *viewdata = m_view->viewdata(); @@ -294,10 +398,10 @@ void debugview_info::draw_contents(HDC windc) // create a compatible DC and an offscreen bitmap HDC const dc = CreateCompatibleDC(windc); - if (dc == nullptr) + if (!dc) return; HBITMAP const bitmap = CreateCompatibleBitmap(windc, client.right, client.bottom); - if (bitmap == nullptr) + if (!bitmap) { DeleteDC(dc); return; @@ -585,6 +689,39 @@ uint32_t debugview_info::process_scroll(WORD type, HWND wnd) } +bool debugview_info::process_context_menu(int x, int y) +{ + // don't show a menu if not in client rect + RECT clientrect; + GetClientRect(m_wnd, &clientrect); + POINT loc{ x, y }; + ScreenToClient(m_wnd, &loc); + if (!PtInRect(&clientrect, loc)) + return false; + + // create the context menu if we haven’t already + if (!m_contextmenu) + { + m_contextmenu = CreatePopupMenu(); + if (!m_contextmenu) + return false; + add_items_to_context_menu(m_contextmenu); + } + + // show the context menu + BOOL const command(TrackPopupMenu( + m_contextmenu, + (GetSystemMetrics(SM_MENUDROPALIGNMENT) ? TPM_RIGHTALIGN : TPM_LEFTALIGN) | TPM_LEFTBUTTON | TPM_NONOTIFY | TPM_RETURNCMD, + x, y, + 0, + m_wnd, + nullptr)); + if (command) + handle_context_menu(command); + return true; +} + + LRESULT debugview_info::view_proc(UINT message, WPARAM wparam, LPARAM lparam) { // handle a few messages @@ -728,7 +865,7 @@ LRESULT debugview_info::view_proc(UINT message, WPARAM wparam, LPARAM lparam) break; } - // hscroll + // horizontal scroll case WM_HSCROLL: { debug_view_xy topleft = m_view->visible_position(); @@ -738,7 +875,7 @@ LRESULT debugview_info::view_proc(UINT message, WPARAM wparam, LPARAM lparam) break; } - // vscroll + // vertical scroll case WM_VSCROLL: { debug_view_xy topleft = m_view->visible_position(); @@ -748,6 +885,11 @@ LRESULT debugview_info::view_proc(UINT message, WPARAM wparam, LPARAM lparam) break; } + case WM_CONTEXTMENU: + if (!process_context_menu(GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam))) + return DefWindowProc(m_wnd, message, wparam, lparam); + break; + // everything else: defaults default: return DefWindowProc(m_wnd, message, wparam, lparam); diff --git a/src/osd/modules/debugger/win/debugviewinfo.h b/src/osd/modules/debugger/win/debugviewinfo.h index fd48c7975bf..4cbf71b7a81 100644 --- a/src/osd/modules/debugger/win/debugviewinfo.h +++ b/src/osd/modules/debugger/win/debugviewinfo.h @@ -52,12 +52,22 @@ public: HWND create_source_combobox(HWND parent, LONG_PTR userdata); protected: + enum + { + ID_CONTEXT_COPY_VISIBLE = 1, + ID_CONTEXT_PASTE + }; + template T *view() const { return downcast(m_view); } + virtual void add_items_to_context_menu(HMENU menu); + virtual void handle_context_menu(unsigned command); + private: void draw_contents(HDC windc); void update(); uint32_t process_scroll(WORD type, HWND wnd); + bool process_context_menu(int x, int y); LRESULT view_proc(UINT message, WPARAM wparam, LPARAM lparam); static void static_update(debug_view &view, void *osdprivate); @@ -70,6 +80,7 @@ private: HWND m_wnd; HWND m_hscroll; HWND m_vscroll; + HMENU m_contextmenu; static bool s_window_class_registered; }; -- cgit v1.2.3