mirror of
https://github.com/holub/mame
synced 2025-07-01 16:19:38 +03:00
win32 debugger: Added context menu with Copy Visible and Paste commands to debug views (partially addresses #6066).
This commit is contained in:
parent
4681b1173d
commit
62d3408cc4
@ -19,6 +19,8 @@
|
|||||||
|
|
||||||
#include "winutil.h"
|
#include "winutil.h"
|
||||||
|
|
||||||
|
#include <mmsystem.h>
|
||||||
|
|
||||||
|
|
||||||
// debugger view styles
|
// debugger view styles
|
||||||
#define DEBUG_VIEW_STYLE WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN
|
#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_view(nullptr),
|
||||||
m_wnd(nullptr),
|
m_wnd(nullptr),
|
||||||
m_hscroll(nullptr),
|
m_hscroll(nullptr),
|
||||||
m_vscroll(nullptr)
|
m_vscroll(nullptr),
|
||||||
|
m_contextmenu(nullptr)
|
||||||
{
|
{
|
||||||
register_window_class();
|
register_window_class();
|
||||||
|
|
||||||
// create the child view
|
// create the child view
|
||||||
m_wnd = CreateWindowEx(DEBUG_VIEW_STYLE_EX, TEXT("MAMEDebugView"), nullptr, DEBUG_VIEW_STYLE,
|
m_wnd = CreateWindowEx(DEBUG_VIEW_STYLE_EX, TEXT("MAMEDebugView"), nullptr, DEBUG_VIEW_STYLE,
|
||||||
0, 0, 100, 100, parent, nullptr, GetModuleHandleUni(), this);
|
0, 0, 100, 100, parent, nullptr, GetModuleHandleUni(), this);
|
||||||
if (m_wnd == nullptr)
|
if (!m_wnd)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
// create the scroll bars
|
// 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);
|
0, 0, 100, CW_USEDEFAULT, m_wnd, nullptr, GetModuleHandleUni(), this);
|
||||||
m_vscroll = CreateWindowEx(VSCROLL_STYLE_EX, TEXT("SCROLLBAR"), nullptr, VSCROLL_STYLE,
|
m_vscroll = CreateWindowEx(VSCROLL_STYLE_EX, TEXT("SCROLLBAR"), nullptr, VSCROLL_STYLE,
|
||||||
0, 0, CW_USEDEFAULT, 100, m_wnd, nullptr, GetModuleHandleUni(), this);
|
0, 0, CW_USEDEFAULT, 100, m_wnd, nullptr, GetModuleHandleUni(), this);
|
||||||
if ((m_hscroll == nullptr) || (m_vscroll == nullptr))
|
if (!m_hscroll || !m_vscroll)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
// create the debug view
|
// create the debug view
|
||||||
m_view = machine().debug_view().alloc_view(type, &debugview_info::static_update, this);
|
m_view = machine().debug_view().alloc_view(type, &debugview_info::static_update, this);
|
||||||
if (m_view == nullptr)
|
if (!m_view)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
if (m_hscroll != nullptr)
|
if (m_hscroll)
|
||||||
DestroyWindow(m_hscroll);
|
DestroyWindow(m_hscroll);
|
||||||
m_hscroll = nullptr;
|
m_hscroll = nullptr;
|
||||||
if (m_vscroll != nullptr)
|
if (m_vscroll)
|
||||||
DestroyWindow(m_vscroll);
|
DestroyWindow(m_vscroll);
|
||||||
m_vscroll = nullptr;
|
m_vscroll = nullptr;
|
||||||
if (m_wnd != nullptr)
|
if (m_wnd)
|
||||||
DestroyWindow(m_wnd);
|
DestroyWindow(m_wnd);
|
||||||
m_wnd = nullptr;
|
m_wnd = nullptr;
|
||||||
if (m_view != nullptr)
|
if (m_view)
|
||||||
machine().debug_view().free_view(*m_view);
|
machine().debug_view().free_view(*m_view);
|
||||||
m_view = nullptr;
|
m_view = nullptr;
|
||||||
}
|
}
|
||||||
@ -89,7 +92,9 @@ cleanup:
|
|||||||
|
|
||||||
debugview_info::~debugview_info()
|
debugview_info::~debugview_info()
|
||||||
{
|
{
|
||||||
if (m_wnd != nullptr)
|
if (m_contextmenu)
|
||||||
|
DestroyMenu(m_contextmenu);
|
||||||
|
if (m_wnd)
|
||||||
DestroyWindow(m_wnd);
|
DestroyWindow(m_wnd);
|
||||||
if (m_view)
|
if (m_view)
|
||||||
machine().debug_view().free_view(*m_view);
|
machine().debug_view().free_view(*m_view);
|
||||||
@ -119,7 +124,7 @@ uint32_t debugview_info::maxwidth()
|
|||||||
if (max < chars)
|
if (max < chars)
|
||||||
max = chars;
|
max = chars;
|
||||||
}
|
}
|
||||||
if (cursource != nullptr)
|
if (cursource)
|
||||||
m_view->set_source(*cursource);
|
m_view->set_source(*cursource);
|
||||||
return (max * metrics().debug_font_width()) + metrics().vscroll_width();
|
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()
|
void debugview_info::send_pageup()
|
||||||
{
|
{
|
||||||
if (m_vscroll)
|
if (m_vscroll)
|
||||||
{
|
|
||||||
SendMessage(m_wnd, WM_VSCROLL, SB_PAGELEFT, (LPARAM)m_vscroll);
|
SendMessage(m_wnd, WM_VSCROLL, SB_PAGELEFT, (LPARAM)m_vscroll);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void debugview_info::send_pagedown()
|
void debugview_info::send_pagedown()
|
||||||
{
|
{
|
||||||
if (m_vscroll)
|
if (m_vscroll)
|
||||||
{
|
|
||||||
SendMessage(m_wnd, WM_VSCROLL, SB_PAGERIGHT, (LPARAM)m_vscroll);
|
SendMessage(m_wnd, WM_VSCROLL, SB_PAGERIGHT, (LPARAM)m_vscroll);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
char const *debugview_info::source_name() const
|
char const *debugview_info::source_name() const
|
||||||
{
|
{
|
||||||
if (m_view != nullptr)
|
if (m_view)
|
||||||
{
|
{
|
||||||
debug_view_source const *const source = m_view->source();
|
debug_view_source const *const source = m_view->source();
|
||||||
if (source != nullptr)
|
if (source)
|
||||||
return source->name();
|
return source->name();
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
@ -193,10 +194,10 @@ char const *debugview_info::source_name() const
|
|||||||
|
|
||||||
device_t *debugview_info::source_device() const
|
device_t *debugview_info::source_device() const
|
||||||
{
|
{
|
||||||
if (m_view != nullptr)
|
if (m_view)
|
||||||
{
|
{
|
||||||
debug_view_source const *const source = m_view->source();
|
debug_view_source const *const source = m_view->source();
|
||||||
if (source != nullptr)
|
if (source)
|
||||||
return source->device();
|
return source->device();
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@ -205,10 +206,10 @@ device_t *debugview_info::source_device() const
|
|||||||
|
|
||||||
bool debugview_info::source_is_visible_cpu() 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();
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
@ -216,10 +217,10 @@ bool debugview_info::source_is_visible_cpu() const
|
|||||||
|
|
||||||
bool debugview_info::set_source_index(int index)
|
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);
|
const debug_view_source *const source = m_view->source(index);
|
||||||
if (source != nullptr)
|
if (source)
|
||||||
{
|
{
|
||||||
m_view->set_source(*source);
|
m_view->set_source(*source);
|
||||||
return true;
|
return true;
|
||||||
@ -231,10 +232,10 @@ bool debugview_info::set_source_index(int index)
|
|||||||
|
|
||||||
bool debugview_info::set_source_for_device(device_t &device)
|
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);
|
const debug_view_source *const source = m_view->source_for_device(&device);
|
||||||
if (source != nullptr)
|
if (source)
|
||||||
{
|
{
|
||||||
m_view->set_source(*source);
|
m_view->set_source(*source);
|
||||||
return true;
|
return true;
|
||||||
@ -247,7 +248,7 @@ bool debugview_info::set_source_for_device(device_t &device)
|
|||||||
bool debugview_info::set_source_for_visible_cpu()
|
bool debugview_info::set_source_for_visible_cpu()
|
||||||
{
|
{
|
||||||
device_t *const curcpu = machine().debugger().console().get_visible_cpu();
|
device_t *const curcpu = machine().debugger().console().get_visible_cpu();
|
||||||
if (curcpu != nullptr)
|
if (curcpu)
|
||||||
return set_source_for_device(*curcpu);
|
return set_source_for_device(*curcpu);
|
||||||
else
|
else
|
||||||
return false;
|
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());
|
auto t_name = osd::text::to_tstring(source->name());
|
||||||
SendMessage(result, CB_ADDSTRING, 0, (LPARAM)t_name.c_str());
|
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_SETCURSEL, m_view->source_index(*cursource), 0);
|
||||||
SendMessage(result, CB_SETDROPPEDWIDTH, ((maxlength + 2) * metrics().debug_font_width()) + metrics().vscroll_width(), 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)
|
void debugview_info::draw_contents(HDC windc)
|
||||||
{
|
{
|
||||||
debug_view_char const *viewdata = m_view->viewdata();
|
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
|
// create a compatible DC and an offscreen bitmap
|
||||||
HDC const dc = CreateCompatibleDC(windc);
|
HDC const dc = CreateCompatibleDC(windc);
|
||||||
if (dc == nullptr)
|
if (!dc)
|
||||||
return;
|
return;
|
||||||
HBITMAP const bitmap = CreateCompatibleBitmap(windc, client.right, client.bottom);
|
HBITMAP const bitmap = CreateCompatibleBitmap(windc, client.right, client.bottom);
|
||||||
if (bitmap == nullptr)
|
if (!bitmap)
|
||||||
{
|
{
|
||||||
DeleteDC(dc);
|
DeleteDC(dc);
|
||||||
return;
|
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)
|
LRESULT debugview_info::view_proc(UINT message, WPARAM wparam, LPARAM lparam)
|
||||||
{
|
{
|
||||||
// handle a few messages
|
// handle a few messages
|
||||||
@ -728,7 +865,7 @@ LRESULT debugview_info::view_proc(UINT message, WPARAM wparam, LPARAM lparam)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// hscroll
|
// horizontal scroll
|
||||||
case WM_HSCROLL:
|
case WM_HSCROLL:
|
||||||
{
|
{
|
||||||
debug_view_xy topleft = m_view->visible_position();
|
debug_view_xy topleft = m_view->visible_position();
|
||||||
@ -738,7 +875,7 @@ LRESULT debugview_info::view_proc(UINT message, WPARAM wparam, LPARAM lparam)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// vscroll
|
// vertical scroll
|
||||||
case WM_VSCROLL:
|
case WM_VSCROLL:
|
||||||
{
|
{
|
||||||
debug_view_xy topleft = m_view->visible_position();
|
debug_view_xy topleft = m_view->visible_position();
|
||||||
@ -748,6 +885,11 @@ LRESULT debugview_info::view_proc(UINT message, WPARAM wparam, LPARAM lparam)
|
|||||||
break;
|
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
|
// everything else: defaults
|
||||||
default:
|
default:
|
||||||
return DefWindowProc(m_wnd, message, wparam, lparam);
|
return DefWindowProc(m_wnd, message, wparam, lparam);
|
||||||
|
@ -52,12 +52,22 @@ public:
|
|||||||
HWND create_source_combobox(HWND parent, LONG_PTR userdata);
|
HWND create_source_combobox(HWND parent, LONG_PTR userdata);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
ID_CONTEXT_COPY_VISIBLE = 1,
|
||||||
|
ID_CONTEXT_PASTE
|
||||||
|
};
|
||||||
|
|
||||||
template <typename T> T *view() const { return downcast<T *>(m_view); }
|
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:
|
private:
|
||||||
void draw_contents(HDC windc);
|
void draw_contents(HDC windc);
|
||||||
void update();
|
void update();
|
||||||
uint32_t process_scroll(WORD type, HWND wnd);
|
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);
|
LRESULT view_proc(UINT message, WPARAM wparam, LPARAM lparam);
|
||||||
|
|
||||||
static void static_update(debug_view &view, void *osdprivate);
|
static void static_update(debug_view &view, void *osdprivate);
|
||||||
@ -70,6 +80,7 @@ private:
|
|||||||
HWND m_wnd;
|
HWND m_wnd;
|
||||||
HWND m_hscroll;
|
HWND m_hscroll;
|
||||||
HWND m_vscroll;
|
HWND m_vscroll;
|
||||||
|
HMENU m_contextmenu;
|
||||||
|
|
||||||
static bool s_window_class_registered;
|
static bool s_window_class_registered;
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user