diff --git a/gtk2_ardour/pianoroll.cc b/gtk2_ardour/pianoroll.cc
index 1f36aa42cb..ad97057a40 100644
--- a/gtk2_ardour/pianoroll.cc
+++ b/gtk2_ardour/pianoroll.cc
@@ -34,6 +34,7 @@
#include "widgets/ardour_button.h"
#include "widgets/ardour_dropdown.h"
+#include "widgets/metabutton.h"
#include "widgets/tooltips.h"
#include "ardour_ui.h"
@@ -41,6 +42,7 @@
#include "editor_drag.h"
#include "gui_thread.h"
#include "keyboard.h"
+#include "midi_util.h"
#include "pianoroll_background.h"
#include "pianoroll.h"
#include "pianoroll_midi_view.h"
@@ -156,16 +158,109 @@ void
Pianoroll::rebuild_parameter_button_map()
{
parameter_button_map.clear ();
- parameter_button_map.insert (std::make_pair (Evoral::Parameter (ARDOUR::MidiVelocityAutomation, _visible_channel), velocity_button));
- parameter_button_map.insert (std::make_pair (Evoral::Parameter (ARDOUR::MidiPitchBenderAutomation, _visible_channel), bender_button));
- parameter_button_map.insert (std::make_pair (Evoral::Parameter (ARDOUR::MidiChannelPressureAutomation, _visible_channel), pressure_button));
- parameter_button_map.insert (std::make_pair (Evoral::Parameter (ARDOUR::MidiCCAutomation, _visible_channel, MIDI_CTL_MSB_EXPRESSION), expression_button));
- parameter_button_map.insert (std::make_pair (Evoral::Parameter (ARDOUR::MidiCCAutomation, _visible_channel, MIDI_CTL_MSB_MODWHEEL), modulation_button));
- parameter_button_map.insert (std::make_pair (Evoral::Parameter (ARDOUR::MidiCCAutomation, _visible_channel, MIDI_CTL_MSB_GENERAL_PURPOSE1), cc_dropdown1));
- parameter_button_map.insert (std::make_pair (Evoral::Parameter (ARDOUR::MidiCCAutomation, _visible_channel, MIDI_CTL_MSB_GENERAL_PURPOSE2), cc_dropdown2));
- parameter_button_map.insert (std::make_pair (Evoral::Parameter (ARDOUR::MidiCCAutomation, _visible_channel, MIDI_CTL_MSB_GENERAL_PURPOSE3), cc_dropdown3));
+ parameter_button_map.insert (std::make_pair (velocity_button, Evoral::Parameter (ARDOUR::MidiVelocityAutomation, _visible_channel)));
+ parameter_button_map.insert (std::make_pair (bender_button, Evoral::Parameter (ARDOUR::MidiPitchBenderAutomation, _visible_channel)));
+ parameter_button_map.insert (std::make_pair (pressure_button, Evoral::Parameter (ARDOUR::MidiChannelPressureAutomation, _visible_channel)));
+ parameter_button_map.insert (std::make_pair (expression_button, Evoral::Parameter (ARDOUR::MidiCCAutomation, _visible_channel, MIDI_CTL_MSB_EXPRESSION)));
+ parameter_button_map.insert (std::make_pair (modulation_button, Evoral::Parameter (ARDOUR::MidiCCAutomation, _visible_channel, MIDI_CTL_MSB_MODWHEEL)));
+
+ parameter_button_map.insert (std::make_pair (cc_dropdown1, Evoral::Parameter (ARDOUR::MidiCCAutomation, _visible_channel, MIDI_CTL_MSB_GENERAL_PURPOSE1)));
+ parameter_button_map.insert (std::make_pair (cc_dropdown2, Evoral::Parameter (ARDOUR::MidiCCAutomation, _visible_channel, MIDI_CTL_MSB_GENERAL_PURPOSE2)));
+ parameter_button_map.insert (std::make_pair (cc_dropdown3, Evoral::Parameter (ARDOUR::MidiCCAutomation, _visible_channel, MIDI_CTL_MSB_GENERAL_PURPOSE3)));
}
+void
+Pianoroll::reset_user_cc_choice (std::string name, Evoral::Parameter param, MetaButton* metabutton)
+{
+ ParameterButtonMap::iterator iter;
+
+ for (iter = parameter_button_map.begin(); iter != parameter_button_map.end(); ++iter) {
+ if (iter->first == metabutton) {
+ parameter_button_map.erase (iter);
+ break;
+ }
+ }
+
+ parameter_button_map.insert (std::make_pair (metabutton, param));
+
+ std::cerr << "Set menu active for " << name << " aka " << ARDOUR::EventTypeMap::instance().to_symbol (param) << std::endl;
+
+ metabutton->set_by_menutext (name);
+}
+
+void
+Pianoroll::add_single_controller_item (Gtk::Menu_Helpers::MenuList& ctl_items,
+ int ctl,
+ const std::string& name,
+ ArdourWidgets::MetaButton* mb)
+{
+ using namespace Gtk::Menu_Helpers;
+
+ const uint16_t selected_channels = 0xffff;
+ for (uint8_t chn = 0; chn < 16; chn++) {
+
+ if (selected_channels & (0x0001 << chn)) {
+
+ Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
+ std::string menu_text (string_compose ("%1: %2 [%3]", ctl, name, int (chn + 1)));
+
+ mb->add_item (name, menu_text, sigc::bind (sigc::mem_fun (*this, &Pianoroll::reset_user_cc_choice), name, fully_qualified_param, mb));
+
+ /* one channel only */
+ break;
+ }
+ }
+}
+
+static void relax() {}
+
+void
+Pianoroll::add_multi_controller_item (Gtk::Menu_Helpers::MenuList& ctl_items,
+ const uint16_t channels,
+ int ctl,
+ const std::string& name,
+ MetaButton* mb)
+{
+ using namespace Gtk;
+ using namespace Gtk::Menu_Helpers;
+
+ Menu* chn_menu = manage (new Menu);
+ MenuList& chn_items (chn_menu->items());
+ std::string menu_text (string_compose ("%1: %2", ctl, name));
+
+ /* Build the channel sub-menu */
+
+ Evoral::Parameter param_without_channel (MidiCCAutomation, 0, ctl);
+
+ for (uint8_t chn = 0; chn < 16; chn++) {
+ if (channels & (0x0001 << chn)) {
+
+ /* for each selected channel, add a menu item for this controller */
+
+ Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
+
+ chn_items.push_back (CheckMenuElem (string_compose (_("Channel %1"), chn+1),
+ sigc::bind (sigc::mem_fun (*this, &Pianoroll::reset_user_cc_choice), menu_text, fully_qualified_param, mb)));
+
+ ParameterButtonMap::iterator i = parameter_button_map.find (mb);
+
+ if (i != parameter_button_map.end()) {
+ if (fully_qualified_param == i->second) {
+ Gtk::CheckMenuItem* cmi = static_cast(&ctl_items.back());
+ cmi->set_active();
+ }
+ }
+ }
+ }
+
+ /* add an item to metabutton's menu that will connect to the
+ * per-channel submenu we built above.
+ */
+
+ mb->add_item (name, menu_text, *chn_menu, sigc::ptr_fun (relax));
+}
+
+
void
Pianoroll::build_lower_toolbar ()
{
@@ -176,26 +271,30 @@ Pianoroll::build_lower_toolbar ()
pressure_button = new ArdourButton (_("Pressure"), elements);
expression_button = new ArdourButton (_("Expression"), elements);
modulation_button = new ArdourButton (_("Modulation"), elements);
- cc_dropdown1 = new ArdourDropdown (elements);
- cc_dropdown2 = new ArdourDropdown (elements);
- cc_dropdown3 = new ArdourDropdown (elements);
+ cc_dropdown1 = new MetaButton ();
+ cc_dropdown2 = new MetaButton ();
+ cc_dropdown3 = new MetaButton ();
- cc_dropdown1->set_text (string_compose (_("CC%1"), MIDI_CTL_MSB_GENERAL_PURPOSE1));
- cc_dropdown2->set_text (string_compose (_("CC%1"), MIDI_CTL_MSB_GENERAL_PURPOSE2));
- cc_dropdown3->set_text (string_compose (_("CC%1"), MIDI_CTL_MSB_GENERAL_PURPOSE3));
+ cc_dropdown1->add_elements (ArdourButton::Indicator);
+ cc_dropdown2->add_elements (ArdourButton::Indicator);
+ cc_dropdown3->add_elements (ArdourButton::Indicator);
+
+ cc_dropdown1->signal_led_clicked.connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::user_led_click), cc_dropdown1));
+ cc_dropdown2->signal_led_clicked.connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::user_led_click), cc_dropdown2));
+ cc_dropdown3->signal_led_clicked.connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::user_led_click), cc_dropdown3));
rebuild_parameter_button_map ();
- /* Only need to do this once because i->second is the actual button,
+ /* Only need to do this once because i->first is the actual button,
* which does not change even when the parameter_button_map is rebuilt.
*/
for (ParameterButtonMap::iterator i = parameter_button_map.begin(); i != parameter_button_map.end(); ++i) {
- i->second->set_active_color (0xff0000ff);
- i->second->set_distinct_led_click (true);
- i->second->set_led_left (true);
- i->second->set_act_on_release (false);
- i->second->set_fallthrough_to_parent (true);
+ i->first->set_active_color (0xff0000ff);
+ i->first->set_distinct_led_click (true);
+ i->first->set_led_left (true);
+ i->first->set_act_on_release (false);
+ i->first->set_fallthrough_to_parent (true);
}
// button_bar.set_homogeneous (true);
@@ -215,6 +314,10 @@ Pianoroll::build_lower_toolbar ()
modulation_button->signal_button_release_event().connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::automation_button_event), ARDOUR::MidiCCAutomation, MIDI_CTL_MSB_MODWHEEL));
expression_button->signal_button_release_event().connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::automation_button_event), ARDOUR::MidiCCAutomation, MIDI_CTL_MSB_EXPRESSION));
+ cc_dropdown1->signal_button_release_event().connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::user_automation_button_event), cc_dropdown1), false);
+ cc_dropdown2->signal_button_release_event().connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::user_automation_button_event), cc_dropdown2), false);
+ cc_dropdown3->signal_button_release_event().connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::user_automation_button_event), cc_dropdown3), false);
+
velocity_button->signal_led_clicked.connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::automation_led_click), ARDOUR::MidiVelocityAutomation, 0));
pressure_button->signal_led_clicked.connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::automation_led_click), ARDOUR::MidiChannelPressureAutomation, 0));
bender_button->signal_led_clicked.connect (sigc::bind (sigc::mem_fun (*this, &Pianoroll::automation_led_click), ARDOUR::MidiPitchBenderAutomation, 0));
@@ -2064,7 +2167,7 @@ Pianoroll::set (TriggerReference & tref)
_track = std::dynamic_pointer_cast (st->shared_from_this());
assert (_track);
- view->set_track (_track);
+ set_track (_track);
_update_connection = Timers::rapid_connect (sigc::mem_fun (*this, &Pianoroll::maybe_update));
_track->DropReferences.connect (object_connections, invalidator (*this), std::bind (&Pianoroll::unset, this), gui_context());
@@ -2096,6 +2199,24 @@ Pianoroll::set_track (std::shared_ptr track)
if (view) {
view->set_track (track);
}
+
+ cc_dropdown1->menu().items().clear ();
+ cc_dropdown2->menu().items().clear ();
+ cc_dropdown3->menu().items().clear ();
+
+ build_controller_menu (cc_dropdown1->menu(), track->instrument_info(), 0xffff,
+ sigc::bind (sigc::mem_fun (*this, &Pianoroll::add_single_controller_item), cc_dropdown1),
+ sigc::bind (sigc::mem_fun (*this, &Pianoroll::add_multi_controller_item), cc_dropdown1));
+ build_controller_menu (cc_dropdown2->menu(), track->instrument_info(), 0xffff,
+ sigc::bind (sigc::mem_fun (*this, &Pianoroll::add_single_controller_item), cc_dropdown2),
+ sigc::bind (sigc::mem_fun (*this, &Pianoroll::add_multi_controller_item), cc_dropdown2));
+ build_controller_menu (cc_dropdown3->menu(), track->instrument_info(), 0xffff,
+ sigc::bind (sigc::mem_fun (*this, &Pianoroll::add_single_controller_item), cc_dropdown3),
+ sigc::bind (sigc::mem_fun (*this, &Pianoroll::add_multi_controller_item), cc_dropdown3));
+
+ // reset_user_cc_choice (Evoral::Parameter (ARDOUR::MidiCCAutomation, _visible_channel, MIDI_CTL_MSB_GENERAL_PURPOSE1), cc_dropdown1);
+ // reset_user_cc_choice (Evoral::Parameter (ARDOUR::MidiCCAutomation, _visible_channel, MIDI_CTL_MSB_GENERAL_PURPOSE2), cc_dropdown2);
+ // reset_user_cc_choice (Evoral::Parameter (ARDOUR::MidiCCAutomation, _visible_channel, MIDI_CTL_MSB_GENERAL_PURPOSE3), cc_dropdown3);
}
void
@@ -2148,6 +2269,48 @@ Pianoroll::zoom_to_show (Temporal::timecnt_t const & duration)
reset_zoom (spp);
}
+void
+Pianoroll::user_activate (MetaButton* metabutton)
+{
+ std::cerr << "shouldn't really be here\n";
+
+ /* Find the parameter */
+
+ ParameterButtonMap::iterator i;
+ for (i = parameter_button_map.begin(); i != parameter_button_map.end(); ++i) {
+ if (i->first == metabutton) {
+ break;
+ }
+ }
+
+ if (i == parameter_button_map.end()) {
+ return;
+ }
+
+ if (view) {
+ view->update_automation_display (i->second, SelectionSet);
+ }
+}
+
+bool
+Pianoroll::user_automation_button_event (GdkEventButton* ev, MetaButton* mb)
+{
+ std::cerr << "UABe\n";
+
+ if (mb->is_menu_popup_event (ev)) {
+ std::cerr << "meny popup event\n";
+ return false;
+ }
+
+ ParameterButtonMap::iterator i = parameter_button_map.find (mb);
+ if (i == parameter_button_map.end()) {
+ std::cerr << "nuttn not found\n";
+ return false;
+ }
+
+ return automation_button_event (ev, i->second.type(), i->second.id());
+}
+
bool
Pianoroll::automation_button_event (GdkEventButton* ev, Evoral::ParameterType type, int id)
{
@@ -2166,6 +2329,8 @@ Pianoroll::automation_button_event (GdkEventButton* ev, Evoral::ParameterType ty
op = SelectionToggle;
}
+ std::cerr << "update ad for " << ARDOUR::EventTypeMap::instance().to_symbol (param) << " op " << op << std::endl;
+
view->update_automation_display (param, op);
}
return true;
@@ -2176,6 +2341,34 @@ Pianoroll::automation_button_event (GdkEventButton* ev, Evoral::ParameterType ty
return false;
}
+void
+Pianoroll::user_led_click (GdkEventButton* ev, MetaButton* metabutton)
+{
+ std::cerr << "ULC\n";
+
+ if (ev->button != 1) {
+ return;
+ }
+
+ /* Find the parameter */
+
+ ParameterButtonMap::iterator i;
+ for (i = parameter_button_map.begin(); i != parameter_button_map.end(); ++i) {
+ if (i->first == metabutton) {
+ break;
+ }
+ }
+
+ if (i == parameter_button_map.end()) {
+ return;
+ }
+
+ std::cerr << "led set active for " << ARDOUR::EventTypeMap::instance().to_symbol (i->second) << std::endl;
+ if (view) {
+ view->set_active_automation (i->second);
+ }
+}
+
void
Pianoroll::automation_led_click (GdkEventButton* ev, Evoral::ParameterType type, int id)
{
@@ -2194,22 +2387,22 @@ Pianoroll::automation_state_changed ()
assert (view);
for (ParameterButtonMap::iterator i = parameter_button_map.begin(); i != parameter_button_map.end(); ++i) {
- std::string str (ARDOUR::EventTypeMap::instance().to_symbol (i->first));
+ std::string str (ARDOUR::EventTypeMap::instance().to_symbol (i->second));
/* Indicate visible automation state with selected/not-selected visual state */
- if (view->is_visible_automation (i->first)) {
- i->second->set_visual_state (Gtkmm2ext::Selected);
+ if (view->is_visible_automation (i->second)) {
+ i->first->set_visual_state (Gtkmm2ext::Selected);
} else {
- i->second->set_visual_state (Gtkmm2ext::NoVisualState);
+ i->first->set_visual_state (Gtkmm2ext::NoVisualState);
}
/* Indicate active automation state with explicit widget active state (LED) */
- if (view->is_active_automation (i->first)) {
- i->second->set_active_state (Gtkmm2ext::ExplicitActive);
+ if (view->is_active_automation (i->second)) {
+ i->first->set_active_state (Gtkmm2ext::ExplicitActive);
} else {
- i->second->set_active_state (Gtkmm2ext::Off);
+ i->first->set_active_state (Gtkmm2ext::Off);
}
}
}
diff --git a/gtk2_ardour/pianoroll.h b/gtk2_ardour/pianoroll.h
index acf14369be..9fe670716f 100644
--- a/gtk2_ardour/pianoroll.h
+++ b/gtk2_ardour/pianoroll.h
@@ -42,6 +42,7 @@ namespace ArdourCanvas {
namespace ArdourWidgets {
class ArdourButton;
+ class MetaButton;
}
class PianorollMidiView;
@@ -204,11 +205,11 @@ class Pianoroll : public CueEditor
ArdourWidgets::ArdourButton* pressure_button;
ArdourWidgets::ArdourButton* expression_button;
ArdourWidgets::ArdourButton* modulation_button;
- ArdourWidgets::ArdourDropdown* cc_dropdown1;
- ArdourWidgets::ArdourDropdown* cc_dropdown2;
- ArdourWidgets::ArdourDropdown* cc_dropdown3;
+ ArdourWidgets::MetaButton* cc_dropdown1;
+ ArdourWidgets::MetaButton* cc_dropdown2;
+ ArdourWidgets::MetaButton* cc_dropdown3;
- typedef std::map ParameterButtonMap;
+ typedef std::map ParameterButtonMap;
ParameterButtonMap parameter_button_map;
void rebuild_parameter_button_map ();
@@ -265,9 +266,12 @@ class Pianoroll : public CueEditor
PBD::ScopedConnectionList capture_connections;
samplecnt_t data_capture_duration;
+ bool user_automation_button_event (GdkEventButton* ev, ArdourWidgets::MetaButton* mb);
bool automation_button_event (GdkEventButton*, Evoral::ParameterType type, int id);
bool automation_button_click (Evoral::ParameterType type, int id, ARDOUR::SelectionOperation);
void automation_led_click (GdkEventButton*, Evoral::ParameterType type, int id);
+ void user_led_click (GdkEventButton* ev, ArdourWidgets::MetaButton* metabutton);
+ void user_activate (ArdourWidgets::MetaButton* metabutton);
int _visible_channel;
@@ -296,4 +300,9 @@ class Pianoroll : public CueEditor
void rec_enable_change ();
void blink_rec_enable (bool);
sigc::connection rec_blink_connection;
+
+ void add_single_controller_item (Gtk::Menu_Helpers::MenuList& ctl_items, int ctl, const std::string& name, ArdourWidgets::MetaButton*);
+ void add_multi_controller_item (Gtk::Menu_Helpers::MenuList& ctl_items, uint16_t channels, int ctl, const std::string& name, ArdourWidgets::MetaButton*);
+ void reset_user_cc_choice (std::string, Evoral::Parameter param, ArdourWidgets::MetaButton*);
+
};