move midi scroomer to right of meter and fix alignment
[ardour.git] / gtk2_ardour / midi_time_axis.cc
index 002efb9910f2b6cf9b015096efbbb581f42a28e9..685f4693583ce74665ee653bb25f274aa93ab1b0 100644 (file)
@@ -19,8 +19,6 @@
 #include <cstdlib>
 #include <cmath>
 
-#include <strings.h> // for ffs(3)
-
 #include <algorithm>
 #include <string>
 #include <vector>
@@ -28,6 +26,7 @@
 #include <sigc++/bind.h>
 
 #include "pbd/error.h"
+#include "pbd/ffs.h"
 #include "pbd/stl_delete.h"
 #include "pbd/whitespace.h"
 #include "pbd/basename.h"
 #include "ardour/midi_source.h"
 #include "ardour/midi_track.h"
 #include "ardour/operations.h"
+#include "ardour/pannable.h"
+#include "ardour/panner.h"
+#include "ardour/panner_shell.h"
 #include "ardour/playlist.h"
+#include "ardour/profile.h"
 #include "ardour/region.h"
 #include "ardour/region_factory.h"
 #include "ardour/route.h"
 #include "i18n.h"
 
 using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
 using namespace PBD;
 using namespace Gtk;
 using namespace Gtkmm2ext;
 using namespace Editing;
 
 // Minimum height at which a control is displayed
-static const uint32_t MIDI_CONTROLS_BOX_MIN_HEIGHT = 140;
+static const uint32_t MIDI_CONTROLS_BOX_MIN_HEIGHT = 160;
 static const uint32_t KEYBOARD_MIN_HEIGHT = 130;
 
 MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session* sess, ArdourCanvas::Canvas& canvas)
@@ -163,7 +167,22 @@ MidiTimeAxisView::set_route (boost::shared_ptr<Route> rt)
                controls_ebox.set_name ("MidiBusControlsBaseUnselected");
        }
 
+       /* if set_state above didn't create a gain automation child, we need to make one */
+       if (automation_child (GainAutomation) == 0) {
+               create_automation_child (GainAutomation, false);
+       }
+
+       /* if set_state above didn't create a mute automation child, we need to make one */
+       if (automation_child (MuteAutomation) == 0) {
+               create_automation_child (MuteAutomation, false);
+       }
+
+       if (_route->panner_shell()) {
+               _route->panner_shell()->Changed.connect (*this, invalidator (*this), boost::bind (&MidiTimeAxisView::ensure_pan_views, this, false), gui_context());
+       }
+
        /* map current state of the route */
+       ensure_pan_views (false);
 
        processors_changed (RouteProcessorChange ());
 
@@ -191,15 +210,26 @@ MidiTimeAxisView::set_route (boost::shared_ptr<Route> rt)
                   label so that they can be reduced in height for stacked-view
                   tracks.
                */
+
+               top_hbox.remove(gm.get_level_meter());
                VBox* v = manage (new VBox);
                HBox* h = manage (new HBox);
-               h->pack_start (*_range_scroomer);
-               h->pack_start (*_piano_roll_header);
+               h->pack_end (*_piano_roll_header);
+               h->pack_end (*_range_scroomer);
+               h->pack_end (gm.get_level_meter(), false, false, 4);
                v->pack_start (*h, false, false);
-               v->pack_start (*manage (new Label ("")), true, true);
                v->show ();
                h->show ();
-               controls_hbox.pack_start(*v, false, false);
+               top_hbox.pack_end(*v, false, false, 0);
+               if (!ARDOUR::Profile->get_mixbus()) {
+                       controls_meters_size_group->remove_widget (gm.get_level_meter());
+                       controls_meters_size_group->add_widget (*h);
+               }
+               // make up for level_meter 4 spc padding in RTA
+               Gtk::Fixed *blank = manage(new Gtk::Fixed());
+               blank->set_size_request(8, -1);
+               blank->show();
+               top_hbox.pack_end(*blank, false, false, 0);
 
                controls_ebox.set_name ("MidiTrackControlsBaseUnselected");
                controls_base_selected_name = "MidiTrackControlsBaseSelected";
@@ -581,6 +611,27 @@ MidiTimeAxisView::build_automation_action_menu (bool for_selection)
                        MenuElem (string_compose ("<i>%1</i>", _("No MIDI Channels selected"))));
                dynamic_cast<Label*> (automation_items.back().get_child())->set_use_markup (true);
        }
+
+       automation_items.push_back (SeparatorElem());
+       automation_items.push_back (CheckMenuElem (_("Fader"), sigc::mem_fun (*this, &MidiTimeAxisView::update_gain_track_visibility)));
+       gain_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&automation_items.back ());
+       gain_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) && 
+                                         (gain_track && string_is_affirmative (gain_track->gui_property ("visible"))));
+
+       _main_automation_menu_map[Evoral::Parameter(GainAutomation)] = gain_automation_item;
+
+       if (!pan_tracks.empty()) {
+               automation_items.push_back (CheckMenuElem (_("Pan"), sigc::mem_fun (*this, &MidiTimeAxisView::update_pan_track_visibility)));
+               pan_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&automation_items.back ());
+               pan_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
+                                                (!pan_tracks.empty() && string_is_affirmative (pan_tracks.front()->gui_property ("visible"))));
+
+               set<Evoral::Parameter> const & params = _route->pannable()->what_can_be_automated ();
+               for (set<Evoral::Parameter>::const_iterator p = params.begin(); p != params.end(); ++p) {
+                       _main_automation_menu_map[*p] = pan_automation_item;
+               }
+       }
+
 }
 
 void
@@ -663,7 +714,7 @@ MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items,
                                        }
                                }
 
-                               CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
+                               Gtk::CheckMenuItem* cmi = static_cast<Gtk::CheckMenuItem*>(&chn_items.back());
                                _channel_command_menu_map[fully_qualified_param] = cmi;
                                cmi->set_active (visible);
                        }
@@ -695,7 +746,7 @@ MidiTimeAxisView::add_channel_command_menu_item (Menu_Helpers::MenuList& items,
                                        }
                                }
 
-                               CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&items.back());
+                               Gtk::CheckMenuItem* cmi = static_cast<Gtk::CheckMenuItem*>(&items.back());
                                _channel_command_menu_map[fully_qualified_param] = cmi;
                                cmi->set_active (visible);
 
@@ -721,7 +772,7 @@ MidiTimeAxisView::add_single_channel_controller_item(Menu_Helpers::MenuList& ctl
                        Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
                        ctl_items.push_back (
                                CheckMenuElem (
-                                       string_compose ("<b>%1</b>: %2 [%3]", ctl, name, int (chn)),
+                                       string_compose ("<b>%1</b>: %2 [%3]", ctl, name, int (chn + 1)),
                                        sigc::bind (
                                                sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
                                                fully_qualified_param)));
@@ -737,7 +788,7 @@ MidiTimeAxisView::add_single_channel_controller_item(Menu_Helpers::MenuList& ctl
                                }
                        }
 
-                       CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&ctl_items.back());
+                       Gtk::CheckMenuItem* cmi = static_cast<Gtk::CheckMenuItem*>(&ctl_items.back());
                        _controller_menu_map[fully_qualified_param] = cmi;
                        cmi->set_active (visible);
 
@@ -793,7 +844,7 @@ MidiTimeAxisView::add_multi_channel_controller_item(Menu_Helpers::MenuList& ctl_
                                }
                        }
 
-                       CheckMenuItem* cmi = static_cast<CheckMenuItem*>(&chn_items.back());
+                       Gtk::CheckMenuItem* cmi = static_cast<Gtk::CheckMenuItem*>(&chn_items.back());
                        _controller_menu_map[fully_qualified_param] = cmi;
                        cmi->set_active (visible);
                }
@@ -1072,19 +1123,147 @@ MidiTimeAxisView::update_range()
        }
 }
 
+void
+MidiTimeAxisView::ensure_pan_views (bool show)
+{
+       bool changed = false;
+       for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
+               changed = true;
+               (*i)->set_marked_for_display (false);
+       }
+       if (changed) {
+               _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
+       }
+       pan_tracks.clear();
+
+       if (!_route->panner()) {
+               return;
+       }
+
+       set<Evoral::Parameter> params = _route->panner()->what_can_be_automated();
+       set<Evoral::Parameter>::iterator p;
+
+       for (p = params.begin(); p != params.end(); ++p) {
+               boost::shared_ptr<ARDOUR::AutomationControl> pan_control = _route->pannable()->automation_control(*p);
+
+               if (pan_control->parameter().type() == NullAutomation) {
+                       error << "Pan control has NULL automation type!" << endmsg;
+                       continue;
+               }
+
+               if (automation_child (pan_control->parameter ()).get () == 0) {
+
+                       /* we don't already have an AutomationTimeAxisView for this parameter */
+
+                       std::string const name = _route->panner()->describe_parameter (pan_control->parameter ());
+
+                       boost::shared_ptr<AutomationTimeAxisView> t (
+                                       new AutomationTimeAxisView (_session,
+                                               _route,
+                                               _route->pannable(),
+                                               pan_control,
+                                               pan_control->parameter (),
+                                               _editor,
+                                               *this,
+                                               false,
+                                               parent_canvas,
+                                               name)
+                                       );
+
+                       pan_tracks.push_back (t);
+                       add_automation_child (*p, t, show);
+               } else {
+                       pan_tracks.push_back (automation_child (pan_control->parameter ()));
+               }
+       }
+}
+
+void
+MidiTimeAxisView::update_gain_track_visibility ()
+{
+       bool const showit = gain_automation_item->get_active();
+
+       if (showit != string_is_affirmative (gain_track->gui_property ("visible"))) {
+               gain_track->set_marked_for_display (showit);
+
+               /* now trigger a redisplay */
+
+               if (!no_redraw) {
+                        _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
+               }
+       }
+}
+
+void
+MidiTimeAxisView::update_pan_track_visibility ()
+{
+       bool const showit = pan_automation_item->get_active();
+       bool changed = false;
+
+       for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
+               if ((*i)->set_marked_for_display (showit)) {
+                       changed = true;
+               }
+       }
+
+       if (changed) {
+               _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
+       }
+}
+
 void
 MidiTimeAxisView::show_all_automation (bool apply_to_selection)
 {
+       using namespace MIDI::Name;
+
        if (apply_to_selection) {
                _editor.get_selection().tracks.foreach_midi_time_axis (
                        boost::bind (&MidiTimeAxisView::show_all_automation, _1, false));
        } else {
                if (midi_track()) {
+                       // Show existing automation
                        const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
 
                        for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
                                create_automation_child(*i, true);
                        }
+
+                       // Show automation for all controllers named in midnam file
+                       boost::shared_ptr<MasterDeviceNames> device_names = get_device_names();
+                       if (gui_property (X_("midnam-model-name")) != "Generic" &&
+                            device_names && !device_names->controls().empty()) {
+                               const std::string device_mode       = _midnam_custom_device_mode_selector.get_active_text();
+                               const uint16_t    selected_channels = midi_track()->get_playback_channel_mask();
+                               for (uint32_t chn = 0; chn < 16; ++chn) {
+                                       if ((selected_channels & (0x0001 << chn)) == 0) {
+                                               // Channel not in use
+                                               continue;
+                                       }
+
+                                       boost::shared_ptr<ChannelNameSet> chan_names = device_names->channel_name_set_by_channel(
+                                               device_mode, chn);
+                                       if (!chan_names) {
+                                               continue;
+                                       }
+
+                                       boost::shared_ptr<ControlNameList> control_names = device_names->control_name_list(
+                                               chan_names->control_list_name());
+                                       if (!control_names) {
+                                               continue;
+                                       }
+
+                                       for (ControlNameList::Controls::const_iterator c = control_names->controls().begin();
+                                            c != control_names->controls().end();
+                                            ++c) {
+                                               const uint16_t ctl = c->second->number();
+                                               if (ctl != MIDI_CTL_MSB_BANK && ctl != MIDI_CTL_LSB_BANK) {
+                                                       /* Skip bank select controllers since they're handled specially */
+                                                       const Evoral::Parameter param(MidiCCAutomation, chn, ctl);
+                                                       create_automation_child(param, true);
+                                               }
+                                       }
+                               }
+                       }
                }
 
                RouteTimeAxisView::show_all_automation ();
@@ -1143,6 +1322,10 @@ MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool
                create_gain_automation_child (param, show);
                break;
 
+       case MuteAutomation:
+               create_mute_automation_child (param, show);
+               break;
+
        case PluginAutomation:
                /* handled elsewhere */
                break;
@@ -1178,6 +1361,12 @@ MidiTimeAxisView::create_automation_child (const Evoral::Parameter& param, bool
                add_automation_child (param, track, show);
                break;
 
+       case PanWidthAutomation:
+       case PanElevationAutomation:
+       case PanAzimuthAutomation:
+               ensure_pan_views (show);
+               break;
+
        default:
                error << "MidiTimeAxisView: unknown automation child "
                      << EventTypeMap::instance().to_symbol(param) << endmsg;
@@ -1394,8 +1583,7 @@ MidiTimeAxisView::add_region (framepos_t pos, framecnt_t length, bool commit)
 
        real_editor->snap_to (pos, 0);
 
-       boost::shared_ptr<Source> src = _session->create_midi_source_for_session (
-               view()->trackview().track().get(), view()->trackview().track()->name());
+       boost::shared_ptr<Source> src = _session->create_midi_source_by_stealing_name (view()->trackview().track());
        PropertyList plist;
 
        plist.add (ARDOUR::Properties::start, 0);
@@ -1489,7 +1677,7 @@ MidiTimeAxisView::playback_channel_mode_changed ()
                _playback_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2</i>", _("Play"), _("some")));
                break;
        case ForceChannel:
-               _playback_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2>%3</i>", _("Play"), _("all"), ffs (midi_track()->get_playback_channel_mask())));
+               _playback_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2>%3</i>", _("Play"), _("all"), PBD::ffs (midi_track()->get_playback_channel_mask())));
                break;
        }
 }
@@ -1505,7 +1693,7 @@ MidiTimeAxisView::capture_channel_mode_changed ()
                _capture_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2</i>", _("Rec"), _("some")));
                break;
        case ForceChannel:
-               _capture_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2>%3</i>", _("Rec"), _("all"), ffs (midi_track()->get_capture_channel_mask())));
+               _capture_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2>%3</i>", _("Rec"), _("all"), PBD::ffs (midi_track()->get_capture_channel_mask())));
                break;
        }
 }