Prepare central engine check and user notification
[ardour.git] / gtk2_ardour / patch_change_widget.cc
index b7aa2cfc12725bbff800ff0d80575428cc2d6a1a..f9c88d80688af5845799a1204200d4c105fab932 100644 (file)
@@ -17,8 +17,8 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
+#include <bitset>
 #include <gtkmm/frame.h>
-#include <boost/algorithm/string.hpp>
 
 #include "pbd/unwind.h"
 
 
 #include "ardour/instrument_info.h"
 #include "ardour/midi_track.h"
+#include "ardour/plugin_insert.h"
 
+#include "gtkmm2ext/menu_elems.h"
+#include "gtkmm2ext/utils.h"
 #include "widgets/tooltips.h"
 
 #include "gui_thread.h"
@@ -48,6 +51,7 @@ PatchChangeWidget::PatchChangeWidget (boost::shared_ptr<ARDOUR::Route> r)
        , _program_table (/*rows*/ 16, /*cols*/ 8, true)
        , _channel (-1)
        , _ignore_spin_btn_signals (false)
+       , _no_notifications (false)
        , _info (r->instrument_info ())
        , _audition_enable (_("Audition on Change"), ArdourWidgets::ArdourButton::led_default_elements)
        , _audition_start_spin (*manage (new Adjustment (48, 0, 127, 1, 16)))
@@ -75,23 +79,25 @@ PatchChangeWidget::PatchChangeWidget (boost::shared_ptr<ARDOUR::Route> r)
        _program_table.set_spacings (1);
        pack_start (_program_table, true, true);
 
-       if (boost::dynamic_pointer_cast<MidiTrack> (_route)) {
-               box = manage (new HBox ());
-               box->set_spacing (4);
-               box->pack_start (_audition_enable, false, false);
-               box->pack_start (*manage (new Label (_("Start Note:"))), false, false);
-               box->pack_start (_audition_start_spin, false, false);
-               box->pack_start (*manage (new Label (_("End Note:"))), false, false);
-               box->pack_start (_audition_end_spin, false, false);
-               box->pack_start (*manage (new Label (_("Velocity:"))), false, false);
-               box->pack_start (_audition_velocity, false, false);
-
-               Box* box2 = manage (new HBox ());
-               box2->pack_start (*box, true, false);
-               box2->set_border_width (2);
-               pack_start (*box2, false, false);
+       if (!boost::dynamic_pointer_cast<MidiTrack> (_route)) {
+               pack_start ( *manage (new Label (_("Note: Patch Selection is volatile (only Midi-Tracks retain bank/patch selection)."))), false, false);
        }
 
+       box = manage (new HBox ());
+       box->set_spacing (4);
+       box->pack_start (_audition_enable, false, false);
+       box->pack_start (*manage (new Label (_("Start Note:"))), false, false);
+       box->pack_start (_audition_start_spin, false, false);
+       box->pack_start (*manage (new Label (_("End Note:"))), false, false);
+       box->pack_start (_audition_end_spin, false, false);
+       box->pack_start (*manage (new Label (_("Velocity:"))), false, false);
+       box->pack_start (_audition_velocity, false, false);
+
+       Box* box2 = manage (new HBox ());
+       box2->pack_start (*box, true, false);
+       box2->set_border_width (2);
+       pack_start (*box2, false, false);
+
        for (uint8_t pgm = 0; pgm < 128; ++pgm) {
                _program_btn[pgm].set_text_ellipsize (Pango::ELLIPSIZE_END);
                _program_btn[pgm].set_layout_ellipsize_width (PANGO_SCALE * 112 * UIConfiguration::instance ().get_ui_scale ());
@@ -101,20 +107,19 @@ PatchChangeWidget::PatchChangeWidget (boost::shared_ptr<ARDOUR::Route> r)
                _program_btn[pgm].signal_clicked.connect (sigc::bind (sigc::mem_fun (*this, &PatchChangeWidget::select_program), pgm));
        }
 
-       using namespace Menu_Helpers;
        for (uint32_t chn = 0; chn < 16; ++chn) {
+               using namespace Menu_Helpers;
+               using namespace Gtkmm2ext;
                char buf[8];
                snprintf (buf, sizeof(buf), "%d", chn + 1);
-               _channel_select.AddMenuElem (MenuElem (buf, sigc::bind (sigc::mem_fun (*this, &PatchChangeWidget::select_channel), chn)));
+               _channel_select.AddMenuElem (MenuElemNoMnemonic (buf, sigc::bind (sigc::mem_fun (*this, &PatchChangeWidget::select_channel), chn)));
        }
 
-       if (boost::dynamic_pointer_cast<MidiTrack> (_route)) {
-               piano_keyboard_set_monophonic (_piano, TRUE);
-               g_signal_connect (G_OBJECT (_piano), "note-on", G_CALLBACK (PatchChangeWidget::_note_on_event_handler), this);
-               g_signal_connect (G_OBJECT (_piano), "note-off", G_CALLBACK (PatchChangeWidget::_note_off_event_handler), this);
-               _pianomm->set_flags(Gtk::CAN_FOCUS);
-               pack_start (*_pianomm, false, false);
-       }
+       piano_keyboard_set_monophonic (_piano, TRUE);
+       g_signal_connect (G_OBJECT (_piano), "note-on", G_CALLBACK (PatchChangeWidget::_note_on_event_handler), this);
+       g_signal_connect (G_OBJECT (_piano), "note-off", G_CALLBACK (PatchChangeWidget::_note_off_event_handler), this);
+       _pianomm->set_flags(Gtk::CAN_FOCUS);
+       pack_start (*_pianomm, false, false);
 
        _audition_start_spin.set_sensitive (false);
        _audition_end_spin.set_sensitive (false);
@@ -128,6 +133,12 @@ PatchChangeWidget::PatchChangeWidget (boost::shared_ptr<ARDOUR::Route> r)
        _info.Changed.connect (_info_changed_connection, invalidator (*this),
                        boost::bind (&PatchChangeWidget::instrument_info_changed, this), gui_context());
 
+       if (!boost::dynamic_pointer_cast<MidiTrack> (_route)) {
+               _route->processors_changed.connect (_route_connection, invalidator (*this),
+                               boost::bind (&PatchChangeWidget::processors_changed, this), gui_context());
+               processors_changed ();
+       }
+
        set_spacing (4);
        show_all ();
 }
@@ -138,6 +149,14 @@ PatchChangeWidget::~PatchChangeWidget ()
        delete _pianomm;
 }
 
+void
+PatchChangeWidget::refresh ()
+{
+       if (is_visible ()) {
+               on_show ();
+       }
+}
+
 void
 PatchChangeWidget::on_show ()
 {
@@ -169,19 +188,30 @@ PatchChangeWidget::select_channel (uint8_t chn)
 
        _channel_select.set_text (string_compose ("%1", (int)(chn + 1)));
        _channel = chn;
+       _no_notifications = false;
 
        _ac_connections.drop_connections ();
 
-       boost::shared_ptr<AutomationControl> bank_msb = _route->automation_control(Evoral::Parameter (MidiCCAutomation, chn, MIDI_CTL_MSB_BANK), true);
-       boost::shared_ptr<AutomationControl> bank_lsb = _route->automation_control(Evoral::Parameter (MidiCCAutomation, chn, MIDI_CTL_LSB_BANK), true);
-       boost::shared_ptr<AutomationControl> program = _route->automation_control(Evoral::Parameter (MidiPgmChangeAutomation, chn), true);
-
-       bank_msb->Changed.connect (_ac_connections, invalidator (*this),
-                       boost::bind (&PatchChangeWidget::bank_changed, this), gui_context ());
-       bank_lsb->Changed.connect (_ac_connections, invalidator (*this),
-                       boost::bind (&PatchChangeWidget::bank_changed, this), gui_context ());
-       program->Changed.connect (_ac_connections, invalidator (*this),
-                       boost::bind (&PatchChangeWidget::program_changed, this), gui_context ());
+       if (boost::dynamic_pointer_cast<MidiTrack> (_route)) {
+               boost::shared_ptr<AutomationControl> bank_msb = _route->automation_control(Evoral::Parameter (MidiCCAutomation, chn, MIDI_CTL_MSB_BANK), true);
+               boost::shared_ptr<AutomationControl> bank_lsb = _route->automation_control(Evoral::Parameter (MidiCCAutomation, chn, MIDI_CTL_LSB_BANK), true);
+               boost::shared_ptr<AutomationControl> program = _route->automation_control(Evoral::Parameter (MidiPgmChangeAutomation, chn), true);
+
+               bank_msb->Changed.connect (_ac_connections, invalidator (*this),
+                               boost::bind (&PatchChangeWidget::bank_changed, this), gui_context ());
+               bank_lsb->Changed.connect (_ac_connections, invalidator (*this),
+                               boost::bind (&PatchChangeWidget::bank_changed, this), gui_context ());
+               program->Changed.connect (_ac_connections, invalidator (*this),
+                               boost::bind (&PatchChangeWidget::program_changed, this), gui_context ());
+       } else if (boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (_route->the_instrument())) {
+               if (pi->plugin()->knows_bank_patch ()) {
+                       pi->plugin ()->BankPatchChange.connect (_ac_connections, invalidator (*this),
+                                       boost::bind (&PatchChangeWidget::bankpatch_changed, this, _1), gui_context ());
+               } else {
+                       _no_notifications = true;
+                       // TODO add note: instrument does not report changes.
+               }
+       }
 
        refill_banks ();
 }
@@ -191,6 +221,7 @@ PatchChangeWidget::refill_banks ()
 {
        cancel_audition ();
        using namespace Menu_Helpers;
+       using namespace Gtkmm2ext;
 
        _current_patch_bank.reset ();
        _bank_select.clear_items ();
@@ -207,8 +238,10 @@ PatchChangeWidget::refill_banks ()
        if (cns) {
                for (MIDI::Name::ChannelNameSet::PatchBanks::const_iterator i = cns->patch_banks().begin(); i != cns->patch_banks().end(); ++i) {
                        std::string n = (*i)->name ();
-                       boost::replace_all (n, "_", " ");
-                       _bank_select.AddMenuElem (MenuElem (n, sigc::bind (sigc::mem_fun (*this, &PatchChangeWidget::select_bank), (*i)->number ())));
+                       if ((*i)->number () == UINT16_MAX) {
+                               continue;
+                       }
+                       _bank_select.AddMenuElem (MenuElemNoMnemonic (n, sigc::bind (sigc::mem_fun (*this, &PatchChangeWidget::select_bank), (*i)->number ())));
                        if ((*i)->number () == b) {
                                _current_patch_bank = *i;
                                _bank_select.set_text (n);
@@ -218,7 +251,7 @@ PatchChangeWidget::refill_banks ()
 
        if (!_current_patch_bank) {
                std::string n = string_compose (_("Bank %1"), b);
-               _bank_select.AddMenuElem (MenuElem (n, sigc::bind (sigc::mem_fun (*this, &PatchChangeWidget::select_bank), b)));
+               _bank_select.AddMenuElem (MenuElemNoMnemonic (n, sigc::bind (sigc::mem_fun (*this, &PatchChangeWidget::select_bank), b)));
                _bank_select.set_text (n);
        }
 
@@ -228,24 +261,32 @@ PatchChangeWidget::refill_banks ()
 void
 PatchChangeWidget::refill_program_list ()
 {
-       // TODO if _current_patch_bank!=0, only clear/reset unused patches
-       for (uint8_t pgm = 0; pgm < 128; ++pgm) {
-               std::string n = string_compose (_("Pgm-%1"), (int)(pgm +1));
-               _program_btn[pgm].set_text (n);
-               set_tooltip (_program_btn[pgm], n);
-       }
+       std::bitset<128> unset_notes;
+       unset_notes.set ();
 
        if (_current_patch_bank) {
                const MIDI::Name::PatchNameList& patches = _current_patch_bank->patch_name_list ();
                for (MIDI::Name::PatchNameList::const_iterator i = patches.begin(); i != patches.end(); ++i) {
-                       std::string n = (*i)->name ();
-                       boost::replace_all (n, "_", " ");
+                       const std::string n = (*i)->name ();
                        MIDI::Name::PatchPrimaryKey const& key = (*i)->patch_primary_key ();
 
                        const uint8_t pgm = key.program();
                        _program_btn[pgm].set_text (n);
-                       set_tooltip (_program_btn[pgm], string_compose (_("%1 (Pgm-%2)"), n, (int)(pgm +1)));
-                       }
+                       set_tooltip (_program_btn[pgm], string_compose (_("%1 (Pgm-%2)"),
+                                               Gtkmm2ext::markup_escape_text (n), (int)(pgm +1)));
+                       unset_notes.reset (pgm);
+               }
+       }
+
+       for (uint8_t pgm = 0; pgm < 128; ++pgm) {
+               if (!unset_notes.test (pgm)) {
+                       _program_btn[pgm].set_name (X_("patch change button"));
+                       continue;
+               }
+               std::string n = string_compose (_("Pgm-%1"), (int)(pgm +1));
+               _program_btn[pgm].set_text (n);
+               _program_btn[pgm].set_name (X_("patch change dim button"));
+               set_tooltip (_program_btn[pgm], n);
        }
 
        program_changed ();
@@ -268,20 +309,48 @@ PatchChangeWidget::select_bank (uint32_t bank)
 {
        cancel_audition ();
 
-       boost::shared_ptr<AutomationControl> bank_msb = _route->automation_control(Evoral::Parameter (MidiCCAutomation, _channel, MIDI_CTL_MSB_BANK), true);
-       boost::shared_ptr<AutomationControl> bank_lsb = _route->automation_control(Evoral::Parameter (MidiCCAutomation, _channel, MIDI_CTL_LSB_BANK), true);
+       if (boost::dynamic_pointer_cast<MidiTrack> (_route)) {
+               boost::shared_ptr<AutomationControl> bank_msb = _route->automation_control(Evoral::Parameter (MidiCCAutomation, _channel, MIDI_CTL_MSB_BANK), true);
+               boost::shared_ptr<AutomationControl> bank_lsb = _route->automation_control(Evoral::Parameter (MidiCCAutomation, _channel, MIDI_CTL_LSB_BANK), true);
+
+               bank_msb->set_value (bank >> 7, PBD::Controllable::NoGroup);
+               bank_lsb->set_value (bank & 127, PBD::Controllable::NoGroup);
+       } else if (boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (_route->the_instrument())) {
+               uint8_t event[3];
+               event[0] = (MIDI_CMD_CONTROL | _channel);
+               event[1] = 0x00;
+               event[2] = bank >> 7;
+               pi->write_immediate_event (3, event);
+
+               event[1] = 0x20;
+               event[2] = bank & 127;
+               pi->write_immediate_event (3, event);
+       }
 
-       bank_msb->set_value (bank >> 7, PBD::Controllable::NoGroup);
-       bank_lsb->set_value (bank & 127, PBD::Controllable::NoGroup);
+       select_program (program (_channel));
 }
 
 void
 PatchChangeWidget::select_program (uint8_t pgm)
 {
        cancel_audition ();
+       if (_no_notifications) {
+               program_changed ();
+       }
 
-       boost::shared_ptr<AutomationControl> program = _route->automation_control(Evoral::Parameter (MidiPgmChangeAutomation, _channel), true);
-       program->set_value (pgm, PBD::Controllable::NoGroup);
+       if (pgm > 127) {
+               return;
+       }
+
+       if (boost::dynamic_pointer_cast<MidiTrack> (_route)) {
+               boost::shared_ptr<AutomationControl> program = _route->automation_control(Evoral::Parameter (MidiPgmChangeAutomation, _channel), true);
+               program->set_value (pgm, PBD::Controllable::NoGroup);
+       } else if (boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (_route->the_instrument())) {
+               uint8_t event[2];
+               event[0] = (MIDI_CMD_PGM_CHANGE | _channel);
+               event[1] = pgm;
+               pi->write_immediate_event (2, event);
+       }
 
        audition ();
 }
@@ -291,10 +360,17 @@ PatchChangeWidget::select_program (uint8_t pgm)
 void
 PatchChangeWidget::bank_changed ()
 {
-       // TODO optimize, just find the bank, set the text and refill_program_list()
        refill_banks ();
 }
 
+void
+PatchChangeWidget::bankpatch_changed (uint8_t chn)
+{
+       if (chn == _channel) {
+               refill_banks ();
+       }
+}
+
 void
 PatchChangeWidget::program_changed ()
 {
@@ -310,6 +386,17 @@ PatchChangeWidget::instrument_info_changed ()
        refill_banks ();
 }
 
+void
+PatchChangeWidget::processors_changed ()
+{
+       assert (!boost::dynamic_pointer_cast<MidiTrack> (_route));
+       if (_route->the_instrument ()) {
+               set_sensitive (true);
+       } else {
+               set_sensitive (false);
+       }
+}
+
 /* ***** play notes *****/
 
 void
@@ -347,22 +434,15 @@ PatchChangeWidget::cancel_audition ()
        _note_queue_connection.disconnect();
 
        if (_audition_note_on) {
-               boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (_route);
-               uint8_t event[3];
-
-               event[0] = (MIDI_CMD_NOTE_OFF | _channel);
-               event[1] = _audition_note_num;
-               event[2] = 0;
-               mt->write_immediate_event (3, event);
+               note_off_event_handler (_audition_note_num);
                piano_keyboard_set_note_off (_piano, _audition_note_num);
        }
-       _audition_note_on = false;
 }
 
 void
 PatchChangeWidget::audition ()
 {
-       if (!boost::dynamic_pointer_cast<MidiTrack> (_route)) {
+       if (!boost::dynamic_pointer_cast<MidiTrack> (_route) && !boost::dynamic_pointer_cast<PluginInsert> (_route)) {
                return;
        }
        if (_channel > 16) {
@@ -386,23 +466,12 @@ PatchChangeWidget::audition ()
 bool
 PatchChangeWidget::audition_next ()
 {
-       boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (_route);
-       uint8_t event[3];
-
        if (_audition_note_on) {
-               event[0] = (MIDI_CMD_NOTE_OFF | _channel);
-               event[1] = _audition_note_num;
-               event[2] = 0;
-               mt->write_immediate_event (3, event);
-               _audition_note_on = false;
+               note_off_event_handler (_audition_note_num);
                piano_keyboard_set_note_off (_piano, _audition_note_num);
                return ++_audition_note_num <= _audition_end_spin.get_value_as_int() && _audition_enable.get_active ();
        } else {
-               event[0] = (MIDI_CMD_NOTE_ON | _channel);
-               event[1] = _audition_note_num;
-               event[2] = _audition_velocity.get_value_as_int ();
-               mt->write_immediate_event (3, event);
-               _audition_note_on = true;
+               note_on_event_handler (_audition_note_num, true);
                piano_keyboard_set_note_on (_piano, _audition_note_num);
                return true;
        }
@@ -411,7 +480,7 @@ PatchChangeWidget::audition_next ()
 void
 PatchChangeWidget::_note_on_event_handler(GtkWidget*, int note, gpointer arg)
 {
-       ((PatchChangeWidget*)arg)->note_on_event_handler(note);
+       ((PatchChangeWidget*)arg)->note_on_event_handler(note, false);
 }
 
 void
@@ -421,30 +490,40 @@ PatchChangeWidget::_note_off_event_handler(GtkWidget*, int note, gpointer arg)
 }
 
 void
-PatchChangeWidget::note_on_event_handler (int note)
+PatchChangeWidget::note_on_event_handler (int note, bool for_audition)
 {
-       cancel_audition ();
-       _pianomm->grab_focus ();
-       boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (_route);
+       if (!for_audition) {
+               cancel_audition ();
+               _pianomm->grab_focus ();
+       }
        uint8_t event[3];
        event[0] = (MIDI_CMD_NOTE_ON | _channel);
        event[1] = note;
        event[2] = _audition_velocity.get_value_as_int ();
-       mt->write_immediate_event (3, event);
        _audition_note_on = true;
        _audition_note_num = note;
+
+       if (boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (_route)) {
+               mt->write_immediate_event (3, event);
+       } else if (boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (_route->the_instrument())) {
+               pi->write_immediate_event (3, event);
+       }
 }
 
 void
 PatchChangeWidget::note_off_event_handler (int note)
 {
-       boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (_route);
        uint8_t event[3];
        event[0] = (MIDI_CMD_NOTE_OFF | _channel);
        event[1] = note;
        event[2] = 0;
-       mt->write_immediate_event (3, event);
        _audition_note_on = false;
+
+       if (boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (_route)) {
+               mt->write_immediate_event (3, event);
+       } else if (boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (_route->the_instrument())) {
+               pi->write_immediate_event (3, event);
+       }
 }
 
 /* ***** query info *****/
@@ -452,25 +531,51 @@ PatchChangeWidget::note_off_event_handler (int note)
 int
 PatchChangeWidget::bank (uint8_t chn) const
 {
-       boost::shared_ptr<AutomationControl> bank_msb = _route->automation_control(Evoral::Parameter (MidiCCAutomation, chn, MIDI_CTL_MSB_BANK), true);
-       boost::shared_ptr<AutomationControl> bank_lsb = _route->automation_control(Evoral::Parameter (MidiCCAutomation, chn, MIDI_CTL_LSB_BANK), true);
-
-       return ((int)bank_msb->get_value () << 7) + (int)bank_lsb->get_value();
+       if (boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (_route)) {
+               boost::shared_ptr<AutomationControl> bank_msb = _route->automation_control(Evoral::Parameter (MidiCCAutomation, chn, MIDI_CTL_MSB_BANK), true);
+               boost::shared_ptr<AutomationControl> bank_lsb = _route->automation_control(Evoral::Parameter (MidiCCAutomation, chn, MIDI_CTL_LSB_BANK), true);
+
+               return ((int)bank_msb->get_value () << 7) + (int)bank_lsb->get_value();
+       } else if (boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (_route->the_instrument())) {
+               uint32_t bankpatch = pi->plugin()->bank_patch (chn);
+               if (bankpatch != UINT32_MAX) {
+                       return bankpatch >> 7;
+               }
+       }
+       return 0;
 }
 
 uint8_t
 PatchChangeWidget::program (uint8_t chn) const
 {
-       boost::shared_ptr<AutomationControl> program = _route->automation_control(Evoral::Parameter (MidiPgmChangeAutomation, chn), true);
-       return program->get_value();
+       if (boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (_route)) {
+               boost::shared_ptr<AutomationControl> program = _route->automation_control(Evoral::Parameter (MidiPgmChangeAutomation, chn), true);
+               return program->get_value();
+       } else if (boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (_route->the_instrument())) {
+               uint32_t bankpatch = pi->plugin()->bank_patch (chn);
+               if (bankpatch != UINT32_MAX) {
+                       return bankpatch & 127;
+               }
+       }
+       return 255;
 }
 
 /* ***************************************************************************/
 
-PatchChangeGridDialog::PatchChangeGridDialog (std::string const& title, boost::shared_ptr<ARDOUR::Route> r)
-       : ArdourDialog (title, false, false)
+PatchChangeGridDialog::PatchChangeGridDialog (boost::shared_ptr<ARDOUR::Route> r)
+       : ArdourDialog (string_compose (_("Select Patch for \"%1\""), r->name()), false, false)
        , w (r)
 {
+       r->PropertyChanged.connect (_route_connection, invalidator (*this), boost::bind (&PatchChangeGridDialog::route_property_changed, this, _1, boost::weak_ptr<Route>(r)), gui_context());
        get_vbox()->add (w);
        w.show ();
 }
+
+void
+PatchChangeGridDialog::route_property_changed (const PBD::PropertyChange& what_changed, boost::weak_ptr<Route> wr)
+{
+       boost::shared_ptr<ARDOUR::Route> r = wr.lock ();
+       if (r && what_changed.contains (ARDOUR::Properties::name)) {
+               set_title (string_compose (_("Select Patch for \"%1\"'"), r->name()));
+       }
+}