Allow strips to add or remove personal sends
[ardour.git] / gtk2_ardour / processor_box.cc
index ca1e448b587e4176ba5ab95dac348632d4ec544f..3b23f294a3649bede5c28db7289e85f5322d8543 100644 (file)
@@ -113,6 +113,7 @@ RefPtr<Action> ProcessorBox::rename_action;
 RefPtr<Action> ProcessorBox::delete_action;
 RefPtr<Action> ProcessorBox::backspace_action;
 RefPtr<Action> ProcessorBox::manage_pins_action;
+RefPtr<Action> ProcessorBox::disk_io_action;
 RefPtr<Action> ProcessorBox::edit_action;
 RefPtr<Action> ProcessorBox::edit_generic_action;
 RefPtr<ActionGroup> ProcessorBox::processor_box_actions;
@@ -217,11 +218,17 @@ ProcessorEntry::ProcessorEntry (ProcessorBox* parent, boost::shared_ptr<Processo
                _processor->PropertyChanged.connect (name_connection, invalidator (*this), boost::bind (&ProcessorEntry::processor_property_changed, this, _1), gui_context());
                _processor->ConfigurationChanged.connect (config_connection, invalidator (*this), boost::bind (&ProcessorEntry::processor_configuration_changed, this, _1, _2), gui_context());
 
+               const uint32_t limit_inline_controls = UIConfiguration::instance().get_max_inline_controls ();
+
                set<Evoral::Parameter> p = _processor->what_can_be_automated ();
                for (set<Evoral::Parameter>::iterator i = p.begin(); i != p.end(); ++i) {
 
                        std::string label = _processor->describe_parameter (*i);
 
+                       if (label == X_("hidden")) {
+                               continue;
+                       }
+
                        if (boost::dynamic_pointer_cast<Send> (_processor)) {
                                label = _("Send");
                        } else if (boost::dynamic_pointer_cast<Return> (_processor)) {
@@ -236,6 +243,10 @@ ProcessorEntry::ProcessorEntry (ProcessorBox* parent, boost::shared_ptr<Processo
                                /* Add non-Amp (Fader & Trim) controls to the processor box */
                                _vbox.pack_start (c->box);
                        }
+
+                       if (limit_inline_controls > 0 && _controls.size() >= limit_inline_controls) {
+                               break;
+                       }
                }
 
                setup_tooltip ();
@@ -706,6 +717,11 @@ Menu *
 ProcessorEntry::build_controls_menu ()
 {
        using namespace Menu_Helpers;
+
+       if (!_plugin_display && _controls.empty ()) {
+               return NULL;
+       }
+
        Menu* menu = manage (new Menu);
        MenuList& items = menu->items ();
 
@@ -714,6 +730,11 @@ ProcessorEntry::build_controls_menu ()
                Gtk::CheckMenuItem* c = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
                c->set_active (_plugin_display->is_visible ());
                c->signal_toggled().connect (sigc::mem_fun (*this, &ProcessorEntry::toggle_inline_display_visibility));
+       }
+
+       if (_controls.empty ()) {
+               return menu;
+       } else {
                items.push_back (SeparatorElem ());
        }
 
@@ -725,9 +746,7 @@ ProcessorEntry::build_controls_menu ()
                MenuElem (_("Hide All Controls"), sigc::mem_fun (*this, &ProcessorEntry::hide_all_controls))
                );
 
-       if (!_controls.empty ()) {
-               items.push_back (SeparatorElem ());
-       }
+       items.push_back (SeparatorElem ());
 
        for (list<Control*>::iterator i = _controls.begin(); i != _controls.end(); ++i) {
                items.push_back (CheckMenuElemNoMnemonic ((*i)->name ()));
@@ -911,7 +930,7 @@ ProcessorEntry::Control::start_touch ()
        if (!c) {
                return;
        }
-       c->start_touch (c->session().transport_frame());
+       c->start_touch (c->session().transport_sample());
 }
 
 void
@@ -921,7 +940,7 @@ ProcessorEntry::Control::end_touch ()
        if (!c) {
                return;
        }
-       c->stop_touch (c->session().transport_frame());
+       c->stop_touch (c->session().transport_sample());
 }
 
 void
@@ -1567,6 +1586,7 @@ ProcessorEntry::PluginInlineDisplay::PluginInlineDisplay (ProcessorEntry& e, boo
        : PluginDisplay (p, max_height)
        , _entry (e)
        , _scroll (false)
+       , _given_max_height (max_height)
 {
        std::string postfix = string_compose(_("\n%1+double-click to toggle inline-display"), Keyboard::tertiary_modifier_name ());
 
@@ -1638,16 +1658,20 @@ ProcessorEntry::PluginInlineDisplay::update_height_alloc (uint32_t inline_height
        }
 
        if (shm != _cur_height) {
-               if (_scroll == sc || _cur_height < shm) {
-                       queue_resize ();
+               queue_resize ();
+               if (!_scroll && sc) {
+                       _max_height = shm;
+               } else {
+                       _max_height = _given_max_height;
                }
                _cur_height = shm;
        }
+
        _scroll = sc;
 }
 
 void
-ProcessorEntry::PluginInlineDisplay::display_frame (cairo_t* cr, double w, double h)
+ProcessorEntry::PluginInlineDisplay::display_sample (cairo_t* cr, double w, double h)
 {
        Gtkmm2ext::rounded_rectangle (cr, .5, -1.5, w - 1, h + 1, 7);
 }
@@ -2010,7 +2034,43 @@ ProcessorBox::build_possible_aux_menu ()
                return 0;
        }
 
-       if (_route->is_monitor ()) {
+       if (_route->is_monitor () || _route->is_listenbus ()) {
+               return 0;
+       }
+
+       using namespace Menu_Helpers;
+       Menu* menu = manage (new Menu);
+       MenuList& items = menu->items();
+
+       for (RouteList::iterator r = rl->begin(); r != rl->end(); ++r) {
+               if ((*r)->is_master() || (*r)->is_monitor () || *r == _route) {
+                       /* don't allow sending to master or monitor or to self */
+                       continue;
+               }
+               if ((*r)->is_listenbus ()) {
+                       continue;
+               }
+               if (_route->internal_send_for (*r)) {
+                       /* aux-send to target already exists */
+                       continue;
+               }
+               items.push_back (MenuElemNoMnemonic ((*r)->name(), sigc::bind (sigc::ptr_fun (ProcessorBox::rb_choose_aux), boost::weak_ptr<Route>(*r))));
+       }
+
+       return menu;
+}
+
+Gtk::Menu*
+ProcessorBox::build_possible_listener_menu ()
+{
+       boost::shared_ptr<RouteList> rl = _session->get_routes_with_internal_returns();
+
+       if (rl->empty()) {
+               /* No aux sends if there are no busses */
+               return 0;
+       }
+
+       if (_route->is_monitor () || _route->is_listenbus ()) {
                return 0;
        }
 
@@ -2019,9 +2079,54 @@ ProcessorBox::build_possible_aux_menu ()
        MenuList& items = menu->items();
 
        for (RouteList::iterator r = rl->begin(); r != rl->end(); ++r) {
-               if (!_route->internal_send_for (*r) && *r != _route) {
-                       items.push_back (MenuElemNoMnemonic ((*r)->name(), sigc::bind (sigc::ptr_fun (ProcessorBox::rb_choose_aux), boost::weak_ptr<Route>(*r))));
+               if ((*r)->is_master() || (*r)->is_monitor () || *r == _route) {
+                       /* don't allow sending to master or monitor or to self */
+                       continue;
                }
+               if (!(*r)->is_listenbus ()) {
+                       continue;
+               }
+               if (_route->internal_send_for (*r)) {
+                       /* aux-send to target already exists */
+                       continue;
+               }
+               items.push_back (MenuElemNoMnemonic ((*r)->name(), sigc::bind (sigc::ptr_fun (ProcessorBox::rb_choose_aux), boost::weak_ptr<Route>(*r))));
+       }
+
+       return menu;
+}
+
+Gtk::Menu*
+ProcessorBox::build_possible_remove_listener_menu ()
+{
+       boost::shared_ptr<RouteList> rl = _session->get_routes_with_internal_returns();
+
+       if (rl->empty()) {
+               /* No aux sends if there are no busses */
+               return 0;
+       }
+
+       if (_route->is_monitor () || _route->is_listenbus ()) {
+               return 0;
+       }
+
+       using namespace Menu_Helpers;
+       Menu* menu = manage (new Menu);
+       MenuList& items = menu->items();
+
+       for (RouteList::iterator r = rl->begin(); r != rl->end(); ++r) {
+               if ((*r)->is_master() || (*r)->is_monitor () || *r == _route) {
+                       /* don't allow sending to master or monitor or to self */
+                       continue;
+               }
+               if (!(*r)->is_listenbus ()) {
+                       continue;
+               }
+               if (!_route->internal_send_for (*r)) {
+                       /* aux-send to target already exists */
+                       continue;
+               }
+               items.push_back (MenuElemNoMnemonic ((*r)->name(), sigc::bind (sigc::ptr_fun (ProcessorBox::rb_remove_aux), boost::weak_ptr<Route>(*r))));
        }
 
        return menu;
@@ -2059,8 +2164,36 @@ ProcessorBox::show_processor_menu (int arg)
                }
        }
 
-       ActionManager::get_action (X_("ProcessorMenu"), "newinsert")->set_sensitive (!_route->is_monitor ());
-       ActionManager::get_action (X_("ProcessorMenu"), "newsend")->set_sensitive (!_route->is_monitor ());
+       Gtk::MenuItem* listen_menu_item = dynamic_cast<Gtk::MenuItem*>(ActionManager::get_widget("/ProcessorMenu/newlisten"));
+
+       if (listen_menu_item) {
+               Menu* m = build_possible_listener_menu();
+               if (m && !m->items().empty()) {
+                       listen_menu_item->set_submenu (*m);
+                       listen_menu_item->set_sensitive (true);
+               } else {
+                       /* stupid gtkmm: we need to pass a null reference here */
+                       gtk_menu_item_set_submenu (listen_menu_item->gobj(), 0);
+                       listen_menu_item->set_sensitive (false);
+               }
+       }
+
+       Gtk::MenuItem* remove_listen_menu_item = dynamic_cast<Gtk::MenuItem*>(ActionManager::get_widget("/ProcessorMenu/removelisten"));
+
+       if (remove_listen_menu_item) {
+               Menu* m = build_possible_remove_listener_menu();
+               if (m && !m->items().empty()) {
+                       remove_listen_menu_item->set_submenu (*m);
+                       remove_listen_menu_item->set_sensitive (true);
+               } else {
+                       /* stupid gtkmm: we need to pass a null reference here */
+                       gtk_menu_item_set_submenu (remove_listen_menu_item->gobj(), 0);
+                       remove_listen_menu_item->set_sensitive (false);
+               }
+       }
+
+       ActionManager::get_action (X_("ProcessorMenu"), "newinsert")->set_sensitive (!_route->is_monitor () && !_route->is_listenbus ());
+       ActionManager::get_action (X_("ProcessorMenu"), "newsend")->set_sensitive (!_route->is_monitor () && !_route->is_listenbus ());
 
        ProcessorEntry* single_selection = 0;
        if (processor_display.selection().size() == 1) {
@@ -2105,7 +2238,6 @@ ProcessorBox::show_processor_menu (int arg)
 
        /* Sensitise actions as approprioate */
 
-
        const bool sensitive = !processor_display.selection().empty() && ! stub_processor_selected ();
 
        paste_action->set_sensitive (!_p_selection.processors.empty());
@@ -2123,6 +2255,11 @@ ProcessorBox::show_processor_menu (int arg)
        }
 
        manage_pins_action->set_sensitive (pi != 0);
+       if (boost::dynamic_pointer_cast<Track>(_route)) {
+               disk_io_action->set_sensitive (true);
+       } else {
+               disk_io_action->set_sensitive (false);
+       }
 
        /* allow editing with an Ardour-generated UI for plugin inserts with editors */
        edit_action->set_sensitive (pi && pi->plugin()->has_editor ());
@@ -2391,9 +2528,13 @@ ProcessorBox::use_plugins (const SelectedPlugins& plugins)
                        return true;
                        // XXX SHAREDPTR delete plugin here .. do we even need to care?
                } else if (plugins.size() == 1 && UIConfiguration::instance().get_open_gui_after_adding_plugin()) {
-                       if (boost::dynamic_pointer_cast<PluginInsert>(processor)->plugin()->has_inline_display() && UIConfiguration::instance().get_prefer_inline_over_gui()) {
-                               ;
-                       } else if (_session->engine().connected () && processor_can_be_edited (processor)) {
+                       if (processor->what_can_be_automated ().size () == 0) {
+                               ; /* plugin without controls, don't show ui */
+                       }
+                       else if (boost::dynamic_pointer_cast<PluginInsert>(processor)->plugin()->has_inline_display() && UIConfiguration::instance().get_prefer_inline_over_gui()) {
+                               ; /* only show inline display */
+                       }
+                       else if (_session->engine().connected () && processor_can_be_edited (processor)) {
                                if ((*p)->has_editor ()) {
                                        edit_processor (processor);
                                } else if (boost::dynamic_pointer_cast<PluginInsert>(processor)->plugin()->parameter_count() > 0) {
@@ -2577,7 +2718,29 @@ ProcessorBox::choose_aux (boost::weak_ptr<Route> wr)
                return;
        }
 
-       _session->add_internal_send (target, _placement, _route);
+       if (target->is_listenbus ()) {
+               _route->add_personal_send (target);
+       } else {
+               _session->add_internal_send (target, _placement, _route);
+       }
+}
+
+void
+ProcessorBox::remove_aux (boost::weak_ptr<Route> wr)
+{
+       if (!_route) {
+               return;
+       }
+
+       boost::shared_ptr<Route> target = wr.lock();
+
+       if (!target) {
+               return;
+       }
+       boost::shared_ptr<Send>  send = _route->internal_send_for (target);
+       boost::shared_ptr<Processor> proc = boost::dynamic_pointer_cast<Processor> (send);
+       _route->remove_processor (proc);
+
 }
 
 void
@@ -2623,27 +2786,34 @@ ProcessorBox::maybe_add_processor_to_ui_list (boost::weak_ptr<Processor> w)
                return;
        }
 
-       /* not on the list; add it */
+       /* see also ProcessorBox::get_editor_window */
+       bool have_ui = false;
 
-       string loc;
-#if 0 // is this still needed? Why?
-       if (_parent_strip) {
-               if (_parent_strip->mixer_owned()) {
-                       loc = X_("M");
-               } else {
-                       loc = X_("R");
+       if (boost::dynamic_pointer_cast<PluginInsert> (p)) {
+               have_ui = true;
+       }
+       else if (boost::dynamic_pointer_cast<PortInsert> (p)) {
+               have_ui = true;
+       }
+       else if (boost::dynamic_pointer_cast<Send> (p)) {
+               if (!boost::dynamic_pointer_cast<InternalSend> (p)) {
+                       have_ui = true;
+               }
+       }
+       else if (boost::dynamic_pointer_cast<Return> (p)) {
+               if (!boost::dynamic_pointer_cast<InternalReturn> (p)) {
+                       have_ui = true;
                }
-       } else {
-               loc = X_("P");
        }
-#else
-       loc = X_("P");
-#endif
+
+       if (!have_ui) {
+               return;
+       }
 
        ProcessorWindowProxy* wp = new ProcessorWindowProxy (
-               string_compose ("%1-%2-%3", loc, _route->id(), p->id()),
-               this,
-               w);
+                       string_compose ("P-%1-%2", _route->id(), p->id()),
+                       this,
+                       w);
 
        const XMLNode* ui_xml = _session->extra_xml (X_("UI"));
 
@@ -2662,9 +2832,12 @@ ProcessorBox::maybe_add_processor_pin_mgr (boost::weak_ptr<Processor> w)
        if (!p || p->pinmgr_proxy ()) {
                return;
        }
+       if (!boost::dynamic_pointer_cast<PluginInsert> (p)) {
+               return;
+       }
 
        PluginPinWindowProxy* wp = new PluginPinWindowProxy (
-                       string_compose ("PM-%2-%3", _route->id(), p->id()), w);
+                       string_compose ("PM-%1-%2", _route->id(), p->id()), w);
        wp->set_session (_session);
 
        const XMLNode* ui_xml = _session->extra_xml (X_("UI"));
@@ -3367,6 +3540,14 @@ ProcessorBox::ab_plugins ()
        ab_direction = !ab_direction;
 }
 
+void
+ProcessorBox::set_disk_io_position (DiskIOPoint diop)
+{
+       boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (_route);
+       if (t) {
+               t->set_disk_io_point (diop);
+       }
+}
 
 void
 ProcessorBox::clear_processors ()
@@ -3598,6 +3779,8 @@ ProcessorBox::register_actions ()
        ActionManager::engine_sensitive_actions.push_back (act);
 
        myactions.register_action (processor_box_actions, X_("newaux"), _("New Aux Send ..."));
+       myactions.register_action (processor_box_actions, X_("newlisten"), _("New Monitor Send ..."));
+       myactions.register_action (processor_box_actions, X_("removelisten"), _("Remove Monitor Send ..."));
 
        myactions.register_action (processor_box_actions, X_("controls"), _("Controls"));
        myactions.register_action (processor_box_actions, X_("send_options"), _("Send Options"));
@@ -3647,6 +3830,12 @@ ProcessorBox::register_actions ()
                processor_box_actions, X_("manage-pins"), _("Pin Connections..."),
                sigc::ptr_fun (ProcessorBox::rb_manage_pins));
 
+       /* Disk IO stuff */
+       disk_io_action = myactions.register_action (processor_box_actions, X_("disk-io-menu"), _("Disk I/O ..."));
+       myactions.register_action (processor_box_actions, X_("disk-io-prefader"), _("Pre-Fader."), sigc::bind (sigc::ptr_fun (ProcessorBox::rb_set_disk_io_position), DiskIOPreFader));
+       myactions.register_action (processor_box_actions, X_("disk-io-postfader"), _("Post-Fader."), sigc::bind (sigc::ptr_fun (ProcessorBox::rb_set_disk_io_position), DiskIOPostFader));
+       myactions.register_action (processor_box_actions, X_("disk-io-custom"), _("Custom."), sigc::bind (sigc::ptr_fun (ProcessorBox::rb_set_disk_io_position), DiskIOCustom));
+
        /* show editors */
        edit_action = myactions.register_action (
                processor_box_actions, X_("edit"), _("Edit..."),
@@ -3679,6 +3868,16 @@ ProcessorBox::rb_ab_plugins ()
        _current_processor_box->ab_plugins ();
 }
 
+void
+ProcessorBox::rb_set_disk_io_position (DiskIOPoint diop)
+{
+       if (_current_processor_box == 0) {
+               return;
+       }
+
+       _current_processor_box->set_disk_io_position (diop);
+}
+
 void
 ProcessorBox::rb_manage_pins ()
 {
@@ -3725,6 +3924,16 @@ ProcessorBox::rb_choose_aux (boost::weak_ptr<Route> wr)
        _current_processor_box->choose_aux (wr);
 }
 
+void
+ProcessorBox::rb_remove_aux (boost::weak_ptr<Route> wr)
+{
+       if (_current_processor_box == 0) {
+               return;
+       }
+
+       _current_processor_box->remove_aux (wr);
+}
+
 void
 ProcessorBox::rb_clear ()
 {