diff --git a/gtk2_ardour/ardour.menus.in b/gtk2_ardour/ardour.menus.in
index a82ed2ca5b..bd9ae6e0da 100644
--- a/gtk2_ardour/ardour.menus.in
+++ b/gtk2_ardour/ardour.menus.in
@@ -698,6 +698,9 @@
+
+
+
diff --git a/gtk2_ardour/ardour_ui.h b/gtk2_ardour/ardour_ui.h
index d9b4503670..e6f32010cf 100644
--- a/gtk2_ardour/ardour_ui.h
+++ b/gtk2_ardour/ardour_ui.h
@@ -170,6 +170,7 @@ class ApplicationBar;
class Meterbridge;
class LuaWindow;
class MidiTracer;
+class PianorollWindow;
class NSM_Client;
class LevelMeterHBox;
class GUIObjectState;
@@ -280,6 +281,7 @@ public:
Gtk::Menu* shared_popup_menu ();
void new_midi_tracer_window ();
+ void new_pianoroll_window ();
void toggle_editing_space();
void toggle_mixer_space();
void toggle_keep_tearoffs();
@@ -520,6 +522,7 @@ private:
void record_state_changed ();
std::list _midi_tracer_windows;
+ std::list _pianoroll_windows;
/* Transport Control */
diff --git a/gtk2_ardour/ardour_ui_dialogs.cc b/gtk2_ardour/ardour_ui_dialogs.cc
index f8e5482fb5..a5d52f8d5c 100644
--- a/gtk2_ardour/ardour_ui_dialogs.cc
+++ b/gtk2_ardour/ardour_ui_dialogs.cc
@@ -71,6 +71,7 @@
#include "monitor_section.h"
#include "midi_tracer.h"
#include "mixer_ui.h"
+#include "pianoroll_window.h"
#include "plugin_dspload_window.h"
#include "plugin_manager_ui.h"
#include "public_editor.h"
@@ -887,6 +888,30 @@ ARDOUR_UI::toggle_meterbridge ()
}
}
+void
+ARDOUR_UI::new_pianoroll_window ()
+{
+ RefPtr act = ActionManager::get_action (X_("Common"), X_("new-pianoroll"));
+ if (!act) {
+ return;
+ }
+
+ std::list::iterator i = _pianoroll_windows.begin ();
+ while (i != _pianoroll_windows.end() && (*i)->get_visible() == true) {
+ ++i;
+ }
+
+ if (i == _pianoroll_windows.end()) {
+ /* all our MIDITracer windows are visible; make a new one */
+ PianorollWindow* pr = new PianorollWindow (string_compose (_("Pianoroll %1"), _pianoroll_windows.size() + 1));
+ pr->show_all ();
+ _pianoroll_windows.push_back (pr);
+ } else {
+ /* re-use the hidden one */
+ (*i)->show_all ();
+ }
+}
+
void
ARDOUR_UI::new_midi_tracer_window ()
{
diff --git a/gtk2_ardour/ardour_ui_ed.cc b/gtk2_ardour/ardour_ui_ed.cc
index ae0a65b176..5bf0015eea 100644
--- a/gtk2_ardour/ardour_ui_ed.cc
+++ b/gtk2_ardour/ardour_ui_ed.cc
@@ -286,6 +286,8 @@ ARDOUR_UI::install_actions ()
act = ActionManager::register_action (common_actions, X_("NewMIDITracer"), _("MIDI Tracer"), sigc::mem_fun(*this, &ARDOUR_UI::new_midi_tracer_window));
ActionManager::session_sensitive_actions.push_back (act);
+ act = ActionManager::register_action (common_actions, X_("new-pianoroll"), _("Piano Roll"), sigc::mem_fun(*this, &ARDOUR_UI::new_pianoroll_window));
+ ActionManager::session_sensitive_actions.push_back (act);
ActionManager::register_action (common_actions, X_("chat"), _("Chat"), sigc::mem_fun(*this, &ARDOUR_UI::launch_chat));
ActionManager::register_action (common_actions, X_("tutorial"), S_("Help|Tutorial"), mem_fun(*this, &ARDOUR_UI::launch_tutorial));
diff --git a/gtk2_ardour/midi_view.cc b/gtk2_ardour/midi_view.cc
index 7fb640281b..0ba6bb7501 100644
--- a/gtk2_ardour/midi_view.cc
+++ b/gtk2_ardour/midi_view.cc
@@ -4256,6 +4256,10 @@ MidiView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selecte
void
MidiView::update_ghost_note (double x, double y, uint32_t state)
{
+ if (!_midi_region) {
+ return;
+ }
+
assert (_ghost_note);
x = _editing_context.canvas_to_timeline (x);
x = std::max (0.0, x);
diff --git a/gtk2_ardour/pianoroll_window.cc b/gtk2_ardour/pianoroll_window.cc
new file mode 100644
index 0000000000..24c7d169d2
--- /dev/null
+++ b/gtk2_ardour/pianoroll_window.cc
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2025 Paul Davis
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "pbd/compose.h"
+
+#include "pianoroll.h"
+#include "pianoroll_window.h"
+
+using namespace ARDOUR;
+
+PianorollWindow::PianorollWindow (std::string const & name)
+ : ArdourWindow (string_compose ("%1 - %2", PROGRAM_NAME, name))
+ , pianoroll (new Pianoroll (name))
+{
+ pianoroll->viewport().set_size_request (600, 120);
+
+ add (pianoroll->toolbox());
+ pianoroll->toolbox().show ();
+}
+
+PianorollWindow::~PianorollWindow ()
+{
+ delete pianoroll;
+}
diff --git a/gtk2_ardour/pianoroll_window.h b/gtk2_ardour/pianoroll_window.h
new file mode 100644
index 0000000000..4abb1635c2
--- /dev/null
+++ b/gtk2_ardour/pianoroll_window.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2025 Paul Davis
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#pragma once
+
+#include "ardour_window.h"
+
+namespace ARDOUR {
+ class MidiRegion;
+ class Track;
+}
+
+class Pianoroll;
+
+class PianorollWindow : public ArdourWindow
+{
+ public:
+ PianorollWindow (std::string const & name);
+ ~PianorollWindow ();
+
+ void set (std::shared_ptr, std::shared_ptr);
+
+ private:
+ Pianoroll* pianoroll;
+};
diff --git a/gtk2_ardour/wscript b/gtk2_ardour/wscript
index 04a5b66416..1fb2f2a2b2 100644
--- a/gtk2_ardour/wscript
+++ b/gtk2_ardour/wscript
@@ -210,6 +210,7 @@ gtk2_ardour_sources = [
'pianoroll_background.cc',
'pianoroll_velocity.cc',
'pianoroll_midi_view.cc',
+ 'pianoroll_window.cc',
'pingback.cc',
'playlist_selector.cc',
'plugin_display.cc',