Temper pants - fix newly added lockup while dragging tempos sometimes.
[ardour.git] / gtk2_ardour / route_ui.cc
index 6935afc5478bbe58517129c9bc050e4e2972fbc0..a0e9ff5ed176844632b5b43bbf32e0d3e917d0c5 100644 (file)
@@ -40,6 +40,7 @@
 #include "ardour_button.h"
 #include "keyboard.h"
 #include "utils.h"
+#include "plugin_pin_dialog.h"
 #include "prompter.h"
 #include "gui_thread.h"
 #include "ardour_dialog.h"
@@ -108,7 +109,7 @@ RouteUI::~RouteUI()
        delete solo_menu;
        delete mute_menu;
        delete sends_menu;
-        delete record_menu;
+       delete record_menu;
        delete comment_window;
        delete input_selector;
        delete output_selector;
@@ -125,20 +126,21 @@ RouteUI::init ()
        mute_menu = 0;
        solo_menu = 0;
        sends_menu = 0;
-        record_menu = 0;
+       record_menu = 0;
        _invert_menu = 0;
        pre_fader_mute_check = 0;
        post_fader_mute_check = 0;
        listen_mute_check = 0;
        main_mute_check = 0;
-        solo_safe_check = 0;
-        solo_isolated_check = 0;
-        solo_isolated_led = 0;
-        solo_safe_led = 0;
+       solo_safe_check = 0;
+       solo_isolated_check = 0;
+       solo_isolated_led = 0;
+       solo_safe_led = 0;
        _solo_release = 0;
        _mute_release = 0;
        denormal_menu_item = 0;
-        step_edit_item = 0;
+       step_edit_item = 0;
+       rec_safe_item = 0;
        multiple_mute_change = false;
        multiple_solo_change = false;
        _i_am_the_modifier = 0;
@@ -266,6 +268,7 @@ RouteUI::set_route (boost::shared_ptr<Route> rp)
        _route->listen_changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::update_solo_display, this), gui_context());
        _route->solo_isolated_changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::update_solo_display, this), gui_context());
        if (is_track()) {
+               track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteUI::map_frozen, this), gui_context());
                track()->TrackModeChanged.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::track_mode_changed, this), gui_context());
                track_mode_changed();
        }
@@ -280,9 +283,10 @@ RouteUI::set_route (boost::shared_ptr<Route> rp)
                boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track>(_route);
 
                t->RecordEnableChanged.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::route_rec_enable_changed, this), gui_context());
+               t->RecordSafeChanged.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::route_rec_enable_changed, this), gui_context());
 
                rec_enable_button->show();
-               rec_enable_button->set_controllable (t->rec_enable_control());
+               rec_enable_button->set_controllable (t->rec_enable_control());
 
                 if (is_midi_track()) {
                         midi_track()->StepEditStatusChange.connect (route_connections, invalidator (*this),
@@ -328,6 +332,8 @@ RouteUI::set_route (boost::shared_ptr<Route> rp)
                blink_rec_display(true); // set initial rec-en button state
        }
 
+       check_rec_enable_sensitivity ();
+       maybe_add_route_print_mgr ();
        route_color_changed();
 }
 
@@ -559,9 +565,9 @@ RouteUI::solo_press(GdkEventButton* ev)
 
                                DisplaySuspender ds;
                                if (Config->get_solo_control_is_listen_control()) {
-                                       _session->set_listen (_session->get_routes(), !_route->listening_via_monitor(),  Session::rt_cleanup, Controllable::NoGroup);
+                                       _session->set_listen (_session->get_routes(), !_route->listening_via_monitor(),  Session::rt_cleanup, Controllable::UseGroup);
                                } else {
-                                       _session->set_solo (_session->get_routes(), !_route->self_soloed(),  Session::rt_cleanup, Controllable::NoGroup);
+                                       _session->set_solo (_session->get_routes(), !_route->self_soloed(),  Session::rt_cleanup, Controllable::UseGroup);
                                }
 
                        } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
@@ -610,19 +616,29 @@ RouteUI::solo_press(GdkEventButton* ev)
                                boost::shared_ptr<RouteList> rl;
 
                                if (ev->button == 1) {
-                                       if (ARDOUR::Profile->get_mixbus() && _route->route_group()) {
 
-                                               rl = _route->route_group()->route_list();
+                                       /* Primary-button1 inverts the implication of
+                                          the group being active. If the group is
+                                          active (for solo), then this modifier means
+                                          "do not apply to solo". If the group is
+                                          inactive (for mute), then this modifier
+                                          means "apply to route". This is all
+                                          accomplished by passing just the actual
+                                          route, along with the InverseGroup group
+                                          control disposition.
 
-                                               if (_solo_release) {
-                                                       _solo_release->routes = rl;
-                                               }
-                                       } else {
-                                               rl.reset (new RouteList);
-                                               rl->push_back (_route);
+                                          NOTE: Primary-button2 is MIDI learn.
+                                       */
+
+                                       rl.reset (new RouteList);
+                                       rl->push_back (_route);
+
+                                       if (_solo_release) {
+                                               _solo_release->routes = rl;
                                        }
 
                                        DisplaySuspender ds;
+
                                        if (Config->get_solo_control_is_listen_control()) {
                                                _session->set_listen (rl, !_route->listening_via_monitor(),  Session::rt_cleanup, Controllable::InverseGroup);
                                        } else {
@@ -729,14 +745,8 @@ RouteUI::rec_enable_press(GdkEventButton* ev)
 
                                boost::shared_ptr<RouteList> rl;
 
-                               if (_route->route_group()) {
-
-                                       rl = _route->route_group()->route_list();
-
-                               } else {
-                                       rl.reset (new RouteList);
-                                       rl->push_back (_route);
-                               }
+                               rl.reset (new RouteList);
+                               rl->push_back (_route);
 
                                DisplaySuspender ds;
                                _session->set_record_enabled (rl, !_route->record_enabled(), Session::rt_cleanup, Controllable::InverseGroup);
@@ -877,29 +887,30 @@ RouteUI::monitor_release (GdkEventButton* ev, MonitorChoice monitor_choice)
 void
 RouteUI::build_record_menu ()
 {
-       if (record_menu) {
-               return;
-       }
-
-       /* no rec-button context menu for non-MIDI tracks
-        */
-
-       if (is_midi_track()) {
+       if (!record_menu) {
                record_menu = new Menu;
                record_menu->set_name ("ArdourContextMenu");
-
                using namespace Menu_Helpers;
                MenuList& items = record_menu->items();
 
-               items.push_back (CheckMenuElem (_("Step Entry"), sigc::mem_fun (*this, &RouteUI::toggle_step_edit)));
-               step_edit_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
+               items.push_back (CheckMenuElem (_("Rec-Safe"), sigc::mem_fun (*this, &RouteUI::toggle_rec_safe)));
+               rec_safe_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
 
-               if (_route->record_enabled()) {
-                       step_edit_item->set_sensitive (false);
+               if (is_midi_track()) {
+                       items.push_back (SeparatorElem());
+                       items.push_back (CheckMenuElem (_("Step Entry"), sigc::mem_fun (*this, &RouteUI::toggle_step_edit)));
+                       step_edit_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
                }
+       }
 
+       if (step_edit_item) {
+               step_edit_item->set_sensitive (!_route->record_enabled());
                step_edit_item->set_active (midi_track()->step_editing());
        }
+       if (rec_safe_item) {
+               rec_safe_item->set_sensitive (!_route->record_enabled());
+               rec_safe_item->set_active (_route->record_safe());
+       }
 }
 
 void
@@ -912,6 +923,18 @@ RouteUI::toggle_step_edit ()
        midi_track()->set_step_editing (step_edit_item->get_active());
 }
 
+void
+RouteUI::toggle_rec_safe ()
+{
+       if (_route->record_enabled()) {
+               return;
+       }
+       DisplaySuspender ds;
+       boost::shared_ptr<RouteList> rl (new RouteList);
+       rl->push_back (_route);
+       _session->set_record_safe (rl, rec_safe_item->get_active (), Session::rt_cleanup);
+}
+
 void
 RouteUI::step_edit_changed (bool yn)
 {
@@ -1301,7 +1324,6 @@ RouteUI::blink_rec_display (bool blinkOn)
                 }
        }
 
-
        check_rec_enable_sensitivity ();
 }
 
@@ -1832,17 +1854,10 @@ RouteUI::map_frozen ()
 {
        ENSURE_GUI_THREAD (*this, &RouteUI::map_frozen)
 
-       AudioTrack* at = dynamic_cast<AudioTrack*>(_route.get());
+       AudioTrack* at = dynamic_cast<AudioTrack*>(_route.get());
 
        if (at) {
-               switch (at->freeze_state()) {
-               case AudioTrack::Frozen:
-                       rec_enable_button->set_sensitive (false);
-                       break;
-               default:
-                       rec_enable_button->set_sensitive (true);
-                       break;
-               }
+               check_rec_enable_sensitivity ();
        }
 }
 
@@ -1915,12 +1930,27 @@ RouteUI::save_as_template ()
 void
 RouteUI::check_rec_enable_sensitivity ()
 {
+       if (!rec_enable_button) {
+               assert (0); // This should not happen
+               return;
+       }
+       if (!_session->writable()) {
+               rec_enable_button->set_sensitive (false);
+               return;
+       }
+
        if (_session->transport_rolling() && rec_enable_button->active_state() && Config->get_disable_disarm_during_roll()) {
                rec_enable_button->set_sensitive (false);
+       } else if (is_audio_track ()  && track()->freeze_state() == AudioTrack::Frozen) {
+               rec_enable_button->set_sensitive (false);
        } else {
                rec_enable_button->set_sensitive (true);
        }
-
+       if (_route && _route->record_safe ()) {
+               rec_enable_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
+       } else {
+               rec_enable_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
+       }
        update_monitoring_display ();
 }
 
@@ -2246,3 +2276,91 @@ RouteUI::route_group() const
 {
        return _route->route_group();
 }
+
+
+RoutePinWindowProxy::RoutePinWindowProxy(std::string const &name, boost::shared_ptr<ARDOUR::Route> route)
+       : WM::ProxyBase (name, string())
+       , _route (boost::weak_ptr<Route> (route))
+{
+       route->DropReferences.connect (going_away_connection, MISSING_INVALIDATOR, boost::bind (&RoutePinWindowProxy::route_going_away, this), gui_context());
+}
+
+RoutePinWindowProxy::~RoutePinWindowProxy()
+{
+       _window = 0;
+}
+
+ARDOUR::SessionHandlePtr*
+RoutePinWindowProxy::session_handle ()
+{
+       ArdourWindow* aw = dynamic_cast<ArdourWindow*> (_window);
+       if (aw) { return aw; }
+       return 0;
+}
+
+Gtk::Window*
+RoutePinWindowProxy::get (bool create)
+{
+       boost::shared_ptr<Route> r = _route.lock ();
+       if (!r) {
+               return 0;
+       }
+
+       if (!_window) {
+               if (!create) {
+                       return 0;
+               }
+               _window = new PluginPinDialog (r);
+               ArdourWindow* aw = dynamic_cast<ArdourWindow*> (_window);
+               if (aw) {
+                       aw->set_session (_session);
+               }
+               _window->show_all ();
+       }
+       return _window;
+}
+
+void
+RoutePinWindowProxy::route_going_away ()
+{
+       delete _window;
+       _window = 0;
+       WM::Manager::instance().remove (this);
+       going_away_connection.disconnect();
+}
+
+void
+RouteUI::maybe_add_route_print_mgr ()
+{
+       if (_route->pinmgr_proxy ()) {
+               return;
+       }
+       RoutePinWindowProxy* wp = new RoutePinWindowProxy (
+                       string_compose ("RPM-%1", _route->id()), _route);
+       wp->set_session (_session);
+
+       const XMLNode* ui_xml = _session->extra_xml (X_("UI"));
+       if (ui_xml) {
+               wp->set_state (*ui_xml, 0);
+       }
+
+#if 0
+       void* existing_ui = _route->pinmgr_proxy ();
+       if (existing_ui) {
+               wp->use_window (*(reinterpret_cast<Gtk::Window*>(existing_ui)));
+       }
+#endif
+       _route->set_pingmgr_proxy (wp);
+
+       WM::Manager::instance().register_window (wp);
+}
+
+void
+RouteUI::manage_pins ()
+{
+       RoutePinWindowProxy* proxy = _route->pinmgr_proxy ();
+       if (proxy) {
+               proxy->get (true);
+               proxy->present();
+       }
+}