X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Fprocessor_box.cc;h=361945d5e0f8de57f93317dbcb8b4750cf39e4ee;hb=2449fc9681ba62eda3f3a42904a3691fe022954d;hp=07781fe7eed439f917532fa6b53c1c4cacaeaba4;hpb=19bb2b33a89a1291451f0740739a36daebe00bae;p=ardour.git diff --git a/gtk2_ardour/processor_box.cc b/gtk2_ardour/processor_box.cc index 07781fe7ee..361945d5e0 100644 --- a/gtk2_ardour/processor_box.cc +++ b/gtk2_ardour/processor_box.cc @@ -21,6 +21,10 @@ #include "gtk2ardour-config.h" #endif +#ifdef COMPILER_MSVC +#define rintf(x) round((x) + 0.5) +#endif + #include #include #include @@ -38,13 +42,16 @@ #include #include #include +#include #include "ardour/amp.h" #include "ardour/audio_track.h" #include "ardour/audioengine.h" #include "ardour/internal_return.h" #include "ardour/internal_send.h" +#include "ardour/panner_shell.h" #include "ardour/plugin_insert.h" +#include "ardour/pannable.h" #include "ardour/port_insert.h" #include "ardour/profile.h" #include "ardour/return.h" @@ -69,7 +76,6 @@ #include "return_ui.h" #include "route_processor_selection.h" #include "send_ui.h" -#include "utils.h" #include "i18n.h" @@ -91,36 +97,61 @@ RefPtr ProcessorBox::rename_action; RefPtr ProcessorBox::edit_action; RefPtr ProcessorBox::edit_generic_action; +static const uint32_t audio_port_color = 0x4A8A0EFF; // Green +static const uint32_t midi_port_color = 0x960909FF; //Red + ProcessorEntry::ProcessorEntry (ProcessorBox* parent, boost::shared_ptr p, Width w) : _button (ArdourButton::led_default_elements) , _position (PreFader) + , _position_num(0) + , _selectable(true) , _parent (parent) , _processor (p) , _width (w) - , _visual_state (Gtk::STATE_NORMAL) + , _input_icon(true) + , _output_icon(false) { _vbox.show (); - _button.set_diameter (3); _button.set_distinct_led_click (true); + _button.set_fallthrough_to_parent(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 (_routing_icon); + _vbox.pack_start (_input_icon); _vbox.pack_start (_button, true, true); + _vbox.pack_end (_output_icon); _button.set_active (_processor->active()); + + _routing_icon.set_no_show_all(true); + _input_icon.set_no_show_all(true); + _button.show (); + _routing_icon.set_visible(false); + _input_icon.hide(); + _output_icon.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()); + _processor->ConfigurationChanged.connect (config_connection, invalidator (*this), boost::bind (&ProcessorEntry::processor_configuration_changed, this, _1, _2), gui_context()); set p = _processor->what_can_be_automated (); for (set::iterator i = p.begin(); i != p.end(); ++i) { - Control* c = new Control (_processor->automation_control (*i), _processor->describe_parameter (*i)); + std::string label = _processor->describe_parameter (*i); + + if (boost::dynamic_pointer_cast (_processor)) { + label = _("Send"); + } else if (boost::dynamic_pointer_cast (_processor)) { + label = _("Return"); + } + + Control* c = new Control (_processor->automation_control (*i), label); _controls.push_back (c); @@ -128,13 +159,14 @@ ProcessorEntry::ProcessorEntry (ProcessorBox* parent, boost::shared_ptrbox); } - - if (boost::dynamic_pointer_cast (_processor)) { - /* Don't label send faders */ - c->hide_label (); - } } + _input_icon.set_ports(_processor->input_streams()); + _output_icon.set_ports(_processor->output_streams()); + + _routing_icon.set_sources(_processor->input_streams()); + _routing_icon.set_sinks(_processor->output_streams()); + setup_tooltip (); setup_visuals (); } else { @@ -168,9 +200,17 @@ ProcessorEntry::drag_text () const } void -ProcessorEntry::set_position (Position p) +ProcessorEntry::set_position (Position p, uint32_t num) { _position = p; + _position_num = num; + + if (_position_num == 0 || _routing_icon.get_visible()) { + _input_icon.show(); + } else { + _input_icon.hide(); + } + setup_visuals (); } @@ -245,18 +285,35 @@ ProcessorEntry::processor_property_changed (const PropertyChange& what_changed) } } +void +ProcessorEntry::processor_configuration_changed (const ChanCount in, const ChanCount out) +{ + _input_icon.set_ports(in); + _output_icon.set_ports(out); + _routing_icon.set_sources(in); + _routing_icon.set_sinks(out); + _input_icon.queue_draw(); + _output_icon.queue_draw(); + _routing_icon.queue_draw(); +} + void ProcessorEntry::setup_tooltip () { if (_processor) { boost::shared_ptr pi = boost::dynamic_pointer_cast (_processor); if (pi) { + std::string postfix = ""; + uint32_t replicated; + if ((replicated = pi->get_count()) > 1) { + postfix = string_compose(_("\nThis mono plugin has been replicated %1 times."), replicated); + } if (pi->plugin()->has_editor()) { ARDOUR_UI::instance()->set_tip (_button, - string_compose (_("%1\nDouble-click to show GUI.\nAlt+double-click to show generic GUI."), name (Wide))); + string_compose (_("%1\nDouble-click to show GUI.\nAlt+double-click to show generic GUI.%2"), name (Wide), postfix)); } else { ARDOUR_UI::instance()->set_tip (_button, - string_compose (_("%1\nDouble-click to show generic GUI."), name (Wide))); + string_compose (_("%1\nDouble-click to show generic GUI.%2"), name (Wide), postfix)); } return; } @@ -295,6 +352,13 @@ ProcessorEntry::name (Width w) const } } else { + boost::shared_ptr pi; + uint32_t replicated; + if ((pi = boost::dynamic_pointer_cast (_processor)) != 0 + && (replicated = pi->get_count()) > 1) + { + name_display += string_compose(_("(%1x1) "), replicated); + } switch (w) { case Wide: @@ -397,23 +461,52 @@ ProcessorEntry::toggle_control_visibility (Control* c) _parent->update_gui_object_state (this); } +Menu * +ProcessorEntry::build_send_options_menu () +{ + using namespace Menu_Helpers; + Menu* menu = manage (new Menu); + MenuList& items = menu->items (); + + boost::shared_ptr send = boost::dynamic_pointer_cast (_processor); + if (send) { + + items.push_back (CheckMenuElem (_("Link panner controls"))); + Gtk::CheckMenuItem* c = dynamic_cast (&items.back ()); + c->set_active (send->panner_shell()->is_linked_to_route()); + c->signal_toggled().connect (sigc::mem_fun (*this, &ProcessorEntry::toggle_panner_link)); + + } + return menu; +} + +void +ProcessorEntry::toggle_panner_link () +{ + boost::shared_ptr send = boost::dynamic_pointer_cast (_processor); + if (send) { + send->panner_shell()->set_linked_to_route(!send->panner_shell()->is_linked_to_route()); + } +} + ProcessorEntry::Control::Control (boost::shared_ptr 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 (&_adjustment, 0, 13, false) + , _slider (&_adjustment, boost::shared_ptr(), 0, 13) , _slider_persistant_tooltip (&_slider) - , _button (ArdourButton::Element (ArdourButton::Text | ArdourButton::Indicator)) + , _button (ArdourButton::led_default_elements) , _ignore_ui_adjustment (false) , _visible (false) , _name (n) { _slider.set_controllable (c); + box.set_padding(0, 0, 4, 4); if (c->toggled()) { _button.set_text (_name); _button.set_led_left (true); _button.set_name ("processor control button"); - box.pack_start (_button); + box.add (_button); _button.show (); _button.signal_clicked.connect (sigc::mem_fun (*this, &Control::button_clicked)); @@ -422,20 +515,36 @@ ProcessorEntry::Control::Control (boost::shared_ptr c, string } else { - _slider.set_name ("PluginSlider"); + _slider.set_name ("ProcessorControlSlider"); _slider.set_text (_name); - box.pack_start (_slider); + box.add (_slider); _slider.show (); - double const lo = c->internal_to_interface (c->lower ()); - double const up = c->internal_to_interface (c->upper ()); - + const ARDOUR::ParameterDescriptor& desc = c->desc(); + double const lo = c->internal_to_interface(desc.lower); + double const up = c->internal_to_interface(desc.upper); + double const normal = c->internal_to_interface(desc.normal); + double smallstep = desc.smallstep; + double largestep = desc.largestep; + + if (smallstep == 0.0) { + smallstep = up / 1000.; + } else { + smallstep = c->internal_to_interface(desc.lower + smallstep); + } + + if (largestep == 0.0) { + largestep = up / 40.; + } else { + largestep = c->internal_to_interface(desc.lower + largestep); + } + _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.set_step_increment (smallstep); + _adjustment.set_page_increment (largestep); + _slider.set_default_value (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 ()); @@ -470,7 +579,6 @@ ProcessorEntry::Control::set_tooltip () string sm = Glib::Markup::escape_text (s.str()); - ARDOUR_UI::instance()->set_tip (_label, sm); _slider_persistant_tooltip.set_tip (sm); ARDOUR_UI::instance()->set_tip (_button, sm); } @@ -488,7 +596,7 @@ ProcessorEntry::Control::slider_adjusted () return; } - c->set_value (c->interface_to_internal (_adjustment.get_value ())); + c->set_value ( c->interface_to_internal(_adjustment.get_value ()) ); set_tooltip (); } @@ -505,6 +613,7 @@ ProcessorEntry::Control::button_clicked () c->set_value (n ? 0 : 1); _button.set_active (!n); + set_tooltip (); } void @@ -523,7 +632,7 @@ ProcessorEntry::Control::control_changed () } else { - _adjustment.set_value (c->internal_to_interface (c->get_value ())); + _adjustment.set_value (c->internal_to_interface(c->get_value ())); stringstream s; s.precision (1); @@ -578,12 +687,6 @@ ProcessorEntry::Control::hide_things () } } -void -ProcessorEntry::Control::hide_label () -{ - _label.hide (); -} - string ProcessorEntry::Control::state_id () const { @@ -593,35 +696,60 @@ ProcessorEntry::Control::state_id () const return string_compose (X_("control %1"), c->id().to_s ()); } -BlankProcessorEntry::BlankProcessorEntry (ProcessorBox* b, Width w) - : ProcessorEntry (b, boost::shared_ptr(), w) -{ -} - PluginInsertProcessorEntry::PluginInsertProcessorEntry (ProcessorBox* b, boost::shared_ptr p, Width w) : ProcessorEntry (b, p, w) , _plugin_insert (p) { - p->SplittingChanged.connect ( + p->PluginIoReConfigure.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 (); + _output_icon.set_ports(_plugin_insert->output_streams()); + _routing_icon.set_splitting(_plugin_insert->splitting ()); + + ChanCount sources = _plugin_insert->input_streams(); + ChanCount sinks = _plugin_insert->natural_input_streams(); + + /* replicated instances */ + if (!_plugin_insert->splitting () && _plugin_insert->get_count() > 1) { + for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) { + sinks.set(*t, sinks.get(*t) * _plugin_insert->get_count()); + } + } + /* MIDI bypass */ + if (_plugin_insert->natural_output_streams().n_midi() == 0 && + _plugin_insert->output_streams().n_midi() == 1) { + sinks.set(DataType::MIDI, 1); + sources.set(DataType::MIDI, 1); + } + + _input_icon.set_ports(sinks); + _routing_icon.set_sinks(sinks); + _routing_icon.set_sources(sources); + + if (_plugin_insert->splitting () || + _plugin_insert->input_streams().n_audio() < _plugin_insert->natural_input_streams().n_audio() + ) + { + _routing_icon.set_size_request (-1, 7); + _routing_icon.set_visible(true); + _input_icon.show(); } else { - _splitting_icon.hide (); + _routing_icon.set_visible(false); + if (_position_num != 0) { + _input_icon.hide(); + } } + + _input_icon.queue_draw(); + _output_icon.queue_draw(); + _routing_icon.queue_draw(); } void @@ -631,35 +759,73 @@ PluginInsertProcessorEntry::hide_things () plugin_insert_splitting_changed (); } -void -PluginInsertProcessorEntry::setup_visuals () + +bool +ProcessorEntry::PortIcon::on_expose_event (GdkEventExpose* ev) { - switch (_position) { - case PreFader: - _splitting_icon.set_name ("ProcessorPreFader"); - break; + cairo_t* cr = gdk_cairo_create (get_window()->gobj()); - case Fader: - _splitting_icon.set_name ("ProcessorFader"); - break; + cairo_rectangle (cr, ev->area.x, ev->area.y, ev->area.width, ev->area.height); + cairo_clip (cr); - case PostFader: - _splitting_icon.set_name ("ProcessorPostFader"); - break; + Gtk::Allocation a = get_allocation(); + double const width = a.get_width(); + double const height = a.get_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); + + if (_ports.n_total() > 1) { + for (uint32_t i = 0; i < _ports.n_total(); ++i) { + if (i < _ports.n_midi()) { + cairo_set_source_rgb (cr, + UINT_RGBA_R_FLT(midi_port_color), + UINT_RGBA_G_FLT(midi_port_color), + UINT_RGBA_B_FLT(midi_port_color)); + } else { + cairo_set_source_rgb (cr, + UINT_RGBA_R_FLT(audio_port_color), + UINT_RGBA_G_FLT(audio_port_color), + UINT_RGBA_B_FLT(audio_port_color)); + } + const float x = rintf(width * (.2f + .6f * i / (_ports.n_total() - 1.f))); + cairo_rectangle (cr, x-1, 0, 3, height); + cairo_fill(cr); + } + } else if (_ports.n_total() == 1) { + if (_ports.n_midi() == 1) { + cairo_set_source_rgb (cr, + UINT_RGBA_R_FLT(midi_port_color), + UINT_RGBA_G_FLT(midi_port_color), + UINT_RGBA_B_FLT(midi_port_color)); + } else { + cairo_set_source_rgb (cr, + UINT_RGBA_R_FLT(audio_port_color), + UINT_RGBA_G_FLT(audio_port_color), + UINT_RGBA_B_FLT(audio_port_color)); + } + const float x = rintf(width * .5); + cairo_rectangle (cr, x-1, 0, 3, height); + cairo_fill(cr); + cairo_stroke(cr); } - ProcessorEntry::setup_visuals (); + cairo_destroy(cr); + return true; } bool -PluginInsertProcessorEntry::SplittingIcon::on_expose_event (GdkEventExpose* ev) +ProcessorEntry::RoutingIcon::on_expose_event (GdkEventExpose* ev) { cairo_t* cr = gdk_cairo_create (get_window()->gobj()); cairo_rectangle (cr, ev->area.x, ev->area.y, ev->area.width, ev->area.height); cairo_clip (cr); - cairo_set_line_width (cr, 1.5); + cairo_set_line_width (cr, 1.0); cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND); Gtk::Allocation a = get_allocation(); @@ -675,21 +841,73 @@ PluginInsertProcessorEntry::SplittingIcon::on_expose_event (GdkEventExpose* ev) 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 ()); - const float si_l = rint(width * 0.3) + .5; - const float si_c = rint(width * 0.5) + .5; - const float si_r = rint(width * 0.7) + .5; - const float si_m = rint(height * 0.5) + .5; - - cairo_move_to (cr, si_l, height); - cairo_line_to (cr, si_l, si_m); - cairo_line_to (cr, si_r, si_m); - cairo_line_to (cr, si_r, height); - - cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT); - cairo_move_to (cr, si_c, si_m); - cairo_line_to (cr, si_c, 0); - cairo_stroke (cr); - + const uint32_t sources = _sources.n_total(); + const uint32_t sinks = _sinks.n_total(); + + /* MIDI */ + const uint32_t midi_sources = _sources.n_midi(); + const uint32_t midi_sinks = _sinks.n_midi(); + + cairo_set_source_rgb (cr, + UINT_RGBA_R_FLT(midi_port_color), + UINT_RGBA_G_FLT(midi_port_color), + UINT_RGBA_B_FLT(midi_port_color)); + if (midi_sources > 0 && midi_sinks > 0 && sinks > 1 && sources > 1) { + for (uint32_t i = 0 ; i < midi_sources; ++i) { + const float si_x = rintf(width * (.2f + .6f * i / (sinks - 1.f))) + .5f; + const float si_x0 = rintf(width * (.2f + .6f * i / (sources - 1.f))) + .5f; + cairo_move_to (cr, si_x, height); + cairo_curve_to (cr, si_x, 0, si_x0, height, si_x0, 0); + cairo_stroke (cr); + } + } else if (midi_sources == 1 && midi_sinks == 1 && sinks == 1 && sources == 1) { + const float si_x = rintf(width * .5f) + .5f; + cairo_move_to (cr, si_x, height); + cairo_line_to (cr, si_x, 0); + cairo_stroke (cr); + } else if (midi_sources == 1 && midi_sinks == 1) { + /* unusual cases -- removed synth, midi-track w/audio plugins */ + const float si_x = rintf(width * (sinks > 1 ? .2f : .5f)) + .5f; + const float si_x0 = rintf(width * (sources > 1 ? .2f : .5f)) + .5f; + cairo_move_to (cr, si_x, height); + cairo_curve_to (cr, si_x, 0, si_x0, height, si_x0, 0); + cairo_stroke (cr); + } + + /* AUDIO */ + const uint32_t audio_sources = _sources.n_audio(); + const uint32_t audio_sinks = _sinks.n_audio(); + cairo_set_source_rgb (cr, + UINT_RGBA_R_FLT(audio_port_color), + UINT_RGBA_G_FLT(audio_port_color), + UINT_RGBA_B_FLT(audio_port_color)); + + if (_splitting) { + assert(audio_sources < 2); + assert(audio_sinks > 1); + /* assume there is only ever one MIDI port */ + const float si_x0 = rintf(width * (midi_sources > 0 ? .8f : .5f)) + .5f; + for (uint32_t i = midi_sinks; i < sinks; ++i) { + const float si_x = rintf(width * (.2f + .6f * i / (sinks - 1.f))) + .5f; + cairo_move_to (cr, si_x, height); + cairo_curve_to (cr, si_x, 0, si_x0, height, si_x0, 0); + cairo_stroke (cr); + } + } else if (audio_sources > 1) { + for (uint32_t i = 0 ; i < audio_sources; ++i) { + const float si_x = rintf(width * (.2f + .6f * (i + midi_sinks) / (sinks - 1.f))) + .5f; + const float si_x0 = rintf(width * (.2f + .6f * (i + midi_sources) / (sources - 1.f))) + .5f; + cairo_move_to (cr, si_x, height); + cairo_curve_to (cr, si_x, 0, si_x0, height, si_x0, 0); + cairo_stroke (cr); + } + } else if (audio_sources == 1 && audio_sinks == 1) { + const float si_x = rintf(width * .5f) + .5f; + cairo_move_to (cr, si_x, height); + cairo_line_to (cr, si_x, 0); + cairo_stroke (cr); + } + cairo_destroy(cr); return true; } @@ -719,7 +937,7 @@ ProcessorBox::ProcessorBox (ARDOUR::Session* sess, boost::function* source, ProcessorEntry* posit `dropped on' processor */ list c = processor_display.children (); list::iterator i = c.begin (); - while (dynamic_cast (*i)) { - assert (i != c.end ()); - ++i; - } assert (i != c.end ()); p = (*i)->processor (); @@ -936,6 +1150,20 @@ ProcessorBox::show_processor_menu (int arg) } } + Gtk::MenuItem* send_menu_item = dynamic_cast(ActionManager::get_widget("/ProcessorMenu/send_options")); + if (send_menu_item) { + if (single_selection) { + Menu* m = single_selection->build_send_options_menu (); + if (m && !m->items().empty()) { + send_menu_item->set_submenu (*m); + send_menu_item->set_sensitive (true); + } else { + gtk_menu_item_set_submenu (send_menu_item->gobj(), 0); + send_menu_item->set_sensitive (false); + } + } + } + /* Sensitise actions as approprioate */ cut_action->set_sensitive (can_cut()); @@ -984,14 +1212,14 @@ ProcessorBox::leave_notify (GdkEventCrossing*) return false; } -void +bool ProcessorBox::processor_operation (ProcessorOperation op) { ProcSelection targets; get_selected_processors (targets); - if (targets.empty()) { +/* if (targets.empty()) { int x, y; processor_display.get_pointer (x, y); @@ -1002,12 +1230,20 @@ ProcessorBox::processor_operation (ProcessorOperation op) targets.push_back (pointer.first->processor ()); } } +*/ + if ( (op == ProcessorsDelete) && targets.empty() ) + return false; //nothing to delete. return false so the editor-mixer, because the user was probably intending to delete something in the editor + switch (op) { case ProcessorsSelectAll: processor_display.select_all (); break; + case ProcessorsSelectNone: + processor_display.select_none (); + break; + case ProcessorsCopy: copy_processors (targets); break; @@ -1045,24 +1281,17 @@ ProcessorBox::processor_operation (ProcessorOperation op) default: break; } + + return true; } ProcessorWindowProxy* ProcessorBox::find_window_proxy (boost::shared_ptr processor) const { - for (list::const_iterator i = _processor_window_info.begin(); i != _processor_window_info.end(); ++i) { - boost::shared_ptr p = (*i)->processor().lock(); - - if (p && p == processor) { - return (*i); - } - } - - return 0; + return processor->window_proxy(); } - bool ProcessorBox::processor_button_press_event (GdkEventButton *ev, ProcessorEntry* child) { @@ -1090,6 +1319,12 @@ ProcessorBox::processor_button_press_event (GdkEventButton *ev, ProcessorEntry* ret = true; + } else if (Keyboard::is_context_menu_event (ev)) { + + show_processor_menu (ev->time); + + ret = true; + } else if (processor && ev->button == 1 && selected) { // this is purely informational but necessary for route params UI @@ -1118,10 +1353,6 @@ ProcessorBox::processor_button_release_event (GdkEventButton *ev, ProcessorEntry sigc::mem_fun(*this, &ProcessorBox::idle_delete_processor), boost::weak_ptr(processor))); - } else if (Keyboard::is_context_menu_event (ev)) { - - show_processor_menu (ev->time); - } else if (processor && Keyboard::is_button2_event (ev) #ifndef GTKOSX && (Keyboard::no_modifier_keys_pressed (ev) && ((ev->state & Gdk::BUTTON2_MASK) == Gdk::BUTTON2_MASK)) @@ -1247,7 +1478,8 @@ ProcessorBox::choose_insert () void ProcessorBox::choose_send () { - boost::shared_ptr send (new Send (*_session, _route->pannable(), _route->mute_master())); + boost::shared_ptr sendpan(new Pannable (*_session)); + boost::shared_ptr send (new Send (*_session, sendpan, _route->mute_master())); /* make an educated guess at the initial number of outputs for the send */ ChanCount outs = (_session->master_out()) @@ -1385,35 +1617,8 @@ ProcessorBox::redisplay_processors () _route->foreach_processor (sigc::bind (sigc::mem_fun (*this, &ProcessorBox::help_count_visible_prefader_processors), &_visible_prefader_processors, &fader_seen)); - if (_visible_prefader_processors == 0) { // fader only - BlankProcessorEntry* bpe = new BlankProcessorEntry (this, _width); - processor_display.add_child (bpe); - } - _route->foreach_processor (sigc::mem_fun (*this, &ProcessorBox::add_processor_to_display)); - - for (list::iterator i = _processor_window_info.begin(); i != _processor_window_info.end(); ++i) { - (*i)->marked = false; - } - _route->foreach_processor (sigc::mem_fun (*this, &ProcessorBox::maybe_add_processor_to_ui_list)); - - /* trim dead wood from the processor window proxy list */ - - list::iterator i = _processor_window_info.begin(); - while (i != _processor_window_info.end()) { - list::iterator j = i; - ++j; - - if (!(*i)->marked) { - WM::Manager::instance().remove (*i); - delete *i; - _processor_window_info.erase (i); - } - - i = j; - } - setup_entry_positions (); } @@ -1427,24 +1632,14 @@ ProcessorBox::maybe_add_processor_to_ui_list (boost::weak_ptr w) if (!p) { return; } - - list::iterator i = _processor_window_info.begin (); - while (i != _processor_window_info.end()) { - - boost::shared_ptr t = (*i)->processor().lock (); - - if (p == t) { - /* this processor is already on the list; done */ - (*i)->marked = true; - return; - } - - ++i; + if (p->window_proxy()) { + return; } /* not on the list; add it */ string loc; +#if 0 // is this still needed? Why? if (_parent_strip) { if (_parent_strip->mixer_owned()) { loc = X_("M"); @@ -1454,6 +1649,9 @@ ProcessorBox::maybe_add_processor_to_ui_list (boost::weak_ptr w) } else { loc = X_("P"); } +#else + loc = X_("P"); +#endif ProcessorWindowProxy* wp = new ProcessorWindowProxy ( string_compose ("%1-%2-%3", loc, _route->id(), p->id()), @@ -1466,19 +1664,13 @@ ProcessorBox::maybe_add_processor_to_ui_list (boost::weak_ptr w) wp->set_state (*ui_xml); } - wp->marked = true; - - /* if the processor already has an existing UI, - note that so that we don't recreate it - */ - void* existing_ui = p->get_ui (); if (existing_ui) { wp->use_window (*(reinterpret_cast(existing_ui))); } - _processor_window_info.push_back (wp); + p->set_window_proxy (wp); WM::Manager::instance().register_window (wp); } @@ -1509,6 +1701,7 @@ ProcessorBox::add_processor_to_display (boost::weak_ptr p) } boost::shared_ptr plugin_insert = boost::dynamic_pointer_cast (processor); + ProcessorEntry* e = 0; if (plugin_insert) { e = new PluginInsertProcessorEntry (this, plugin_insert, _width); @@ -1516,6 +1709,13 @@ ProcessorBox::add_processor_to_display (boost::weak_ptr p) e = new ProcessorEntry (this, processor, _width); } + boost::shared_ptr send = boost::dynamic_pointer_cast (processor); + boost::shared_ptr ext = boost::dynamic_pointer_cast (processor); + + //faders and meters are not deletable, copy/paste-able, so they shouldn't be selectable + if (!send && !plugin_insert && !ext) + e->set_selectable(false); + /* Set up this entry's state from the GUIObjectState */ XMLNode* proc = entry_gui_object_state (e); if (proc) { @@ -1543,15 +1743,16 @@ ProcessorBox::setup_entry_positions () list children = processor_display.children (); bool pre_fader = true; + uint32_t num = 0; for (list::iterator i = children.begin(); i != children.end(); ++i) { if (boost::dynamic_pointer_cast((*i)->processor())) { pre_fader = false; - (*i)->set_position (ProcessorEntry::Fader); + (*i)->set_position (ProcessorEntry::Fader, num++); } else { if (pre_fader) { - (*i)->set_position (ProcessorEntry::PreFader); + (*i)->set_position (ProcessorEntry::PreFader, num++); } else { - (*i)->set_position (ProcessorEntry::PostFader); + (*i)->set_position (ProcessorEntry::PostFader, num++); } } } @@ -1873,9 +2074,10 @@ ProcessorBox::paste_processor_state (const XMLNodeList& nlist, boost::shared_ptr continue; } + boost::shared_ptr sendpan(new Pannable (*_session)); XMLNode n (**niter); - InternalSend* s = new InternalSend (*_session, _route->pannable(), _route->mute_master(), - boost::shared_ptr(), Delivery::Aux); + InternalSend* s = new InternalSend (*_session, sendpan, _route->mute_master(), + _route, boost::shared_ptr(), Delivery::Aux); IOProcessor::prepare_for_reset (n, s->name()); @@ -1888,8 +2090,10 @@ ProcessorBox::paste_processor_state (const XMLNodeList& nlist, boost::shared_ptr } else if (type->value() == "send") { + boost::shared_ptr sendpan(new Pannable (*_session)); XMLNode n (**niter); - Send* s = new Send (*_session, _route->pannable(), _route->mute_master()); + + Send* s = new Send (*_session, _route->pannable(), _route->mute_master()); IOProcessor::prepare_for_reset (n, s->name()); @@ -2077,6 +2281,18 @@ ProcessorBox::get_editor_window (boost::shared_ptr processor, bool us boost::shared_ptr port_insert; Window* gidget = 0; + /* This method may or may not return a Window, but if it does not it + * will modify the parent mixer strip appearance layout to allow + * "editing" the @param processor that was passed in. + * + * So for example, if the processor is an Amp (gain), the parent strip + * will be forced back into a model where the fader controls the main gain. + * If the processor is a send, then we map the send controls onto the + * strip. + * + * Plugins and others will return a window for control. + */ + if (boost::dynamic_pointer_cast(_route) != 0) { if (boost::dynamic_pointer_cast (_route)->freeze_state() == AudioTrack::Frozen) { @@ -2151,7 +2367,7 @@ ProcessorBox::get_editor_window (boost::shared_ptr processor, bool us } else if ((port_insert = boost::dynamic_pointer_cast (processor)) != 0) { if (!_session->engine().connected()) { - MessageDialog msg ( _("Not connected to JACK - no I/O changes are possible")); + MessageDialog msg ( _("Not connected to audio engine - no I/O changes are possible")); msg.run (); return 0; } @@ -2210,6 +2426,7 @@ ProcessorBox::register_actions () ActionManager::register_action (popup_act_grp, X_("newaux"), _("New Aux Send ...")); ActionManager::register_action (popup_act_grp, X_("controls"), _("Controls")); + ActionManager::register_action (popup_act_grp, X_("send_options"), _("Send Options")); ActionManager::register_action (popup_act_grp, X_("clear"), _("Clear (all)"), sigc::ptr_fun (ProcessorBox::rb_clear)); @@ -2554,7 +2771,13 @@ ProcessorBox::generate_processor_title (boost::shared_ptr pi) maker += " ..."; } - return string_compose(_("%1: %2 (by %3)"), _route->name(), pi->name(), maker); + SessionObject* owner = pi->owner(); + + if (owner) { + return string_compose(_("%1: %2 (by %3)"), owner->name(), pi->name(), maker); + } else { + return string_compose(_("%1 (by %2)"), pi->name(), maker); + } } /** @param p Processor. @@ -2563,16 +2786,10 @@ ProcessorBox::generate_processor_title (boost::shared_ptr pi) Window * ProcessorBox::get_processor_ui (boost::shared_ptr p) const { - list::const_iterator i = _processor_window_info.begin (); - while (i != _processor_window_info.end()) { - boost::shared_ptr t = (*i)->processor().lock (); - if (t && t == p) { - return (*i)->get (); - } - - ++i; + ProcessorWindowProxy* wp = p->window_proxy(); + if (wp) { + return wp->get (); } - return 0; } @@ -2583,24 +2800,9 @@ ProcessorBox::get_processor_ui (boost::shared_ptr p) const void ProcessorBox::set_processor_ui (boost::shared_ptr p, Gtk::Window* w) { - list::iterator i = _processor_window_info.begin (); - + assert (p->window_proxy()); p->set_ui (w); - - while (i != _processor_window_info.end()) { - boost::shared_ptr t = (*i)->processor().lock (); - if (t && t == p) { - (*i)->use_window (*w); - return; - } - - ++i; - } - - /* we shouldn't get here, because the ProcessorUIList should always contain - an entry for each processor. - */ - assert (false); + p->window_proxy()->use_window (*w); } void @@ -2667,15 +2869,45 @@ ProcessorBox::update_gui_object_state (ProcessorEntry* entry) entry->add_control_state (proc); } +bool +ProcessorBox::is_editor_mixer_strip() const +{ + return _parent_strip && !_parent_strip->mixer_owned(); +} + ProcessorWindowProxy::ProcessorWindowProxy (string const & name, ProcessorBox* box, boost::weak_ptr processor) : WM::ProxyBase (name, string()) - , marked (false) , _processor_box (box) , _processor (processor) , is_custom (false) , want_custom (false) { + boost::shared_ptr p = _processor.lock (); + if (!p) { + return; + } + p->DropReferences.connect (going_away_connection, MISSING_INVALIDATOR, boost::bind (&ProcessorWindowProxy::processor_going_away, this), gui_context()); +} + +ProcessorWindowProxy::~ProcessorWindowProxy() +{ + /* processor window proxies do not own the windows they create with + * ::get(), so set _window to null before the normal WindowProxy method + * deletes it. + */ + _window = 0; +} +void +ProcessorWindowProxy::processor_going_away () +{ + delete _window; + _window = 0; + WM::Manager::instance().remove (this); + /* should be no real reason to do this, since the object that would + send DropReferences is about to be deleted, but lets do it anyway. + */ + going_away_connection.disconnect(); } ARDOUR::SessionHandlePtr*