From 482873760c11f4a69a7bcf0e014be91800e97f57 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Sat, 21 May 2016 16:36:08 -0400 Subject: initial implementation of VCA time axis views --- gtk2_ardour/editor.cc | 20 ++++ gtk2_ardour/editor.h | 1 + gtk2_ardour/editor_routes.cc | 114 ++++++++++++++---- gtk2_ardour/editor_routes.h | 6 +- gtk2_ardour/route_ui.cc | 57 ++++++--- gtk2_ardour/vca_time_axis.cc | 273 +++++++++++++++++++++++++++++++++++++++++++ gtk2_ardour/vca_time_axis.h | 74 ++++++++++++ 7 files changed, 503 insertions(+), 42 deletions(-) create mode 100644 gtk2_ardour/vca_time_axis.cc create mode 100644 gtk2_ardour/vca_time_axis.h (limited to 'gtk2_ardour') diff --git a/gtk2_ardour/editor.cc b/gtk2_ardour/editor.cc index 6d54f5ca1a..5826ba0b7f 100644 --- a/gtk2_ardour/editor.cc +++ b/gtk2_ardour/editor.cc @@ -76,6 +76,7 @@ #include "ardour/session_playlists.h" #include "ardour/tempo.h" #include "ardour/utils.h" +#include "ardour/vca_manager.h" #include "canvas/debug.h" #include "canvas/text.h" @@ -132,6 +133,7 @@ #include "tooltips.h" #include "ui_config.h" #include "utils.h" +#include "vca_time_axis.h" #include "verbose_cursor.h" #include "i18n.h" @@ -1387,6 +1389,7 @@ Editor::set_session (Session *t) _session->StepEditStatusChange.connect (_session_connections, invalidator (*this), boost::bind (&Editor::step_edit_status_change, this, _1), gui_context()); _session->TransportStateChange.connect (_session_connections, invalidator (*this), boost::bind (&Editor::map_transport_state, this), gui_context()); _session->PositionChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::map_position_change, this, _1), gui_context()); + _session->vca_manager().VCAAdded.connect (_session_connections, invalidator (*this), boost::bind (&Editor::add_vcas, this, _1), gui_context()); _session->RouteAdded.connect (_session_connections, invalidator (*this), boost::bind (&Editor::add_routes, this, _1), gui_context()); _session->DirtyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::update_title, this), gui_context()); _session->tempo_map().PropertyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::tempo_map_changed, this, _1), gui_context()); @@ -5265,6 +5268,23 @@ Editor::resume_route_redisplay () } } +void +Editor::add_vcas (VCAList& vcas) +{ + VCATimeAxisView* vtv; + list new_views; + + for (VCAList::iterator v = vcas.begin(); v != vcas.end(); ++v) { + vtv = new VCATimeAxisView (*this, _session, *_track_canvas); + vtv->set_vca (*v); + new_views.push_back (vtv); + } + + if (new_views.size() > 0) { + _routes->vcas_added (new_views); + } +} + void Editor::add_routes (RouteList& routes) { diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h index 4151b10727..0e651b5dad 100644 --- a/gtk2_ardour/editor.h +++ b/gtk2_ardour/editor.h @@ -775,6 +775,7 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD void add_routes (ARDOUR::RouteList&); void timeaxisview_deleted (TimeAxisView *); + void add_vcas (ARDOUR::VCAList&); Gtk::HBox global_hpacker; Gtk::VBox global_vpacker; diff --git a/gtk2_ardour/editor_routes.cc b/gtk2_ardour/editor_routes.cc index dfc9e86877..ff7599a443 100644 --- a/gtk2_ardour/editor_routes.cc +++ b/gtk2_ardour/editor_routes.cc @@ -34,6 +34,8 @@ #include "ardour/session.h" #include "ardour/solo_isolate_control.h" #include "ardour/utils.h" +#include "ardour/vca.h" +#include "ardour/vca_manager.h" #include "gtkmm2ext/cell_renderer_pixbuf_multi.h" #include "gtkmm2ext/cell_renderer_pixbuf_toggle.h" @@ -52,6 +54,7 @@ #include "plugin_setup_dialog.h" #include "route_sorter.h" #include "tooltips.h" +#include "vca_time_axis.h" #include "utils.h" #include "i18n.h" @@ -685,6 +688,54 @@ EditorRoutes::active_changed (std::string const & path) } } +void +EditorRoutes::vcas_added (list vcas) +{ + PBD::Unwinder at (_adding_routes, true); + bool from_scratch = (_model->children().size() == 0); + Gtk::TreeModel::Children::iterator insert_iter = _model->children().end(); + + _display.set_model (Glib::RefPtr()); + + for (list::iterator x = vcas.begin(); x != vcas.end(); ++x) { + + boost::shared_ptr vca ((*x)->vca()); + + TreeModel::Row row = *(_model->insert (insert_iter)); + + row[_columns.text] = vca->name(); + row[_columns.visible] = (*x)->marked_for_display(); + + row[_columns.active] = true; + row[_columns.tv] = *x; + row[_columns.stripable] = vca; + row[_columns.is_track] = false; + row[_columns.is_input_active] = false; + row[_columns.is_midi] = false; + row[_columns.mute_state] = RouteUI::mute_active_state (_session, vca); + row[_columns.solo_state] = RouteUI::solo_active_state (vca); + row[_columns.solo_visible] = true; + row[_columns.solo_isolate_state] = RouteUI::solo_isolate_active_state (vca); + row[_columns.solo_safe_state] = RouteUI::solo_safe_active_state (vca); + row[_columns.name_editable] = true; + + boost::weak_ptr wv (vca); + + // vca->gui_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::handle_gui_changes, this, _1, _2), gui_context()); + vca->PropertyChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::route_property_changed, this, _1, wv), gui_context()); + + vca->mute_control()->Changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_mute_display, this), gui_context()); + vca->solo_control()->Changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_display, this), gui_context()); + } + + _display.set_model (_model); + + /* now update route order keys from the treeview/track display order */ + if (!from_scratch) { + sync_presentation_info_from_treeview (); + } +} + void EditorRoutes::routes_added (list routes) { @@ -707,16 +758,18 @@ EditorRoutes::routes_added (list routes) for (list::iterator x = routes.begin(); x != routes.end(); ++x) { + boost::shared_ptr route = (*x)->route (); boost::shared_ptr midi_trk = boost::dynamic_pointer_cast ((*x)->route()); TreeModel::Row row = *(_model->insert (insert_iter)); - row[_columns.text] = (*x)->route()->name(); + row[_columns.text] = route->name(); row[_columns.visible] = (*x)->marked_for_display(); - row[_columns.active] = (*x)->route()->active (); + + row[_columns.active] = route->active (); row[_columns.tv] = *x; - row[_columns.stripable] = (*x)->route (); - row[_columns.is_track] = (boost::dynamic_pointer_cast ((*x)->route()) != 0); + row[_columns.stripable] = route; + row[_columns.is_track] = (boost::dynamic_pointer_cast (route) != 0); if (midi_trk) { row[_columns.is_input_active] = midi_trk->input_active (); @@ -726,37 +779,36 @@ EditorRoutes::routes_added (list routes) row[_columns.is_midi] = false; } - row[_columns.mute_state] = (*x)->route()->muted() ? Gtkmm2ext::ExplicitActive : Gtkmm2ext::Off; - row[_columns.solo_state] = RouteUI::solo_active_state ((*x)->route()); + row[_columns.mute_state] = RouteUI::mute_active_state (_session, route); + row[_columns.solo_state] = RouteUI::solo_active_state (route); row[_columns.solo_visible] = !(*x)->route()->is_master (); - row[_columns.solo_isolate_state] = (*x)->route()->solo_isolate_control()->solo_isolated(); - row[_columns.solo_safe_state] = (*x)->route()->solo_safe_control()->solo_safe(); + row[_columns.solo_isolate_state] = RouteUI::solo_isolate_active_state (route); + row[_columns.solo_safe_state] = RouteUI::solo_safe_active_state (route); row[_columns.name_editable] = true; - boost::weak_ptr wr ((*x)->route()); + boost::weak_ptr wr (route); - (*x)->route()->gui_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::handle_gui_changes, this, _1, _2), gui_context()); - (*x)->route()->PropertyChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::route_property_changed, this, _1, wr), gui_context()); + route->gui_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::handle_gui_changes, this, _1, _2), gui_context()); + route->PropertyChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::route_property_changed, this, _1, wr), gui_context()); if ((*x)->is_track()) { - boost::shared_ptr t = boost::dynamic_pointer_cast ((*x)->route()); + boost::shared_ptr t = boost::dynamic_pointer_cast (route); t->rec_enable_control()->Changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context()); t->rec_safe_control()->Changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context()); } if ((*x)->is_midi_track()) { - boost::shared_ptr t = boost::dynamic_pointer_cast ((*x)->route()); + boost::shared_ptr t = boost::dynamic_pointer_cast (route); t->StepEditStatusChange.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context()); t->InputActiveChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_input_active_display, this), gui_context()); } - (*x)->route()->mute_control()->Changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_mute_display, this), gui_context()); - (*x)->route()->solo_control()->Changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_display, this), gui_context()); - (*x)->route()->solo_isolate_control()->Changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_isolate_display, this), gui_context()); - (*x)->route()->solo_safe_control()->Changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_safe_display, this), gui_context()); - - (*x)->route()->active_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_active_display, this), gui_context ()); + route->mute_control()->Changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_mute_display, this), gui_context()); + route->solo_control()->Changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_display, this), gui_context()); + route->solo_isolate_control()->Changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_isolate_display, this), gui_context()); + route->solo_safe_control()->Changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_solo_safe_display, this), gui_context()); + route->active_changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_active_display, this), gui_context ()); } update_rec_display (); @@ -816,7 +868,7 @@ EditorRoutes::route_removed (TimeAxisView *tv) } void -EditorRoutes::route_property_changed (const PropertyChange& what_changed, boost::weak_ptr r) +EditorRoutes::route_property_changed (const PropertyChange& what_changed, boost::weak_ptr s) { if (!what_changed.contains (ARDOUR::Properties::name)) { return; @@ -824,9 +876,9 @@ EditorRoutes::route_property_changed (const PropertyChange& what_changed, boost: ENSURE_GUI_THREAD (*this, &EditorRoutes::route_name_changed, r) - boost::shared_ptr route = r.lock (); + boost::shared_ptr stripable = s.lock (); - if (!route) { + if (!stripable) { return; } @@ -834,9 +886,9 @@ EditorRoutes::route_property_changed (const PropertyChange& what_changed, boost: TreeModel::Children::iterator i; for (i = rows.begin(); i != rows.end(); ++i) { - boost::shared_ptr t = (*i)[_columns.stripable]; - if (t == route) { - (*i)[_columns.text] = route->name(); + boost::shared_ptr ss = (*i)[_columns.stripable]; + if (ss == stripable) { + (*i)[_columns.text] = stripable->name(); break; } } @@ -1379,6 +1431,13 @@ struct PresentationInfoRouteSorter } }; +struct PresentationInfoVCASorter +{ + bool operator() (boost::shared_ptr a, boost::shared_ptr b) { + return a->presentation_info().global_order () < b->presentation_info().global_order (); + } +}; + void EditorRoutes::initial_display () { @@ -1393,6 +1452,11 @@ EditorRoutes::initial_display () r.sort (PresentationInfoRouteSorter ()); _editor->add_routes (r); + + VCAList v (_session->vca_manager().vcas()); + v.sort (PresentationInfoVCASorter ()); + + _editor->add_vcas (v); } void diff --git a/gtk2_ardour/editor_routes.h b/gtk2_ardour/editor_routes.h index 7065a2cd4b..56c94babf3 100644 --- a/gtk2_ardour/editor_routes.h +++ b/gtk2_ardour/editor_routes.h @@ -22,8 +22,11 @@ #include "pbd/signals.h" #include "gtkmm2ext/widget_state.h" + #include "editor_component.h" +class VCATimeAxisView; + class EditorRoutes : public EditorComponent, public PBD::ScopedConnectionList, public ARDOUR::SessionHandlePtr { public: @@ -56,6 +59,7 @@ public: void redisplay (); void update_visibility (); void routes_added (std::list routes); + void vcas_added (std::list routes); void route_removed (TimeAxisView *); void hide_track_in_display (TimeAxisView &); std::list views () const; @@ -82,7 +86,7 @@ private: void active_changed (std::string const &); void reordered (Gtk::TreeModel::Path const &, Gtk::TreeModel::iterator const &, int *); bool button_press (GdkEventButton *); - void route_property_changed (const PBD::PropertyChange&, boost::weak_ptr); + void route_property_changed (const PBD::PropertyChange&, boost::weak_ptr); void handle_gui_changes (std::string const &, void *); bool idle_update_mute_rec_solo_etc (); void update_rec_display (); diff --git a/gtk2_ardour/route_ui.cc b/gtk2_ardour/route_ui.cc index 0a1faeaa67..32218932b3 100644 --- a/gtk2_ardour/route_ui.cc +++ b/gtk2_ardour/route_ui.cc @@ -1116,13 +1116,20 @@ RouteUI::send_blink (bool onoff) Gtkmm2ext::ActiveState RouteUI::solo_active_state (boost::shared_ptr s) { - if (!s->solo_control()->can_solo()) { + boost::shared_ptr sc = s->solo_control(); + + if (!sc) { return Gtkmm2ext::Off; } - if (s->solo_control()->self_soloed()) { + if (!sc->can_solo()) { + return Gtkmm2ext::Off; + } + + + if (sc->self_soloed()) { return Gtkmm2ext::ExplicitActive; - } else if (s->solo_control()->soloed_by_others()) { + } else if (sc->soloed_by_others()) { return Gtkmm2ext::ImplicitActive; } else { return Gtkmm2ext::Off; @@ -1130,13 +1137,19 @@ RouteUI::solo_active_state (boost::shared_ptr s) } Gtkmm2ext::ActiveState -RouteUI::solo_isolate_active_state (boost::shared_ptr r) +RouteUI::solo_isolate_active_state (boost::shared_ptr s) { - if (r->is_master() || r->is_monitor()) { + boost::shared_ptr sc = s->solo_isolate_control(); + + if (!sc) { + return Gtkmm2ext::Off; + } + + if (s->is_master() || s->is_monitor()) { return Gtkmm2ext::Off; } - if (r->solo_isolate_control()->solo_isolated()) { + if (sc->solo_isolated()) { return Gtkmm2ext::ExplicitActive; } else { return Gtkmm2ext::Off; @@ -1144,13 +1157,19 @@ RouteUI::solo_isolate_active_state (boost::shared_ptr r) } Gtkmm2ext::ActiveState -RouteUI::solo_safe_active_state (boost::shared_ptr r) +RouteUI::solo_safe_active_state (boost::shared_ptr s) { - if (r->is_master() || r->is_monitor()) { + boost::shared_ptr sc = s->solo_safe_control(); + + if (!sc) { return Gtkmm2ext::Off; } - if (r->solo_safe_control()->solo_safe()) { + if (s->is_master() || s->is_monitor()) { + return Gtkmm2ext::Off; + } + + if (sc->solo_safe()) { return Gtkmm2ext::ExplicitActive; } else { return Gtkmm2ext::Off; @@ -1205,18 +1224,24 @@ RouteUI::solo_changed_so_update_mute () } ActiveState -RouteUI::mute_active_state (Session* s, boost::shared_ptr r) +RouteUI::mute_active_state (Session*, boost::shared_ptr s) { - if (r->is_monitor()) { - return ActiveState(0); + boost::shared_ptr mc = s->mute_control(); + + if (s->is_monitor()) { + return Gtkmm2ext::Off; + } + + if (!mc) { + return Gtkmm2ext::Off; } if (Config->get_show_solo_mutes() && !Config->get_solo_control_is_listen_control ()) { - if (r->mute_control()->muted_by_self ()) { + if (mc->muted_by_self ()) { /* full mute */ return Gtkmm2ext::ExplicitActive; - } else if (r->mute_control()->muted_by_others_soloing () || r->mute_control()->muted_by_masters ()) { + } else if (mc->muted_by_others_soloing () || mc->muted_by_masters ()) { /* this will reflect both solo mutes AND master mutes */ return Gtkmm2ext::ImplicitActive; } else { @@ -1226,10 +1251,10 @@ RouteUI::mute_active_state (Session* s, boost::shared_ptr r) } else { - if (r->mute_control()->muted_by_self()) { + if (mc->muted_by_self()) { /* full mute */ return Gtkmm2ext::ExplicitActive; - } else if (r->mute_control()->muted_by_masters ()) { + } else if (mc->muted_by_masters ()) { /* this shows only master mutes, not mute-by-others-soloing */ return Gtkmm2ext::ImplicitActive; } else { diff --git a/gtk2_ardour/vca_time_axis.cc b/gtk2_ardour/vca_time_axis.cc new file mode 100644 index 0000000000..8510401d39 --- /dev/null +++ b/gtk2_ardour/vca_time_axis.cc @@ -0,0 +1,273 @@ +/* + Copyright (C) 2016 Paul Davis + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "pbd/convert.h" + +#include "ardour/mute_control.h" +#include "ardour/profile.h" +#include "ardour/session.h" +#include "ardour/solo_control.h" +#include "ardour/vca.h" + +#include "gtkmm2ext/doi.h" + +#include "gui_thread.h" +#include "public_editor.h" +#include "tooltips.h" +#include "ui_config.h" +#include "vca_time_axis.h" + +#include "i18n.h" + +using namespace ARDOUR; +using namespace ARDOUR_UI_UTILS; +using namespace Gtkmm2ext; +using namespace PBD; + +VCATimeAxisView::VCATimeAxisView (PublicEditor& ed, Session* s, ArdourCanvas::Canvas& canvas) + : AxisView (s) + , TimeAxisView (s, ed, (TimeAxisView*) 0, canvas) + , gain_meter (s, true, 75, 14) // XXX stupid magic numbers, match sizes in RouteTimeAxisView +{ + solo_button.set_name ("solo button"); + set_tooltip (solo_button, _("Solo slaves")); + solo_button.signal_button_release_event().connect (sigc::mem_fun (*this, &VCATimeAxisView::solo_release), false); + + mute_button.set_name ("mute button"); + mute_button.set_text (_("M")); + set_tooltip (mute_button, _("Mute slaves")); + mute_button.signal_button_release_event().connect (sigc::mem_fun (*this, &VCATimeAxisView::mute_release), false); + + drop_button.set_name ("mute button"); + drop_button.set_text (_("D")); + set_tooltip (drop_button, _("Unassign all slaves")); + drop_button.signal_button_release_event().connect (sigc::mem_fun (*this, &VCATimeAxisView::drop_release), false); + + spill_button.set_name ("mute button"); + spill_button.set_text (_("V")); + set_tooltip (spill_button, _("Show only slaves")); + spill_button.signal_button_release_event().connect (sigc::mem_fun (*this, &VCATimeAxisView::spill_release), false); + + mute_button.set_tweaks(ArdourButton::TrackHeader); + solo_button.set_tweaks(ArdourButton::TrackHeader); + drop_button.set_tweaks(ArdourButton::TrackHeader); + spill_button.set_tweaks(ArdourButton::TrackHeader); + + controls_table.attach (mute_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0); + controls_table.attach (solo_button, 3, 4, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0); + controls_table.attach (drop_button, 2, 3, 1, 2, Gtk::SHRINK, Gtk::SHRINK, 0, 0); + controls_table.attach (spill_button, 3, 4, 1, 2, Gtk::SHRINK, Gtk::SHRINK, 0, 0); + controls_table.attach (gain_meter.get_gain_slider(), 0, 2, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0); + + mute_button.show (); + solo_button.show (); + drop_button.show (); + spill_button.show (); + gain_meter.get_gain_slider().show (); + + s->config.ParameterChanged.connect (*this, invalidator (*this), boost::bind (&VCATimeAxisView::parameter_changed, this, _1), gui_context()); + Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&VCATimeAxisView::parameter_changed, this, _1), gui_context()); + UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &VCATimeAxisView::parameter_changed)); +} + +VCATimeAxisView::~VCATimeAxisView () +{ +} + +void +VCATimeAxisView::self_delete () +{ + delete_when_idle (this); +} + +void +VCATimeAxisView::parameter_changed (std::string const & p) +{ + if (p == "track-name-number") { + update_track_number_visibility(); + } else if (p == "use-monitor-bus" || p == "solo-control-is-listen-control" || p == "listen-position") { + set_button_names (); + } +} + +bool +VCATimeAxisView::solo_release (GdkEventButton*) +{ + /* We use NoGroup because VCA controls are never part of a group. This + is redundant, but clear. + */ + _vca->solo_control()->set_value (_vca->solo_control()->self_soloed() ? 0.0 : 1.0, Controllable::NoGroup); + return true; +} + +bool +VCATimeAxisView::mute_release (GdkEventButton*) +{ + /* We use NoGroup because VCA controls are never part of a group. This + is redundant, but clear. + */ + _vca->mute_control()->set_value (_vca->mute_control()->muted_by_self() ? 0.0 : 1.0, Controllable::NoGroup); + return true; +} + +void +VCATimeAxisView::set_vca (boost::shared_ptr v) +{ + _vca = v; + + gain_meter.set_controls (boost::shared_ptr(), + boost::shared_ptr(), + boost::shared_ptr(), + _vca->gain_control()); + + // Mixer_UI::instance()->show_vca_change.connect (sigc::mem_fun (*this, &VCAMasterStrip::spill_change)); + + _vca->PropertyChanged.connect (vca_connections, invalidator (*this), boost::bind (&VCATimeAxisView::vca_property_changed, this, _1), gui_context()); + + _vca->solo_control()->Changed.connect (vca_connections, invalidator (*this), boost::bind (&VCATimeAxisView::update_solo_display, this), gui_context()); + _vca->mute_control()->Changed.connect (vca_connections, invalidator (*this), boost::bind (&VCATimeAxisView::update_mute_display, this), gui_context()); + _vca->DropReferences.connect (vca_connections, invalidator (*this), boost::bind (&VCATimeAxisView::self_delete, this), gui_context()); + + /* VCA number never changes */ + number_label.set_text (to_string (_vca->number(), std::dec)); + + update_vca_name (); + set_button_names (); + update_solo_display (); + update_mute_display (); + update_track_number_visibility (); +} + +void +VCATimeAxisView::vca_property_changed (PropertyChange const & what_changed) +{ + if (what_changed.contains (ARDOUR::Properties::name)) { + update_vca_name (); + } +} + +void +VCATimeAxisView::update_vca_name () +{ + name_label.set_text (_vca->name()); +} + +void +VCATimeAxisView::update_mute_display () +{ + if (_vca->mute_control()->muted_by_self()) { + mute_button.set_active_state (ExplicitActive); + } else if (_vca->mute_control()->muted_by_masters ()) { + mute_button.set_active_state (ImplicitActive); + } else { + mute_button.set_active_state (Gtkmm2ext::Off); + } +} + +void +VCATimeAxisView::update_solo_display () +{ + if (_vca->solo_control()->self_soloed()) { + solo_button.set_active_state (ExplicitActive); + } else if (_vca->solo_control()->soloed_by_masters ()) { + solo_button.set_active_state (ImplicitActive); + } else { + solo_button.set_active_state (Gtkmm2ext::Off); + } + + update_mute_display (); +} + +std::string +VCATimeAxisView::name() const +{ + return _vca->name(); +} + +std::string +VCATimeAxisView::state_id() const +{ + return string_compose ("vtv %1", _vca->id().to_s()); +} + +void +VCATimeAxisView::set_button_names () +{ + if (Config->get_solo_control_is_listen_control()) { + switch (Config->get_listen_position()) { + case AfterFaderListen: + solo_button.set_text (S_("AfterFader|A")); + set_tooltip (solo_button, _("After-fade listen (AFL)")); + break; + case PreFaderListen: + solo_button.set_text (S_("PreFader|P")); + set_tooltip (solo_button, _("Pre-fade listen (PFL)")); + break; + } + } else { + solo_button.set_text (S_("Solo|S")); + set_tooltip (solo_button, _("Solo")); + } +} + +void +VCATimeAxisView::update_track_number_visibility () +{ + DisplaySuspender ds; + bool show_label = _session->config.get_track_name_number(); + + if (number_label.get_parent()) { + controls_table.remove (number_label); + } + + if (show_label) { + if (ARDOUR::Profile->get_mixbus()) { + controls_table.attach (number_label, 3, 4, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0); + } else { + controls_table.attach (number_label, 0, 1, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0); + } + + // see ArdourButton::on_size_request(), we should probably use a global size-group here instead. + // except the width of the number label is subtracted from the name-hbox, so we + // need to explictly calculate it anyway until the name-label & entry become ArdourWidgets. + + int tnw = (2 + std::max(2u, _session->track_number_decimals())) * number_label.char_pixel_width(); + if (tnw & 1) --tnw; + number_label.set_size_request(tnw, -1); + number_label.show (); + name_hbox.set_size_request (TimeAxisView::name_width_px - 2 - tnw, -1); // -2 = cellspacing + } else { + number_label.hide (); + name_hbox.set_size_request (TimeAxisView::name_width_px, -1); + } +} + +bool +VCATimeAxisView::spill_release (GdkEventButton*) +{ + return true; +} + +bool +VCATimeAxisView::drop_release (GdkEventButton*) +{ + _vca->Drop (); /* EMIT SIGNAL */ + + return true; +} diff --git a/gtk2_ardour/vca_time_axis.h b/gtk2_ardour/vca_time_axis.h new file mode 100644 index 0000000000..42dd73f5b3 --- /dev/null +++ b/gtk2_ardour/vca_time_axis.h @@ -0,0 +1,74 @@ +/* + Copyright (C) 2016 Paul Davis + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __ardour_vca_time_axis_h__ +#define __ardour_vca_time_axis_h__ + +#include "ardour_button.h" +#include "time_axis_view.h" +#include "gain_meter.h" + +namespace ArdourCanvas { + class Canvas; +} + +namespace ARDOUR { + class Session; + class VCA; +} + +class VCATimeAxisView : public TimeAxisView +{ + public: + VCATimeAxisView (PublicEditor&, ARDOUR::Session*, ArdourCanvas::Canvas& canvas); + virtual ~VCATimeAxisView (); + + void set_vca (boost::shared_ptr); + boost::shared_ptr vca() const { return _vca; } + + std::string name() const; + std::string state_id() const; + + bool selectable() const { return false; } + + protected: + boost::shared_ptr _vca; + ArdourButton solo_button; + ArdourButton mute_button; + ArdourButton spill_button; + ArdourButton drop_button; + ArdourButton number_label; + GainMeterBase gain_meter; + PBD::ScopedConnectionList vca_connections; + + void parameter_changed (std::string const& p); + void vca_property_changed (PBD::PropertyChange const&); + void update_vca_name (); + void set_button_names (); + void update_solo_display (); + void update_mute_display (); + void update_track_number_visibility (); + bool solo_release (GdkEventButton*); + bool mute_release (GdkEventButton*); + bool spill_release (GdkEventButton*); + bool drop_release (GdkEventButton*); + void self_delete (); +}; + +#endif /* __ardour_route_time_axis_h__ */ -- cgit v1.2.3