win32 debugger: Added context menu with Copy Visible and Paste commands to debug views (partially addresses #6066).

This commit is contained in:
Vas Crabb 2021-01-26 02:51:54 +11:00
parent 4681b1173d
commit 62d3408cc4
2 changed files with 183 additions and 30 deletions

View File

@ -19,6 +19,8 @@
#include "winutil.h"
#include <mmsystem.h>
// 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<LPWSTR>(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<LPCWSTR>(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 havent 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);

View File

@ -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 <typename T> T *view() const { return downcast<T *>(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;
};