summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author Vas Crabb <vas@vastheman.com>2021-01-26 02:51:54 +1100
committer Vas Crabb <vas@vastheman.com>2021-01-26 02:51:54 +1100
commit62d3408cc4a32eab2cd3048d1eec3bc42dc56c84 (patch)
tree72ef5d8913778ad6706a42e7259c25823a906aac
parent4681b1173d77e30738a2d11a3e2940c76beb2629 (diff)
win32 debugger: Added context menu with Copy Visible and Paste commands to debug views (partially addresses #6066).
-rw-r--r--src/osd/modules/debugger/win/debugviewinfo.cpp202
-rw-r--r--src/osd/modules/debugger/win/debugviewinfo.h11
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 <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 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 <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;
};