Allow to send immediate PC messages without closing the dialog.
[ardour.git] / gtk2_ardour / mixer_strip.cc
index 21212ddb56b97d969dc1576855697fa5a8efac11..96383cad984954500edc0c0258b92189e969f10a 100644 (file)
 
 #include <sigc++/bind.h>
 
+#include <gtkmm/messagedialog.h>
+
 #include "pbd/convert.h"
 #include "pbd/enumwriter.h"
 #include "pbd/replace_all.h"
 #include "pbd/stacktrace.h"
 
-#include <gtkmm2ext/gtk_ui.h>
-#include <gtkmm2ext/utils.h>
-#include <gtkmm2ext/choice.h>
-#include <gtkmm2ext/doi.h>
-#include <gtkmm2ext/slider_controller.h>
-#include <gtkmm2ext/bindable_button.h>
-
 #include "ardour/amp.h"
 #include "ardour/audio_track.h"
 #include "ardour/audioengine.h"
 #include "ardour/vca.h"
 #include "ardour/vca_manager.h"
 
+#include "gtkmm2ext/gtk_ui.h"
+#include "gtkmm2ext/menu_elems.h"
+#include "gtkmm2ext/utils.h"
+#include "gtkmm2ext/doi.h"
+
+#include "widgets/tooltips.h"
+
 #include "ardour_window.h"
 #include "enums_convert.h"
 #include "mixer_strip.h"
 #include "mixer_ui.h"
 #include "keyboard.h"
-#include "ardour_button.h"
 #include "public_editor.h"
 #include "send_ui.h"
 #include "io_selector.h"
 #include "gui_thread.h"
 #include "route_group_menu.h"
 #include "meter_patterns.h"
-#include "tooltips.h"
 #include "ui_config.h"
 
 #include "pbd/i18n.h"
 
 using namespace ARDOUR;
-using namespace ARDOUR_UI_UTILS;
+using namespace ArdourWidgets;
 using namespace PBD;
 using namespace Gtk;
 using namespace Gtkmm2ext;
@@ -268,6 +268,8 @@ MixerStrip::init ()
        trim_control.set_tooltip_prefix (_("Trim: "));
        trim_control.set_name ("trim knob");
        trim_control.set_no_show_all (true);
+       trim_control.StartGesture.connect(sigc::mem_fun(*this, &MixerStrip::trim_start_touch));
+       trim_control.StopGesture.connect(sigc::mem_fun(*this, &MixerStrip::trim_end_touch));
        input_button_box.pack_start (trim_control, false, false);
 
        global_vpacker.set_border_width (1);
@@ -432,7 +434,7 @@ MixerStrip::vca_assign (boost::shared_ptr<ARDOUR::VCA> vca)
 {
        boost::shared_ptr<Slavable> sl = boost::dynamic_pointer_cast<Slavable> ( route() );
        if (sl)
-               sl->assign(vca, false);
+               sl->assign(vca);
 }
 
 void
@@ -497,6 +499,24 @@ MixerStrip::update_trim_control ()
        }
 }
 
+void
+MixerStrip::trim_start_touch ()
+{
+       assert (_route && _session);
+       if (route()->trim() && route()->trim()->active() && route()->n_inputs().n_audio() > 0) {
+               route()->trim()->gain_control ()->start_touch (_session->transport_frame());
+       }
+}
+
+void
+MixerStrip::trim_end_touch ()
+{
+       assert (_route && _session);
+       if (route()->trim() && route()->trim()->active() && route()->n_inputs().n_audio() > 0) {
+               route()->trim()->gain_control ()->stop_touch (_session->transport_frame());
+       }
+}
+
 void
 MixerStrip::set_route (boost::shared_ptr<Route> rt)
 {
@@ -884,27 +904,37 @@ MixerStrip::output_press (GdkEventButton *ev)
 
                boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
 
-               /* give user bundles first chance at being in the menu */
+               /* guess the user-intended main type of the route output */
+               DataType intended_type = guess_main_type(false);
+
+               /* try adding the master bus first */
+               boost::shared_ptr<Route> master = _session->master_out();
+               if (master) {
+                       maybe_add_bundle_to_output_menu (master->input()->bundle(), current, intended_type);
+               }
+
+               /* then other routes inputs */
+               boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
+               RouteList copy = *routes;
+               copy.sort (RouteCompareByName ());
+               for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
+                       maybe_add_bundle_to_output_menu ((*i)->input()->bundle(), current, intended_type);
+               }
 
+               /* then try adding user bundles, often labeled/grouped physical inputs */
                for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
                        if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
-                               maybe_add_bundle_to_output_menu (*i, current);
+                               maybe_add_bundle_to_output_menu (*i, current, intended_type);
                        }
                }
 
+               /* then all other bundles, including physical outs or other sofware */
                for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
                        if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
-                               maybe_add_bundle_to_output_menu (*i, current);
+                               maybe_add_bundle_to_output_menu (*i, current, intended_type);
                        }
                }
 
-               boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
-               RouteList copy = *routes;
-               copy.sort (RouteCompareByName ());
-               for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
-                       maybe_add_bundle_to_output_menu ((*i)->input()->bundle(), current);
-               }
-
                if (citems.size() == n_with_separator) {
                        /* no routes added; remove the separator */
                        citems.pop_back ();
@@ -1048,13 +1078,7 @@ MixerStrip::bundle_input_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
                return;
        }
 
-       ARDOUR::BundleList current = _route->input()->bundles_connected ();
-
-       if (std::find (current.begin(), current.end(), c) == current.end()) {
-               _route->input()->connect_ports_to_bundle (c, true, this);
-       } else {
-               _route->input()->disconnect_ports_from_bundle (c, this);
-       }
+       _route->input()->connect_ports_to_bundle (c, true, this);
 }
 
 void
@@ -1064,13 +1088,7 @@ MixerStrip::bundle_output_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
                return;
        }
 
-       ARDOUR::BundleList current = _route->output()->bundles_connected ();
-
-       if (std::find (current.begin(), current.end(), c) == current.end()) {
-               _route->output()->connect_ports_to_bundle (c, true, this);
-       } else {
-               _route->output()->disconnect_ports_from_bundle (c, this);
-       }
+       _route->output()->connect_ports_to_bundle (c, true, true, this);
 }
 
 void
@@ -1094,49 +1112,59 @@ MixerStrip::maybe_add_bundle_to_input_menu (boost::shared_ptr<Bundle> b, ARDOUR:
        input_menu_bundles.push_back (b);
 
        MenuList& citems = input_menu.items();
-
-       std::string n = b->name ();
-       replace_all (n, "_", " ");
-
-       citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_input_chosen), b)));
+       citems.push_back (MenuElemNoMnemonic (b->name (), sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_input_chosen), b)));
 }
 
 void
-MixerStrip::maybe_add_bundle_to_output_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
+MixerStrip::maybe_add_bundle_to_output_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/,
+                                             DataType type)
 {
        using namespace Menu_Helpers;
 
-       if (b->ports_are_inputs() == false || b->nchannels() != _route->n_outputs() || *b == *_route->input()->bundle()) {
+       /* The bundle should be an input one, but not ours */
+       if (b->ports_are_inputs() == false || *b == *_route->input()->bundle()) {
                return;
        }
 
+       /* Don't add the monitor input unless we are Master */
+       boost::shared_ptr<Route> monitor = _session->monitor_out();
+       if ((!_route->is_master()) && monitor && b->has_same_ports (monitor->input()->bundle()))
+               return;
+
+       /* It should either match exactly our outputs (if |type| is DataType::NIL)
+        * or have the same number of |type| channels than our outputs. */
+       if (type == DataType::NIL) {
+               if(b->nchannels() != _route->n_outputs())
+                       return;
+       } else {
+               if (b->nchannels().n(type) != _route->n_outputs().n(type))
+                       return;
+       }
+
+       /* Avoid adding duplicates */
        list<boost::shared_ptr<Bundle> >::iterator i = output_menu_bundles.begin ();
        while (i != output_menu_bundles.end() && b->has_same_ports (*i) == false) {
                ++i;
        }
-
        if (i != output_menu_bundles.end()) {
                return;
        }
 
+       /* Now add the bundle to the menu */
        output_menu_bundles.push_back (b);
 
        MenuList& citems = output_menu.items();
-
-       std::string n = b->name ();
-       replace_all (n, "_", " ");
-
-       citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_output_chosen), b)));
+       citems.push_back (MenuElemNoMnemonic (b->name (), sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_output_chosen), b)));
 }
 
 void
 MixerStrip::update_diskstream_display ()
 {
-        if (is_track() && input_selector) {
-                        input_selector->hide_all ();
-        }
+       if (is_track() && input_selector) {
+               input_selector->hide_all ();
+       }
 
-        route_color_changed ();
+       route_color_changed ();
 }
 
 void
@@ -1183,6 +1211,53 @@ MixerStrip::update_panner_choices ()
        panners.set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
 }
 
+DataType
+MixerStrip::guess_main_type(bool for_input, bool favor_connected) const
+{
+       /* The heuristic follows these principles:
+        *  A) If all ports that the user connected are of the same type, then he
+        *     very probably intends to use the IO with that type. A common subcase
+        *     is when the IO has only ports of the same type (connected or not).
+        *  B) If several types of ports are connected, then we should guess based
+        *     on the likeliness of the user wanting to use a given type.
+        *     We assume that the DataTypes are ordered from the most likely to the
+        *     least likely when iterating or comparing them with "<".
+        *  C) If no port is connected, the same logic can be applied with all ports
+        *     instead of connected ones. TODO: Try other ideas, for instance look at
+        *     the last plugin output when |for_input| is false (note: when StrictIO
+        *     the outs of the last plugin should be the same as the outs of the route
+        *     modulo the panner which forwards non-audio anyway).
+        * All of these constraints are respected by the following algorithm that
+        * just returns the most likely datatype found in connected ports if any, or
+        * available ports if any (since if all ports are of the same type, the most
+        * likely found will be that one obviously). */
+
+       boost::shared_ptr<IO> io = for_input ? _route->input() : _route->output();
+
+       /* Find most likely type among connected ports */
+       if (favor_connected) {
+               DataType type = DataType::NIL; /* NIL is always last so least likely */
+               for (PortSet::iterator p = io->ports().begin(); p != io->ports().end(); ++p) {
+                       if (p->connected() && p->type() < type)
+                               type = p->type();
+               }
+               if (type != DataType::NIL) {
+                       /* There has been a connected port (necessarily non-NIL) */
+                       return type;
+               }
+       }
+
+       /* Find most likely type among available ports.
+        * The iterator stops before NIL. */
+       for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
+               if (io->n_ports().n(*t) > 0)
+                       return *t;
+       }
+
+       /* No port at all, return the most likely datatype by default */
+       return DataType::front();
+}
+
 /*
  * Output port labelling
  * =====================
@@ -1223,12 +1298,8 @@ MixerStrip::update_panner_choices ()
  */
 
 void
-MixerStrip::update_io_button (boost::shared_ptr<ARDOUR::Route> route, Width width, bool for_input)
+MixerStrip::update_io_button (bool for_input)
 {
-       uint32_t io_count;
-       uint32_t io_index;
-       boost::shared_ptr<IO> io;
-       boost::shared_ptr<Port> port;
        vector<string> port_connections;
 
        uint32_t total_connection_count = 0;
@@ -1252,71 +1323,20 @@ MixerStrip::update_io_button (boost::shared_ptr<ARDOUR::Route> route, Width widt
        ostringstream tooltip;
        char * tooltip_cstr;
 
-       /* To avoid confusion, the button caption only shows connections that match the expected datatype
-        *
-        * First of all, if the user made only connections to a given type, we should use that one since
-        * it is very probably what the user expects. If there are several connections types, then show
-        * audio ones as primary, which matches expectations for both audio tracks with midi control and
-        * synthesisers. This first heuristic can be expressed with these two rules:
-        * A) If there are connected audio ports, consider audio as primary type.
-        * B) Else, if there are connected midi ports, consider midi as primary type.
-        *
-        * If there are no connected ports, then we choose the primary type based on the type of existing
-        * but unconnected ports. Again:
-        * C) If there are audio ports, consider audio as primary type.
-        * D) Else, if there are midi ports, consider midi as primary type. */
-
-       DataType dt = DataType::AUDIO;
-       bool match = false;
-
-       if (for_input) {
-               io = route->input();
-       } else {
-               io = route->output();
-       }
-
-       io_count = io->n_ports().n_total();
-       for (io_index = 0; io_index < io_count; ++io_index) {
-               port = io->nth (io_index);
-               if (port->connected()) {
-                       match = true;
-                       if (port->type() == DataType::AUDIO) {
-                               /* Rule A) applies no matter the remaining ports */
-                               dt = DataType::AUDIO;
-                               break;
-                       }
-                       if (port->type() == DataType::MIDI) {
-                               /* Rule B) is a good candidate... */
-                               dt = DataType::MIDI;
-                               /* ...but continue the loop to check remaining ports for rule A) */
-                       }
-               }
-       }
-
-       if (!match) {
-               /* Neither rule A) nor rule B) matched */
-               if ( io->n_ports().n_audio() > 0 ) {
-                       /* Rule C */
-                       dt = DataType::AUDIO;
-               } else if ( io->n_ports().n_midi() > 0 ) {
-                       /* Rule D */
-                       dt = DataType::MIDI;
-               }
-       }
+       DataType dt = guess_main_type(for_input);
 
        if ( dt == DataType::MIDI ) {
                tooltip << _("MIDI ");
        }
 
        if (for_input) {
-               tooltip << string_compose (_("<b>INPUT</b> to %1"), Gtkmm2ext::markup_escape_text (route->name()));
+               tooltip << string_compose (_("<b>INPUT</b> to %1"), Gtkmm2ext::markup_escape_text (_route->name()));
        } else {
-               tooltip << string_compose (_("<b>OUTPUT</b> from %1"), Gtkmm2ext::markup_escape_text (route->name()));
+               tooltip << string_compose (_("<b>OUTPUT</b> from %1"), Gtkmm2ext::markup_escape_text (_route->name()));
        }
 
-       for (io_index = 0; io_index < io_count; ++io_index) {
-               port = io->nth (io_index);
-
+       boost::shared_ptr<IO> io = for_input ? _route->input() : _route->output();
+       for (PortSet::iterator port = io->ports().begin(); port != io->ports().end(); ++port) {
                port_connections.clear ();
                port->get_connections(port_connections);
 
@@ -1485,7 +1505,7 @@ MixerStrip::update_io_button (boost::shared_ptr<ARDOUR::Route> route, Width widt
 void
 MixerStrip::update_input_display ()
 {
-       update_io_button (_route, _width, true);
+       update_io_button (true);
        panners.setup_pan ();
 
        if (has_audio_outputs ()) {
@@ -1499,7 +1519,7 @@ MixerStrip::update_input_display ()
 void
 MixerStrip::update_output_display ()
 {
-       update_io_button (_route, _width, false);
+       update_io_button (false);
        gpm.setup_meters ();
        panners.setup_pan ();
 
@@ -1628,6 +1648,7 @@ MixerStrip::route_group_changed ()
 void
 MixerStrip::route_color_changed ()
 {
+       using namespace ARDOUR_UI_UTILS;
        name_button.modify_bg (STATE_NORMAL, color());
        number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
        reset_strip_style ();
@@ -1737,11 +1758,11 @@ MixerStrip::build_route_ops_menu ()
                   sane thing for users anyway.
                */
 
-               RouteTimeAxisView* rtav = PublicEditor::instance().get_route_view_by_route_id (_route->id());
-               if (rtav) {
+               StripableTimeAxisView* stav = PublicEditor::instance().get_stripable_time_axis_by_id (_route->id());
+               if (stav) {
                        Selection& selection (PublicEditor::instance().get_selection());
-                       if (!selection.selected (rtav)) {
-                               selection.set (rtav);
+                       if (!selection.selected (stav)) {
+                               selection.set (stav);
                        }
 
                        if (!_route->is_master()) {