+RefPtr<Action> ProcessorBox::cut_action;
+RefPtr<Action> ProcessorBox::rename_action;
+RefPtr<Action> ProcessorBox::edit_action;
+RefPtr<Action> ProcessorBox::edit_generic_action;
+Glib::RefPtr<Gdk::Pixbuf> ProcessorEntry::_slider_pixbuf;
+Glib::RefPtr<Gdk::Pixbuf> ProcessorEntry::_slider_pixbuf_desensitised;
+
+ProcessorEntry::ProcessorEntry (ProcessorBox* parent, boost::shared_ptr<Processor> p, Width w)
+ : _button (ArdourButton::led_default_elements)
+ , _position (PreFader)
+ , _parent (parent)
+ , _processor (p)
+ , _width (w)
+ , _visual_state (Gtk::STATE_NORMAL)
+{
+ _vbox.show ();
+
+ _button.set_diameter (3);
+ _button.set_distinct_led_click (true);
+ _button.set_led_left (true);
+ _button.signal_led_clicked.connect (sigc::mem_fun (*this, &ProcessorEntry::led_clicked));
+ _button.set_text (name (_width));
+
+ if (_processor) {
+
+ _vbox.pack_start (_button, true, true);
+
+ _button.set_active (_processor->active());
+ _button.show ();
+
+ _processor->ActiveChanged.connect (active_connection, invalidator (*this), boost::bind (&ProcessorEntry::processor_active_changed, this), gui_context());
+ _processor->PropertyChanged.connect (name_connection, invalidator (*this), boost::bind (&ProcessorEntry::processor_property_changed, this, _1), gui_context());
+
+ set<Evoral::Parameter> p = _processor->what_can_be_automated ();
+ for (set<Evoral::Parameter>::iterator i = p.begin(); i != p.end(); ++i) {
+
+ Control* c = new Control (
+ _slider_pixbuf,
+ _slider_pixbuf_desensitised,
+ _processor->automation_control (*i),
+ _processor->describe_parameter (*i)
+ );
+
+ _controls.push_back (c);
+
+ if (boost::dynamic_pointer_cast<Amp> (_processor) == 0) {
+ /* Add non-Amp controls to the processor box */
+ _vbox.pack_start (c->box);
+ }
+
+ if (boost::dynamic_pointer_cast<Send> (_processor)) {
+ /* Don't label send faders */
+ c->hide_label ();
+ }
+ }
+
+ setup_tooltip ();
+ setup_visuals ();
+ } else {
+ _vbox.set_size_request (-1, _button.size_request().height);
+ }
+}
+
+ProcessorEntry::~ProcessorEntry ()
+{
+ for (list<Control*>::iterator i = _controls.begin(); i != _controls.end(); ++i) {
+ delete *i;
+ }
+}
+
+EventBox&
+ProcessorEntry::action_widget ()
+{
+ return _button;
+}
+
+Gtk::Widget&
+ProcessorEntry::widget ()
+{
+ return _vbox;
+}
+
+string
+ProcessorEntry::drag_text () const
+{
+ return name (Wide);
+}
+
+void
+ProcessorEntry::set_position (Position p)
+{
+ _position = p;
+ setup_visuals ();
+}
+
+void
+ProcessorEntry::set_visual_state (Gtkmm2ext::VisualState s, bool yn)
+{
+ if (yn) {
+ _button.set_visual_state (Gtkmm2ext::VisualState (_button.visual_state() | s));
+ } else {
+ _button.set_visual_state (Gtkmm2ext::VisualState (_button.visual_state() & ~s));
+ }
+}
+
+void
+ProcessorEntry::setup_visuals ()
+{
+ switch (_position) {
+ case PreFader:
+ _button.set_name ("processor prefader");
+ break;
+
+ case Fader:
+ _button.set_name ("processor fader");
+ break;
+
+ case PostFader:
+ _button.set_name ("processor postfader");
+ break;
+ }
+}
+
+
+boost::shared_ptr<Processor>
+ProcessorEntry::processor () const
+{
+ return _processor;
+}
+
+void
+ProcessorEntry::set_enum_width (Width w)
+{
+ _width = w;
+}
+
+void
+ProcessorEntry::led_clicked()
+{
+ if (_processor) {
+ if (_button.get_active ()) {
+ _processor->deactivate ();
+ } else {
+ _processor->activate ();
+ }
+ }
+}
+
+void
+ProcessorEntry::processor_active_changed ()
+{
+ if (_processor) {
+ _button.set_active (_processor->active());
+ }
+}
+
+void
+ProcessorEntry::processor_property_changed (const PropertyChange& what_changed)
+{
+ if (what_changed.contains (ARDOUR::Properties::name)) {
+ _button.set_text (name (_width));
+ setup_tooltip ();
+ }
+}
+
+void
+ProcessorEntry::setup_tooltip ()
+{
+ ARDOUR_UI::instance()->set_tip (_button, name (Wide));
+}
+
+string
+ProcessorEntry::name (Width w) const
+{
+ boost::shared_ptr<Send> send;
+ string name_display;
+
+ if (!_processor) {
+ return string();
+ }
+
+ if ((send = boost::dynamic_pointer_cast<Send> (_processor)) != 0 &&
+ !boost::dynamic_pointer_cast<InternalSend>(_processor)) {
+
+ name_display += '>';
+
+ /* grab the send name out of its overall name */
+
+ string::size_type lbracket, rbracket;
+ lbracket = send->name().find ('[');
+ rbracket = send->name().find (']');
+
+ switch (w) {
+ case Wide:
+ name_display += send->name().substr (lbracket+1, lbracket-rbracket-1);
+ break;
+ case Narrow:
+ name_display += PBD::short_version (send->name().substr (lbracket+1, lbracket-rbracket-1), 4);
+ break;
+ }
+
+ } else {
+
+ switch (w) {
+ case Wide:
+ name_display += _processor->display_name();
+ break;
+ case Narrow:
+ name_display += PBD::short_version (_processor->display_name(), 5);
+ break;
+ }
+
+ }
+
+ return name_display;
+}
+
+void
+ProcessorEntry::setup_slider_pix ()
+{
+ _slider_pixbuf = ::get_icon ("fader_belt_h_thin");
+ assert (_slider_pixbuf);
+ _slider_pixbuf_desensitised = ::get_icon ("fader_belt_h_thin_desensitised");
+ assert (_slider_pixbuf_desensitised);
+}
+
+void
+ProcessorEntry::set_pixel_width (int p)
+{
+ for (list<Control*>::iterator i = _controls.begin(); i != _controls.end(); ++i) {
+ (*i)->set_pixel_width (p);
+ }
+}
+
+void
+ProcessorEntry::show_all_controls ()
+{
+ for (list<Control*>::iterator i = _controls.begin(); i != _controls.end(); ++i) {
+ (*i)->set_visible (true);
+ }
+
+ _parent->update_gui_object_state (this);
+}
+
+void
+ProcessorEntry::hide_all_controls ()
+{
+ for (list<Control*>::iterator i = _controls.begin(); i != _controls.end(); ++i) {
+ (*i)->set_visible (false);
+ }
+
+ _parent->update_gui_object_state (this);
+}
+
+void
+ProcessorEntry::add_control_state (XMLNode* node) const
+{
+ for (list<Control*>::const_iterator i = _controls.begin(); i != _controls.end(); ++i) {
+ (*i)->add_state (node);
+ }
+}
+
+void
+ProcessorEntry::set_control_state (XMLNode const * node)
+{
+ for (list<Control*>::const_iterator i = _controls.begin(); i != _controls.end(); ++i) {
+ (*i)->set_state (node);
+ }
+}
+
+string
+ProcessorEntry::state_id () const
+{
+ return string_compose ("processor %1", _processor->id().to_s());
+}
+
+void
+ProcessorEntry::hide_things ()
+{
+ for (list<Control*>::iterator i = _controls.begin(); i != _controls.end(); ++i) {
+ (*i)->hide_things ();
+ }
+}
+
+
+Menu *
+ProcessorEntry::build_controls_menu ()
+{
+ using namespace Menu_Helpers;
+ Menu* menu = manage (new Menu);
+ MenuList& items = menu->items ();
+
+ items.push_back (
+ MenuElem (_("Show All Controls"), sigc::mem_fun (*this, &ProcessorEntry::show_all_controls))
+ );
+
+ items.push_back (
+ MenuElem (_("Hide All Controls"), sigc::mem_fun (*this, &ProcessorEntry::hide_all_controls))
+ );
+
+ if (!_controls.empty ()) {
+ items.push_back (SeparatorElem ());
+ }
+
+ for (list<Control*>::iterator i = _controls.begin(); i != _controls.end(); ++i) {
+ items.push_back (CheckMenuElem ((*i)->name ()));
+ CheckMenuItem* c = dynamic_cast<CheckMenuItem*> (&items.back ());
+ c->set_active ((*i)->visible ());
+ c->signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &ProcessorEntry::toggle_control_visibility), *i));
+ }
+
+ return menu;
+}
+
+void
+ProcessorEntry::toggle_control_visibility (Control* c)
+{
+ c->set_visible (!c->visible ());
+ _parent->update_gui_object_state (this);
+}
+
+ProcessorEntry::Control::Control (Glib::RefPtr<Gdk::Pixbuf> s, Glib::RefPtr<Gdk::Pixbuf> sd, boost::shared_ptr<AutomationControl> c, string const & n)
+ : _control (c)
+ , _adjustment (gain_to_slider_position_with_max (1.0, Config->get_max_gain()), 0, 1, 0.01, 0.1)
+ , _slider (s, sd, &_adjustment, 0, false)
+ , _slider_persistant_tooltip (&_slider)
+ , _button (ArdourButton::Element (ArdourButton::Text | ArdourButton::Indicator))
+ , _ignore_ui_adjustment (false)
+ , _visible (false)
+ , _name (n)
+{
+ _slider.set_controllable (c);
+
+ if (c->toggled()) {
+ _button.set_text (_name);
+ _button.set_led_left (true);
+ _button.set_name ("processor control button");
+ box.pack_start (_button);
+ _button.show ();
+
+ _button.signal_clicked.connect (sigc::mem_fun (*this, &Control::button_clicked));
+ _button.signal_led_clicked.connect (sigc::mem_fun (*this, &Control::button_clicked));
+ c->Changed.connect (_connection, MISSING_INVALIDATOR, boost::bind (&Control::control_changed, this), gui_context ());
+
+ } else {
+
+ box.pack_start (_label);
+ _label.show ();
+ _label.set_text (_name);
+ box.pack_start (_slider);
+ _slider.show ();
+
+ double const lo = c->internal_to_interface (c->lower ());
+ double const up = c->internal_to_interface (c->upper ());
+
+ _adjustment.set_lower (lo);
+ _adjustment.set_upper (up);
+ _adjustment.set_step_increment ((up - lo) / 100);
+ _adjustment.set_page_increment ((up - lo) / 10);
+ _slider.set_default_value (c->internal_to_interface (c->normal ()));
+
+ _adjustment.signal_value_changed().connect (sigc::mem_fun (*this, &Control::slider_adjusted));
+ c->Changed.connect (_connection, MISSING_INVALIDATOR, boost::bind (&Control::control_changed, this), gui_context ());
+ }
+
+ ARDOUR_UI::RapidScreenUpdate.connect (sigc::mem_fun (*this, &Control::control_changed));
+
+ control_changed ();
+ set_tooltip ();
+
+ /* We're providing our own PersistentTooltip */
+ set_no_tooltip_whatsoever (_slider);
+}
+
+void
+ProcessorEntry::Control::set_tooltip ()
+{
+ boost::shared_ptr<AutomationControl> c = _control.lock ();
+
+ if (!c) {
+ return;
+ }
+
+ stringstream s;
+ s << _name << ": ";
+ if (c->toggled ()) {
+ s << (c->get_value() > 0.5 ? _("on") : _("off"));
+ } else {
+ s << setprecision(2) << fixed;
+ s << c->internal_to_user (c->get_value ());
+ }
+
+ ARDOUR_UI::instance()->set_tip (_label, s.str ());
+ _slider_persistant_tooltip.set_tip (s.str ());
+ ARDOUR_UI::instance()->set_tip (_button, s.str ());
+}
+
+void
+ProcessorEntry::Control::set_pixel_width (int p)
+{
+ _slider.set_fader_length (p);
+}
+
+void
+ProcessorEntry::Control::slider_adjusted ()
+{
+ if (_ignore_ui_adjustment) {
+ return;
+ }
+
+ boost::shared_ptr<AutomationControl> c = _control.lock ();
+
+ if (!c) {
+ return;
+ }
+
+ c->set_value (c->interface_to_internal (_adjustment.get_value ()));
+ set_tooltip ();
+}
+
+void
+ProcessorEntry::Control::button_clicked ()
+{
+ boost::shared_ptr<AutomationControl> c = _control.lock ();
+
+ if (!c) {
+ return;
+ }
+
+ bool const n = _button.get_active ();
+
+ c->set_value (n ? 0 : 1);
+ _button.set_active (!n);
+}
+
+void
+ProcessorEntry::Control::control_changed ()
+{
+ boost::shared_ptr<AutomationControl> c = _control.lock ();
+ if (!c) {
+ return;
+ }
+
+ _ignore_ui_adjustment = true;
+
+ if (c->toggled ()) {
+
+ _button.set_active (c->get_value() > 0.5);
+
+ } else {
+
+ _adjustment.set_value (c->internal_to_interface (c->get_value ()));
+
+ stringstream s;
+ s.precision (1);
+ s.setf (ios::fixed, ios::floatfield);
+ s << c->internal_to_user (c->get_value ());
+ }
+
+ _ignore_ui_adjustment = false;
+}
+
+void
+ProcessorEntry::Control::add_state (XMLNode* node) const
+{
+ XMLNode* c = new XMLNode (X_("Object"));
+ c->add_property (X_("id"), state_id ());
+ c->add_property (X_("visible"), _visible);
+ node->add_child_nocopy (*c);
+}
+
+void
+ProcessorEntry::Control::set_state (XMLNode const * node)
+{
+ XMLNode* n = GUIObjectState::get_node (node, state_id ());
+ if (n) {
+ XMLProperty* p = n->property (X_("visible"));
+ set_visible (p && string_is_affirmative (p->value ()));
+ } else {
+ set_visible (false);
+ }
+}
+
+void
+ProcessorEntry::Control::set_visible (bool v)
+{
+ if (v) {
+ box.show ();
+ } else {
+ box.hide ();
+ }
+
+ _visible = v;
+}
+
+/** Called when the Editor might have re-shown things that
+ we want hidden.
+*/
+void
+ProcessorEntry::Control::hide_things ()
+{
+ if (!_visible) {
+ box.hide ();
+ }
+}
+
+void
+ProcessorEntry::Control::hide_label ()
+{
+ _label.hide ();
+}
+
+string
+ProcessorEntry::Control::state_id () const
+{
+ boost::shared_ptr<AutomationControl> c = _control.lock ();
+ assert (c);
+
+ return string_compose (X_("control %1"), c->id().to_s ());
+}
+
+BlankProcessorEntry::BlankProcessorEntry (ProcessorBox* b, Width w)
+ : ProcessorEntry (b, boost::shared_ptr<Processor>(), w)
+{
+}
+
+PluginInsertProcessorEntry::PluginInsertProcessorEntry (ProcessorBox* b, boost::shared_ptr<ARDOUR::PluginInsert> p, Width w)
+ : ProcessorEntry (b, p, w)
+ , _plugin_insert (p)
+{
+ p->SplittingChanged.connect (
+ _splitting_connection, invalidator (*this), boost::bind (&PluginInsertProcessorEntry::plugin_insert_splitting_changed, this), gui_context()
+ );
+
+ _splitting_icon.set_size_request (-1, 12);
+
+ _vbox.pack_start (_splitting_icon);
+ _vbox.reorder_child (_splitting_icon, 0);
+
+ plugin_insert_splitting_changed ();
+}
+
+void
+PluginInsertProcessorEntry::plugin_insert_splitting_changed ()
+{
+ if (_plugin_insert->splitting ()) {
+ _splitting_icon.show ();
+ } else {
+ _splitting_icon.hide ();
+ }
+}
+
+void
+PluginInsertProcessorEntry::hide_things ()
+{
+ ProcessorEntry::hide_things ();
+ plugin_insert_splitting_changed ();
+}
+
+void
+PluginInsertProcessorEntry::setup_visuals ()
+{
+ switch (_position) {
+ case PreFader:
+ _splitting_icon.set_name ("ProcessorPreFader");
+ break;
+
+ case Fader:
+ _splitting_icon.set_name ("ProcessorFader");
+ break;
+
+ case PostFader:
+ _splitting_icon.set_name ("ProcessorPostFader");
+ break;
+ }
+
+ ProcessorEntry::setup_visuals ();
+}
+
+bool
+PluginInsertProcessorEntry::SplittingIcon::on_expose_event (GdkEventExpose* ev)
+{
+ cairo_t* cr = gdk_cairo_create (get_window()->gobj());
+
+ cairo_set_line_width (cr, 1);
+
+ double const width = ev->area.width;
+ double const height = ev->area.height;
+
+ Gdk::Color const bg = get_style()->get_bg (STATE_NORMAL);
+ cairo_set_source_rgb (cr, bg.get_red_p (), bg.get_green_p (), bg.get_blue_p ());
+
+ cairo_rectangle (cr, 0, 0, width, height);
+ cairo_fill (cr);
+
+ Gdk::Color const fg = get_style()->get_fg (STATE_NORMAL);
+ cairo_set_source_rgb (cr, fg.get_red_p (), fg.get_green_p (), fg.get_blue_p ());
+
+ cairo_move_to (cr, width * 0.3, height);
+ cairo_line_to (cr, width * 0.3, height * 0.5);
+ cairo_line_to (cr, width * 0.7, height * 0.5);
+ cairo_line_to (cr, width * 0.7, height);
+ cairo_move_to (cr, width * 0.5, height * 0.5);
+ cairo_line_to (cr, width * 0.5, 0);
+ cairo_stroke (cr);
+
+ return true;
+}
+
+ProcessorBox::ProcessorBox (ARDOUR::Session* sess, boost::function<PluginSelector*()> get_plugin_selector,
+ RouteProcessorSelection& rsel, MixerStrip* parent, bool owner_is_mixer)
+ : _parent_strip (parent)