X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Froute_time_axis.cc;h=17c0874641731dc211770d2e5d216c08186f7a86;hb=0938a42440cc82ce8d0cb064840c258c863714ab;hp=9093abca1b12e57200b05a6ad73bc88288363ea7;hpb=a57b9cdd99b47fde8bfa265c0f72a380bb5cbb6f;p=ardour.git diff --git a/gtk2_ardour/route_time_axis.cc b/gtk2_ardour/route_time_axis.cc index 9093abca1b..17c0874641 100644 --- a/gtk2_ardour/route_time_axis.cc +++ b/gtk2_ardour/route_time_axis.cc @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include @@ -39,7 +39,6 @@ #include #include #include -#include #include #include @@ -54,6 +53,7 @@ #include "ardour/playlist.h" #include "ardour/processor.h" #include "ardour/profile.h" +#include "ardour/region_factory.h" #include "ardour/route_group.h" #include "ardour/session.h" #include "ardour/session_playlist.h" @@ -62,6 +62,8 @@ #include "evoral/Parameter.hpp" #include "ardour_ui.h" +#include "debug.h" +#include "global_signals.h" #include "route_time_axis.h" #include "automation_time_axis.h" #include "canvas_impl.h" @@ -102,32 +104,48 @@ RouteTimeAxisView::setup_slider_pix () } } -RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, boost::shared_ptr rt, Canvas& canvas) +RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, Canvas& canvas) : AxisView(sess) - , RouteUI(rt, sess) + , RouteUI(sess) , TimeAxisView(sess,ed,(TimeAxisView*) 0, canvas) + , _view (0) , parent_canvas (canvas) , button_table (3, 3) , route_group_button (_("g")) , playlist_button (_("p")) , automation_button (_("a")) + , automation_action_menu (0) + , plugins_submenu_item (0) + , route_group_menu (0) + , playlist_action_menu (0) + , mode_menu (0) + , color_mode_menu (0) , gm (sess, slider, true, 115) - , _ignore_track_mode_change (false) { +} + +void +RouteTimeAxisView::set_route (boost::shared_ptr rt) +{ + RouteUI::set_route (rt); + gm.set_controls (_route, _route->shared_peak_meter(), _route->amp()); gm.get_level_meter().set_no_show_all(); gm.get_level_meter().setup_meters(50); - _has_state = true; - playlist_menu = 0; - playlist_action_menu = 0; - automation_action_menu = 0; - plugins_submenu_item = 0; - mode_menu = 0; - _view = 0; + string str = gui_property ("height"); + if (!str.empty()) { + set_height (atoi (str)); + } else { + set_height (preset_height (HeightNormal)); + } if (!_route->is_hidden()) { - _marked_for_display = true; + if (gui_property ("visible").empty()) { + set_gui_property ("visible", true); + } + } else { + set_gui_property ("visible", false); } mute_changed (0); @@ -135,9 +153,6 @@ RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, boost::sh timestretch_rect = 0; no_redraw = false; - destructive_track_mode_item = 0; - normal_track_mode_item = 0; - non_layered_track_mode_item = 0; ignore_toggle = false; @@ -149,7 +164,7 @@ RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, boost::sh playlist_button.unset_flags (Gtk::CAN_FOCUS); automation_button.unset_flags (Gtk::CAN_FOCUS); - route_group_button.signal_button_release_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::edit_click), false); + route_group_button.signal_button_release_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false); playlist_button.signal_clicked().connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click)); automation_button.signal_clicked().connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click)); @@ -171,11 +186,16 @@ RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, boost::sh rec_enable_button->show_all (); controls_table.attach (*rec_enable_button, 5, 6, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0); - ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record")); + + if (is_midi_track()) { + ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record (Right-click for Step Edit)")); + } else { + ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record")); + } rec_enable_button->set_sensitive (_session->writable()); } - + controls_hbox.pack_start(gm.get_level_meter(), false, false); _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context()); _route->input()->changed.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context()); @@ -211,29 +231,37 @@ RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, boost::sh if (is_track()) { - track()->TrackModeChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::track_mode_changed, this), gui_context()); + str = gui_property ("layer-display"); + if (!str.empty()) { + set_layer_display (LayerDisplay (string_2_enum (str, _view->layer_display ()))); + } + track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context()); track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context()); /* pick up the correct freeze state */ map_frozen (); - } _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_unit)); + _editor.HorizontalPositionChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::horizontal_position_changed)); ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler)); PropertyList* plist = new PropertyList(); - + plist->add (ARDOUR::Properties::edit, true); plist->add (ARDOUR::Properties::mute, true); plist->add (ARDOUR::Properties::solo, true); - + route_group_menu = new RouteGroupMenu (_session, plist); - route_group_menu->GroupSelected.connect (sigc::mem_fun (*this, &RouteTimeAxisView::set_route_group_from_menu)); gm.get_gain_slider().signal_scroll_event().connect(sigc::mem_fun(*this, &RouteTimeAxisView::controls_ebox_scroll), false); gm.get_gain_slider().set_name ("TrackGainFader"); + + gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false); + + show_name_entry (); + hide_name_label (); } RouteTimeAxisView::~RouteTimeAxisView () @@ -244,9 +272,6 @@ RouteTimeAxisView::~RouteTimeAxisView () delete *i; } - delete playlist_menu; - playlist_menu = 0; - delete playlist_action_menu; playlist_action_menu = 0; @@ -264,15 +289,24 @@ RouteTimeAxisView::post_construct () /* map current state of the route */ update_diskstream_display (); + setup_processor_menu_and_curves (); + reset_processor_automation_curves (); +} +/** Set up the processor menu for the current set of processors, and + * display automation curves for any parameters which have data. + */ +void +RouteTimeAxisView::setup_processor_menu_and_curves () +{ + _subplugin_menu_map.clear (); subplugin_menu.items().clear (); _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu)); _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves)); - reset_processor_automation_curves (); } gint -RouteTimeAxisView::edit_click (GdkEventButton *ev) +RouteTimeAxisView::route_group_click (GdkEventButton *ev) { if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) { if (_route->route_group()) { @@ -281,22 +315,13 @@ RouteTimeAxisView::edit_click (GdkEventButton *ev) return false; } - route_group_menu->rebuild (_route->route_group ()); - route_group_menu->popup (ev->button, ev->time); + WeakRouteList r; + r.push_back (route ()); - return false; -} + route_group_menu->build (r); + route_group_menu->menu()->popup (ev->button, ev->time); -void -RouteTimeAxisView::set_route_group_from_menu (RouteGroup *eg) -{ - if (eg) { - eg->add (_route); - } else { - if (_route->route_group()) { - _route->route_group()->remove (_route); - } - } + return false; } void @@ -314,6 +339,10 @@ RouteTimeAxisView::label_view () name_entry.set_text (x); } + if (x != name_label.get_text()) { + name_label.set_text (x); + } + ARDOUR_UI::instance()->set_tip (name_entry, x); } @@ -327,7 +356,6 @@ RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed) void RouteTimeAxisView::take_name_changed (void *src) - { if (src != this) { label_view (); @@ -337,15 +365,7 @@ RouteTimeAxisView::take_name_changed (void *src) void RouteTimeAxisView::playlist_click () { - // always build a new action menu - - delete playlist_action_menu; - - playlist_action_menu = new Menu; - playlist_action_menu->set_name ("ArdourContextMenu"); - - build_playlist_menu (playlist_action_menu); - + build_playlist_menu (); conditionally_add_to_selection (); playlist_action_menu->popup (1, gtk_get_current_event_time()); } @@ -354,65 +374,45 @@ void RouteTimeAxisView::automation_click () { conditionally_add_to_selection (); - build_automation_action_menu (); + build_automation_action_menu (false); automation_action_menu->popup (1, gtk_get_current_event_time()); } -int -RouteTimeAxisView::set_state (const XMLNode& node, int version) -{ - TimeAxisView::set_state (node, version); - - XMLNodeList kids = node.children(); - XMLNodeConstIterator iter; - const XMLProperty* prop; - - if (_view && (prop = node.property ("layer-display"))) { - set_layer_display (LayerDisplay (string_2_enum (prop->value(), _view->layer_display ()))); - } - - for (iter = kids.begin(); iter != kids.end(); ++iter) { - if ((*iter)->name() == AutomationTimeAxisView::state_node_name) { - if ((prop = (*iter)->property ("automation-id")) != 0) { - - Evoral::Parameter param = ARDOUR::EventTypeMap::instance().new_parameter(prop->value()); - bool show = ((prop = (*iter)->property ("shown")) != 0) && string_is_affirmative (prop->value()); - create_automation_child(param, show); - } else { - warning << "Automation child has no ID" << endmsg; - } - } - } - - return 0; -} - void -RouteTimeAxisView::build_automation_action_menu () +RouteTimeAxisView::build_automation_action_menu (bool for_selection) { using namespace Menu_Helpers; + /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu, + otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu) + */ + + detach_menu (subplugin_menu); + + _main_automation_menu_map.clear (); delete automation_action_menu; automation_action_menu = new Menu; MenuList& items = automation_action_menu->items(); automation_action_menu->set_name ("ArdourContextMenu"); - + items.push_back (MenuElem (_("Show All Automation"), - sigc::mem_fun(*this, &RouteTimeAxisView::show_all_automation))); - + sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection))); + items.push_back (MenuElem (_("Show Existing Automation"), - sigc::mem_fun(*this, &RouteTimeAxisView::show_existing_automation))); - + sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection))); + items.push_back (MenuElem (_("Hide All Automation"), - sigc::mem_fun(*this, &RouteTimeAxisView::hide_all_automation))); - - /* attach the plugin submenu. It may have previously been used elsewhere, so we detach it first. */ + sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection))); - detach_menu (subplugin_menu); - items.push_back (MenuElem (_("Plugins"), subplugin_menu)); - items.back().set_sensitive (!subplugin_menu.items().empty()); + items.push_back (SeparatorElem ()); + + /* Attach the plugin submenu. It may have previously been used elsewhere, + so it was detached above */ + + items.push_back (MenuElem (_("Plugins"), subplugin_menu)); + items.back().set_sensitive (!subplugin_menu.items().empty() && (!for_selection || _editor.get_selection().tracks.size() == 1));; } void @@ -429,97 +429,230 @@ RouteTimeAxisView::build_display_menu () MenuList& items = display_menu->items(); display_menu->set_name ("ArdourContextMenu"); - items.push_back (MenuElem (_("Color"), sigc::mem_fun(*this, &RouteTimeAxisView::select_track_color))); + items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color))); + + if (_size_menu) { + detach_menu (*_size_menu); + } + build_size_menu (); + items.push_back (MenuElem (_("Height"), *_size_menu)); items.push_back (SeparatorElem()); if (!Profile->get_sae()) { items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog))); - /* rebuild this every time */ - build_automation_action_menu (); - detach_menu (*automation_action_menu); - items.push_back (MenuElem (_("Automation"), *automation_action_menu)); + items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1); items.push_back (SeparatorElem()); } // Hook for derived classes to add type specific stuff append_extra_display_menu_items (); - items.push_back (SeparatorElem()); if (is_track()) { - Menu *layers_menu = manage(new Menu); + Menu* layers_menu = manage (new Menu); MenuList &layers_items = layers_menu->items(); layers_menu->set_name("ArdourContextMenu"); RadioMenuItem::Group layers_group; - layers_items.push_back(RadioMenuElem (layers_group, _("Overlaid"), - sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid))); - layers_items.push_back(RadioMenuElem (layers_group, _("Stacked"), - sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked))); + /* Find out how many overlaid/stacked tracks we have in the selection */ - items.push_back (MenuElem (_("Layers"), *layers_menu)); + int overlaid = 0; + int stacked = 0; + TrackSelection const & s = _editor.get_selection().tracks; + for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) { + StreamView* v = (*i)->view (); + if (!v) { + continue; + } - Menu* alignment_menu = manage (new Menu); - MenuList& alignment_items = alignment_menu->items(); - alignment_menu->set_name ("ArdourContextMenu"); + switch (v->layer_display ()) { + case Overlaid: + ++overlaid; + break; + case Stacked: + ++stacked; + break; + } + } - RadioMenuItem::Group align_group; + /* We're not connecting to signal_toggled() here; in the case where these two items are + set to be in the `inconsistent' state, it seems that one or other will end up active + as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you + select the active one, no toggled signal is emitted so nothing happens. + */ - alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material"), - sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_style), ExistingMaterial))); - align_existing_item = dynamic_cast(&alignment_items.back()); - if (track()->alignment_style() == ExistingMaterial) { - align_existing_item->set_active(); - } + layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid"))); + RadioMenuItem* i = dynamic_cast (&layers_items.back ()); + i->set_active (overlaid != 0 && stacked == 0); + i->set_inconsistent (overlaid != 0 && stacked != 0); + i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true)); - alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time"), - sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_style), CaptureTime))); - align_capture_item = dynamic_cast(&alignment_items.back()); - if (track()->alignment_style() == CaptureTime) { - align_capture_item->set_active(); - } + layers_items.push_back ( + RadioMenuElem (layers_group, _("Stacked"), + sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true)) + ); + + i = dynamic_cast (&layers_items.back ()); + i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true)); + i->set_active (overlaid == 0 && stacked != 0); + i->set_inconsistent (overlaid != 0 && stacked != 0); + + items.push_back (MenuElem (_("Layers"), *layers_menu)); if (!Profile->get_sae()) { - items.push_back (MenuElem (_("Alignment"), *alignment_menu)); - track()->AlignmentStyleChanged.connect (route_connections, invalidator (*this), boost::bind (&RouteTimeAxisView::align_style_changed, this), gui_context()); + Menu* alignment_menu = manage (new Menu); + MenuList& alignment_items = alignment_menu->items(); + alignment_menu->set_name ("ArdourContextMenu"); - RadioMenuItem::Group mode_group; - items.push_back (RadioMenuElem (mode_group, _("Normal Mode"), sigc::bind ( - sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), - ARDOUR::Normal))); - normal_track_mode_item = dynamic_cast(&items.back()); + RadioMenuItem::Group align_group; - items.push_back (RadioMenuElem (mode_group, _("Tape Mode"), sigc::bind ( - sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), - ARDOUR::Destructive))); - destructive_track_mode_item = dynamic_cast(&items.back()); + /* Same verbose hacks as for the layering options above */ - items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode"), - sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered))); - non_layered_track_mode_item = dynamic_cast(&items.back()); + int existing = 0; + int capture = 0; + int automatic = 0; + int styles = 0; + boost::shared_ptr first_track; + TrackSelection const & s = _editor.get_selection().tracks; + for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) { + RouteTimeAxisView* r = dynamic_cast (*i); + if (!r || !r->is_track ()) { + continue; + } - _ignore_track_mode_change = true; - - switch (track()->mode()) { - case ARDOUR::Destructive: - destructive_track_mode_item->set_active (); - break; - case ARDOUR::Normal: - normal_track_mode_item->set_active (); - break; - case ARDOUR::NonLayered: - non_layered_track_mode_item->set_active (); - break; + if (!first_track) { + first_track = r->track(); + } + + switch (r->track()->alignment_choice()) { + case Automatic: + ++automatic; + styles |= 0x1; + switch (r->track()->alignment_style()) { + case ExistingMaterial: + ++existing; + break; + case CaptureTime: + ++capture; + break; + } + break; + case UseExistingMaterial: + ++existing; + styles |= 0x2; + break; + case UseCaptureTime: + ++capture; + styles |= 0x4; + break; + } } - - _ignore_track_mode_change = false; - } - track()->AlignmentStyleChanged.connect (route_connections, invalidator (*this), boost::bind (&RouteTimeAxisView::align_style_changed, this), gui_context()); + bool inconsistent; + switch (styles) { + case 1: + case 2: + case 4: + inconsistent = false; + break; + default: + inconsistent = true; + break; + } + + RadioMenuItem* i; + + if (!inconsistent && first_track) { + + alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)"))); + i = dynamic_cast (&alignment_items.back()); + i->set_active (automatic != 0 && existing == 0 && capture == 0); + i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true)); + + switch (first_track->alignment_choice()) { + case Automatic: + switch (first_track->alignment_style()) { + case ExistingMaterial: + alignment_items.push_back (MenuElem (_("(Currently: Existing Material)"))); + break; + case CaptureTime: + alignment_items.push_back (MenuElem (_("(Currently: Capture Time)"))); + break; + } + break; + default: + break; + } + + alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material"))); + i = dynamic_cast (&alignment_items.back()); + i->set_active (existing != 0 && capture == 0 && automatic == 0); + i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true)); + + alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time"))); + i = dynamic_cast (&alignment_items.back()); + i->set_active (existing == 0 && capture != 0 && automatic == 0); + i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true)); + + items.push_back (MenuElem (_("Alignment"), *alignment_menu)); + + } else { + /* show nothing */ + } + + Menu* mode_menu = manage (new Menu); + MenuList& mode_items = mode_menu->items (); + mode_menu->set_name ("ArdourContextMenu"); + + RadioMenuItem::Group mode_group; + + int normal = 0; + int tape = 0; + int non_layered = 0; + + for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) { + RouteTimeAxisView* r = dynamic_cast (*i); + if (!r || !r->is_track ()) { + continue; + } + + switch (r->track()->mode()) { + case Normal: + ++normal; + break; + case Destructive: + ++tape; + break; + case NonLayered: + ++non_layered; + break; + } + } + + mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode"))); + i = dynamic_cast (&mode_items.back ()); + i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true)); + i->set_active (normal != 0 && tape == 0 && non_layered == 0); + i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0)); + + mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode"))); + i = dynamic_cast (&mode_items.back ()); + i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true)); + i->set_active (normal == 0 && tape != 0 && non_layered == 0); + i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0)); + + mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode"))); + i = dynamic_cast (&mode_items.back ()); + i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true)); + i->set_active (normal == 0 && tape == 0 && non_layered != 0); + i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0)); + + items.push_back (MenuElem (_("Mode"), *mode_menu)); + } color_mode_menu = build_color_mode_menu(); if (color_mode_menu) { @@ -527,128 +660,111 @@ RouteTimeAxisView::build_display_menu () } items.push_back (SeparatorElem()); - } - items.push_back (CheckMenuElem (_("Active"), sigc::mem_fun(*this, &RouteUI::toggle_route_active))); - route_active_menu_item = dynamic_cast (&items.back()); - route_active_menu_item->set_active (_route->active()); + build_playlist_menu (); + items.push_back (MenuElem (_("Playlist"), *playlist_action_menu)); + items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1); - items.push_back (SeparatorElem()); - items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, false))); - if (!Profile->get_sae()) { - items.push_back (MenuElem (_("Remove"), sigc::mem_fun(*this, &RouteUI::remove_this_route))); - } else { - items.push_front (SeparatorElem()); - items.push_front (MenuElem (_("Delete"), sigc::mem_fun(*this, &RouteUI::remove_this_route))); + route_group_menu->detach (); + + WeakRouteList r; + for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) { + RouteTimeAxisView* rtv = dynamic_cast (*i); + if (rtv) { + r.push_back (rtv->route ()); + } + } + + if (r.empty ()) { + r.push_back (route ()); + } + + route_group_menu->build (r); + items.push_back (MenuElem (_("Route Group"), *route_group_menu->menu ())); + + build_automation_action_menu (true); + items.push_back (MenuElem (_("Automation"), *automation_action_menu)); + + items.push_back (SeparatorElem()); } -} -static bool __reset_item (RadioMenuItem* item, RadioMenuItem* item_2) -{ - item->set_active (); - item_2->set_active (); - return false; -} + int active = 0; + int inactive = 0; + TrackSelection const & s = _editor.get_selection().tracks; + for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) { + RouteTimeAxisView* r = dynamic_cast (*i); + if (!r) { + continue; + } -void -RouteTimeAxisView::set_track_mode (TrackMode mode) -{ - if (_ignore_track_mode_change) { - return; + if (r->route()->active()) { + ++active; + } else { + ++inactive; + } } - - RadioMenuItem* item; - RadioMenuItem* other_item; - RadioMenuItem* other_item_2; - - switch (mode) { - case ARDOUR::Normal: - item = normal_track_mode_item; - other_item = non_layered_track_mode_item; - other_item_2 = destructive_track_mode_item; - break; - case ARDOUR::NonLayered: - item = non_layered_track_mode_item; - other_item = normal_track_mode_item; - other_item_2 = destructive_track_mode_item; - break; - case ARDOUR::Destructive: - item = destructive_track_mode_item; - other_item = normal_track_mode_item; - other_item_2 = non_layered_track_mode_item; - break; - default: - fatal << string_compose (_("programming error: %1 %2"), "illegal track mode in RouteTimeAxisView::set_track_mode", mode) << endmsg; - /*NOTREACHED*/ - return; + + items.push_back (CheckMenuElem (_("Active"))); + CheckMenuItem* i = dynamic_cast (&items.back()); + bool click_sets_active = true; + if (active > 0 && inactive == 0) { + i->set_active (true); + click_sets_active = false; + } else if (active > 0 && inactive > 0) { + i->set_inconsistent (true); } + i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true)); - if (item && other_item && other_item_2 && track()->mode() != mode) { - _set_track_mode (track().get(), mode, other_item, other_item_2); + items.push_back (SeparatorElem()); + items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true))); + if (!Profile->get_sae()) { + items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true))); + } else { + items.push_front (SeparatorElem()); + items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true))); } } void -RouteTimeAxisView::_set_track_mode (Track* track, TrackMode mode, RadioMenuItem* reset_item, RadioMenuItem* reset_item_2) +RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection) { - bool needs_bounce; - - if (!track->can_use_mode (mode, needs_bounce)) { + if (apply_to_selection) { + _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false)); + } else { - if (!needs_bounce) { - /* cannot be done */ - Glib::signal_idle().connect (sigc::bind (sigc::ptr_fun (__reset_item), reset_item, reset_item_2)); - return; - } else { - cerr << "would bounce this one\n"; - /* XXX: radio menu item becomes inconsistent with track state in this case */ - return; - } - } + bool needs_bounce; - track->set_mode (mode); + if (!track()->can_use_mode (mode, needs_bounce)) { - rec_enable_button->remove (); + if (!needs_bounce) { + /* cannot be done */ + return; + } else { + cerr << "would bounce this one\n"; + return; + } + } - switch (mode) { - case ARDOUR::NonLayered: - case ARDOUR::Normal: - rec_enable_button->add (*(manage (new Image (::get_icon (X_("record_normal_red")))))); - break; - case ARDOUR::Destructive: - rec_enable_button->add (*(manage (new Image (::get_icon (X_("record_tape_red")))))); - break; - } + track()->set_mode (mode); - rec_enable_button->show_all (); -} + rec_enable_button->remove (); -void -RouteTimeAxisView::track_mode_changed () -{ - RadioMenuItem* item; + switch (mode) { + case ARDOUR::NonLayered: + case ARDOUR::Normal: + rec_enable_button->add (*(manage (new Image (::get_icon (X_("record_normal_red")))))); + break; + case ARDOUR::Destructive: + rec_enable_button->add (*(manage (new Image (::get_icon (X_("record_tape_red")))))); + break; + } - switch (track()->mode()) { - case ARDOUR::Normal: - item = normal_track_mode_item; - break; - case ARDOUR::NonLayered: - item = non_layered_track_mode_item; - break; - case ARDOUR::Destructive: - item = destructive_track_mode_item; - break; - default: - fatal << string_compose (_("programming error: %1 %2"), "illegal track mode in RouteTimeAxisView::set_track_mode", track()->mode()) << endmsg; - /*NOTREACHED*/ - return; + rec_enable_button->show_all (); } - - item->set_active (); } void -RouteTimeAxisView::show_timestretch (nframes_t start, nframes_t end) +RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end) { double x1; double x2; @@ -742,20 +858,13 @@ RouteTimeAxisView::set_height (uint32_t h) TimeAxisView::set_height (h); - ensure_xml_node (); - if (_view) { _view->set_height ((double) current_height()); } - char buf[32]; - snprintf (buf, sizeof (buf), "%u", height); - xml_node->add_property ("height", buf); - if (height >= preset_height (HeightNormal)) { + reset_meter(); - show_name_entry (); - hide_name_label (); gm.get_gain_slider().show(); mute_button->show(); @@ -774,11 +883,9 @@ RouteTimeAxisView::set_height (uint32_t h) playlist_button.show(); } - } else if (height >= preset_height (HeightSmaller)) { + } else { reset_meter(); - show_name_entry (); - hide_name_label (); gm.get_gain_slider().hide(); mute_button->show(); @@ -797,49 +904,19 @@ RouteTimeAxisView::set_height (uint32_t h) playlist_button.hide (); } - } else { - - - /* don't allow name_entry to be hidden while - it has focus, otherwise the GUI becomes unusable. - */ - - if (name_entry.has_focus()) { - if (name_entry.get_text() != _route->name()) { - name_entry_changed (); - } - controls_ebox.grab_focus (); - } - - hide_name_entry (); - show_name_label (); - - gm.get_gain_slider().hide(); - mute_button->hide(); - solo_button->hide(); - if (rec_enable_button) - rec_enable_button->hide(); - - route_group_button.hide (); - automation_button.hide (); - playlist_button.hide (); - name_label.set_text (_route->name()); } if (height_changed && !no_redraw) { /* only emit the signal if the height really changed */ - _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */ + request_redraw (); } } void -RouteTimeAxisView::select_track_color () +RouteTimeAxisView::route_color_changed () { - if (RouteUI::choose_color ()) { - - if (_view) { - _view->apply_color (_color, StreamView::RegionColor); - } + if (_view) { + _view->apply_color (color(), StreamView::RegionColor); } } @@ -849,6 +926,14 @@ RouteTimeAxisView::reset_samples_per_unit () set_samples_per_unit (_editor.get_current_zoom()); } +void +RouteTimeAxisView::horizontal_position_changed () +{ + if (_view) { + _view->horizontal_position_changed (); + } +} + void RouteTimeAxisView::set_samples_per_unit (double spu) { @@ -866,42 +951,21 @@ RouteTimeAxisView::set_samples_per_unit (double spu) } void -RouteTimeAxisView::align_style_changed () -{ - switch (track()->alignment_style()) { - case ExistingMaterial: - if (!align_existing_item->get_active()) { - align_existing_item->set_active(); - } - break; - case CaptureTime: - if (!align_capture_item->get_active()) { - align_capture_item->set_active(); - } - break; - } -} - -void -RouteTimeAxisView::set_align_style (AlignStyle style) +RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection) { - RadioMenuItem* item; - - switch (style) { - case ExistingMaterial: - item = align_existing_item; - break; - case CaptureTime: - item = align_capture_item; - break; - default: - fatal << string_compose (_("programming error: %1 %2"), "illegal align style in RouteTimeAxisView::set_align_style", style) << endmsg; - /*NOTREACHED*/ - return; - } + if (!mitem->get_active()) { + /* this is one of the two calls made when these radio menu items change status. this one + is for the item that became inactive, and we want to ignore it. + */ + return; + } - if (item->get_active()) { - track()->set_align_style (style); + if (apply_to_selection) { + _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false)); + } else { + if (track ()) { + track()->set_align_choice (choice); + } } } @@ -992,7 +1056,7 @@ RouteTimeAxisView::use_copy_playlist (bool prompt, vectorname(); - if (route_group() && route_group()->is_active()) { + if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::edit.property_id)) { name = resolve_new_group_playlist_name(name, playlists_before_op); } @@ -1047,7 +1111,7 @@ RouteTimeAxisView::use_new_playlist (bool prompt, vectorname(); - if (route_group() && route_group()->is_active()) { + if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::edit.property_id)) { name = resolve_new_group_playlist_name(name,playlists_before_op); } @@ -1168,7 +1232,7 @@ RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions) * @param results List to add things to. */ void -RouteTimeAxisView::get_selectables (nframes_t start, nframes_t end, double top, double bot, list& results) +RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list& results) { double speed = 1.0; @@ -1176,8 +1240,8 @@ RouteTimeAxisView::get_selectables (nframes_t start, nframes_t end, double top, speed = track()->speed(); } - nframes_t start_adjusted = session_frame_to_track_frame(start, speed); - nframes_t end_adjusted = session_frame_to_track_frame(end, speed); + framepos_t const start_adjusted = session_frame_to_track_frame(start, speed); + framepos_t const end_adjusted = session_frame_to_track_frame(end, speed); if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) { _view->get_selectables (start_adjusted, end_adjusted, top, bot, results); @@ -1235,34 +1299,32 @@ RouteTimeAxisView::playlist () const void RouteTimeAxisView::name_entry_changed () { - string x; - - x = name_entry.get_text (); + string x = name_entry.get_text (); if (x == _route->name()) { return; } - strip_whitespace_edges(x); + strip_whitespace_edges (x); if (x.length() == 0) { name_entry.set_text (_route->name()); return; } - if (!_session->route_name_unique (x)) { - ARDOUR_UI::instance()->popup_error (_("A track already exists with that name")); - name_entry.set_text (_route->name()); - } else if (_session->route_name_internal (x)) { - ARDOUR_UI::instance()->popup_error (_("You cannot create a track with that name as it is reserved for Ardour")); - name_entry.set_text (_route->name()); - } else { + if (_session->route_name_internal (x)) { + ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"), + PROGRAM_NAME)); + name_entry.grab_focus (); + } else if (RouteUI::verify_new_route_name (x)) { _route->set_name (x); + } else { + name_entry.grab_focus (); } } boost::shared_ptr -RouteTimeAxisView::find_next_region (nframes_t pos, RegionPoint point, int32_t dir) +RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir) { boost::shared_ptr pl = playlist (); @@ -1273,8 +1335,8 @@ RouteTimeAxisView::find_next_region (nframes_t pos, RegionPoint point, int32_t d return boost::shared_ptr (); } -nframes64_t -RouteTimeAxisView::find_next_region_boundary (nframes64_t pos, int32_t dir) +framepos_t +RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir) { boost::shared_ptr pl = playlist (); @@ -1285,17 +1347,16 @@ RouteTimeAxisView::find_next_region_boundary (nframes64_t pos, int32_t dir) return -1; } -bool +void RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op) { boost::shared_ptr what_we_got; boost::shared_ptr tr = track (); boost::shared_ptr playlist; - bool ret = false; if (tr == 0) { /* route is a bus, not a track */ - return false; + return; } playlist = tr->playlist(); @@ -1309,23 +1370,28 @@ RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op) } } - playlist->clear_history (); - playlist->clear_owned_history (); + playlist->clear_changes (); + playlist->clear_owned_changes (); switch (op) { + case Delete: + if (playlist->cut (time) != 0) { + vector cmds; + playlist->rdiff (cmds); + _session->add_commands (cmds); + + _session->add_command (new StatefulDiffCommand (playlist)); + } + break; + case Cut: if ((what_we_got = playlist->cut (time)) != 0) { _editor.get_cut_buffer().add (what_we_got); - - vector cmds; - + vector cmds; playlist->rdiff (cmds); - - for (vector::iterator c = cmds.begin(); c != cmds.end(); ++c) { - _session->add_command (*c); - } + _session->add_commands (cmds); + _session->add_command (new StatefulDiffCommand (playlist)); - ret = true; } break; case Copy: @@ -1336,25 +1402,19 @@ RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op) case Clear: if ((what_we_got = playlist->cut (time)) != 0) { - vector cmds; - + + vector cmds; playlist->rdiff (cmds); - - for (vector::iterator c = cmds.begin(); c != cmds.end(); ++c) { - _session->add_command (*c); - } + _session->add_commands (cmds); _session->add_command (new StatefulDiffCommand (playlist)); what_we_got->release (); - ret = true; } break; } - - return ret; } bool -RouteTimeAxisView::paste (nframes_t pos, float times, Selection& selection, size_t nth) +RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth) { if (!is_track()) { return false; @@ -1369,11 +1429,14 @@ RouteTimeAxisView::paste (nframes_t pos, float times, Selection& selection, size return false; } + DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos)); + if (track()->speed() != 1.0f) { pos = session_frame_to_track_frame (pos, track()->speed()); + DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos)); } - pl->clear_history (); + pl->clear_changes (); pl->paste (*p, pos, times); _session->add_command (new StatefulDiffCommand (pl)); @@ -1381,20 +1444,6 @@ RouteTimeAxisView::paste (nframes_t pos, float times, Selection& selection, size } -TimeAxisView::Children -RouteTimeAxisView::get_child_list() -{ - TimeAxisView::Children redirect_children; - - for (Children::iterator i = children.begin(); i != children.end(); ++i) { - if (!(*i)->hidden()) { - redirect_children.push_back(*i); - } - } - return redirect_children; -} - - struct PlaylistSorter { bool operator() (boost::shared_ptr a, boost::shared_ptr b) const { return a->sort_id() < b->sort_id(); @@ -1402,55 +1451,48 @@ struct PlaylistSorter { }; void -RouteTimeAxisView::build_playlist_menu (Gtk::Menu * menu) +RouteTimeAxisView::build_playlist_menu () { using namespace Menu_Helpers; - if (!menu || !is_track()) { + if (!is_track()) { return; } - MenuList& playlist_items = menu->items(); - menu->set_name ("ArdourContextMenu"); - playlist_items.clear(); - - delete playlist_menu; + delete playlist_action_menu; + playlist_action_menu = new Menu; + playlist_action_menu->set_name ("ArdourContextMenu"); + MenuList& playlist_items = playlist_action_menu->items(); + playlist_action_menu->set_name ("ArdourContextMenu"); + playlist_items.clear(); - vector > playlists, playlists_tr; - boost::shared_ptr tr = track(); RadioMenuItem::Group playlist_group; + boost::shared_ptr tr = track (); - _session->playlists->get (playlists); - - /* find the playlists for this diskstream */ - for (vector >::iterator i = playlists.begin(); i != playlists.end(); ++i) { - if (((*i)->get_orig_diskstream_id() == tr->diskstream_id()) || (tr->playlist()->id() == (*i)->id())) { - playlists_tr.push_back(*i); - } - } + vector > playlists_tr = _session->playlists->playlists_for_track (tr); /* sort the playlists */ PlaylistSorter cmp; sort (playlists_tr.begin(), playlists_tr.end(), cmp); - + /* add the playlists to the menu */ for (vector >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) { playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name())); RadioMenuItem *item = static_cast(&playlist_items.back()); item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr (*i))); - + if (tr->playlist()->id() == (*i)->id()) { item->set_active(); - + } } - + playlist_items.push_back (SeparatorElem()); playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist))); playlist_items.push_back (SeparatorElem()); - if (!route_group() || !route_group()->is_active()) { + if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::edit.property_id)) { playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this))); playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this))); @@ -1465,7 +1507,7 @@ RouteTimeAxisView::build_playlist_menu (Gtk::Menu * menu) playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this))); playlist_items.push_back (SeparatorElem()); - playlist_items.push_back (MenuElem(_("Select from all..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector))); + playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector))); } void @@ -1484,50 +1526,48 @@ RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr return; } - boost::shared_ptr apl = boost::dynamic_pointer_cast (pl); + if (track()->playlist() == pl) { + // exit when use_playlist is called by the creation of the playlist menu + // or the playlist choice is unchanged + return; + } - if (apl) { - if (track()->playlist() == apl) { - // exit when use_playlist is called by the creation of the playlist menu - // or the playlist choice is unchanged + track()->use_playlist (pl); + + RouteGroup* rg = route_group(); + + if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::edit.property_id)) { + std::string group_string = "." + rg->name() + "."; + + std::string take_name = pl->name(); + std::string::size_type idx = take_name.find(group_string); + + if (idx == std::string::npos) return; - } - track()->use_playlist (apl); - - if (route_group() && route_group()->is_active()) { - std::string group_string = "."+route_group()->name()+"."; - - std::string take_name = apl->name(); - std::string::size_type idx = take_name.find(group_string); - - if (idx == std::string::npos) - return; - - take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name - - boost::shared_ptr rl (route_group()->route_list()); - - for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) { - if ( (*i) == this->route()) { - continue; - } - - std::string playlist_name = (*i)->name()+group_string+take_name; - - boost::shared_ptr track = boost::dynamic_pointer_cast(*i); - if (!track) { - std::cerr << "route " << (*i)->name() << " is not a Track" << std::endl; - continue; - } - - boost::shared_ptr ipl = session()->playlists->by_name(playlist_name); - if (!ipl) { - // No playlist for this track for this take yet, make it - track->use_new_playlist(); - track->playlist()->set_name(playlist_name); - } else { - track->use_playlist(ipl); - } + + take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name + + boost::shared_ptr rl (rg->route_list()); + + for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) { + if ( (*i) == this->route()) { + continue; + } + + std::string playlist_name = (*i)->name()+group_string+take_name; + + boost::shared_ptr track = boost::dynamic_pointer_cast(*i); + if (!track) { + continue; + } + + boost::shared_ptr ipl = session()->playlists->by_name(playlist_name); + if (!ipl) { + // No playlist for this track for this take yet, make it + track->use_new_playlist(); + track->playlist()->set_name(playlist_name); + } else { + track->use_playlist(ipl); } } } @@ -1583,22 +1623,24 @@ RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param) { boost::shared_ptr track = automation_child (param); Gtk::CheckMenuItem* menu = automation_child_menu_item (param); - + if (!track) { /* it doesn't exist yet, so we don't care about the button state: just add it */ create_automation_child (param, true); } else { assert (menu); bool yn = menu->get_active(); - if (track->set_visibility (menu->get_active()) && yn) { - + bool changed = false; + + if ((changed = track->set_marked_for_display (menu->get_active())) && yn) { + /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden() will have done that for us. */ - - if (!no_redraw) { - _route->gui_changed (X_("track_height"), (void *) 0); /* EMIT_SIGNAL */ - } + + if (changed && !no_redraw) { + request_redraw (); + } } } } @@ -1614,9 +1656,6 @@ RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param) Gtk::CheckMenuItem* menu = automation_child_menu_item (param); - // if Evoral::Parameter::operator< doesn't obey strict weak ordering, we may crash here.... - track->get_state_node()->add_property (X_("shown"), X_("no")); - if (menu && !_hidden) { ignore_toggle = true; menu->set_active (false); @@ -1624,114 +1663,120 @@ RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param) } if (_route && !no_redraw) { - _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */ + request_redraw (); } } void -RouteTimeAxisView::show_all_automation () +RouteTimeAxisView::show_all_automation (bool apply_to_selection) { - no_redraw = true; + if (apply_to_selection) { + _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false)); + } else { + no_redraw = true; - /* Show our automation */ + /* Show our automation */ - for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) { - i->second->set_marked_for_display (true); - i->second->canvas_display()->show(); - i->second->get_state_node()->add_property ("shown", X_("yes")); + for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) { + i->second->set_marked_for_display (true); - Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first); - - if (menu) { - menu->set_active(true); + Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first); + + if (menu) { + menu->set_active(true); + } } - } - /* Show processor automation */ + /* Show processor automation */ - for (list::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) { - for (vector::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) { - if ((*ii)->view == 0) { - add_processor_automation_curve ((*i)->processor, (*ii)->what); - } + for (list::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) { + for (vector::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) { + if ((*ii)->view == 0) { + add_processor_automation_curve ((*i)->processor, (*ii)->what); + } - (*ii)->menu_item->set_active (true); + (*ii)->menu_item->set_active (true); + } } - } - no_redraw = false; + no_redraw = false; - /* Redraw */ + /* Redraw */ - _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */ + request_redraw (); + } } void -RouteTimeAxisView::show_existing_automation () +RouteTimeAxisView::show_existing_automation (bool apply_to_selection) { - no_redraw = true; + if (apply_to_selection) { + _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false)); + } else { + no_redraw = true; - /* Show our automation */ + /* Show our automation */ - for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) { - if (i->second->has_automation()) { - i->second->set_marked_for_display (true); - i->second->canvas_display()->show(); - i->second->get_state_node()->add_property ("shown", X_("yes")); + for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) { + if (i->second->has_automation()) { + i->second->set_marked_for_display (true); - Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first); - assert (menu); - menu->set_active(true); + Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first); + if (menu) { + menu->set_active(true); + } + } } - } - - /* Show processor automation */ + /* Show processor automation */ - for (list::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) { - for (vector::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) { - if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) { - (*ii)->menu_item->set_active (true); + for (list::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) { + for (vector::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) { + if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) { + (*ii)->menu_item->set_active (true); + } } } - } - no_redraw = false; + no_redraw = false; - _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */ + request_redraw (); + } } void -RouteTimeAxisView::hide_all_automation () +RouteTimeAxisView::hide_all_automation (bool apply_to_selection) { - no_redraw = true; + if (apply_to_selection) { + _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false)); + } else { + no_redraw = true; - /* Hide our automation */ + /* Hide our automation */ - for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) { - i->second->set_marked_for_display (false); - i->second->hide (); - i->second->get_state_node()->add_property ("shown", X_("no")); + for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) { + i->second->set_marked_for_display (false); - Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first); - - if (menu) { - menu->set_active (false); + Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first); + + if (menu) { + menu->set_active (false); + } } - } - /* Hide processor automation */ + /* Hide processor automation */ - for (list::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) { - for (vector::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) { - (*ii)->menu_item->set_active (false); + for (list::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) { + for (vector::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) { + (*ii)->menu_item->set_active (false); + } } - } - no_redraw = false; - _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */ + no_redraw = false; + request_redraw (); + } } @@ -1739,13 +1784,11 @@ void RouteTimeAxisView::region_view_added (RegionView* rv) { /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */ - if (is_audio_track()) { - for (Children::iterator i = children.begin(); i != children.end(); ++i) { - boost::shared_ptr atv; + for (Children::iterator i = children.begin(); i != children.end(); ++i) { + boost::shared_ptr atv; - if ((atv = boost::dynamic_pointer_cast (*i)) != 0) { - atv->add_ghost(rv); - } + if ((atv = boost::dynamic_pointer_cast (*i)) != 0) { + atv->add_ghost(rv); } } @@ -1793,25 +1836,7 @@ RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr return 0; } -static string -legalize_for_xml_node (string str) -{ - string::size_type pos; - string legal_chars = "abcdefghijklmnopqrtsuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_=:"; - string legal; - - legal = str; - pos = 0; - - while ((pos = legal.find_first_not_of (legal_chars, pos)) != string::npos) { - legal.replace (pos, 1, "_"); - pos += 1; - } - - return legal; -} - - +/** Add an AutomationTimeAxisView to display automation for a processor's parameter */ void RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr processor, Evoral::Parameter what) { @@ -1819,9 +1844,10 @@ RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr ProcessorAutomationNode* pan; if ((pan = find_processor_automation_node (processor, what)) == 0) { - error << _("programming error: ") - << string_compose (X_("processor automation curve for %1:%2 not registered with track!"), - processor->name(), what) + /* session state may never have been saved with new plugin */ + error << _("programming error: ") + << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"), + processor->name(), what.type(), (int) what.channel(), what.id() ) << endmsg; /*NOTREACHED*/ return; @@ -1831,50 +1857,32 @@ RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr return; } - name = processor->describe_parameter (what); - - /* create a string that is a legal XML node name that can be used to refer to this redirect+port combination */ - - /* FIXME: ew */ - - char state_name[256]; - snprintf (state_name, sizeof (state_name), "Redirect-%s-%" PRIu32, legalize_for_xml_node (processor->name()).c_str(), what.id()); - boost::shared_ptr control - = boost::dynamic_pointer_cast(processor->control(what, true)); - + = boost::dynamic_pointer_cast(processor->control(what, true)); + pan->view = boost::shared_ptr( - new AutomationTimeAxisView (_session, _route, processor, control, - _editor, *this, false, parent_canvas, name, state_name)); + new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (), + _editor, *this, false, parent_canvas, + processor->describe_parameter (what), processor->name())); pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor)); - if (!pan->view->marked_for_display()) { - pan->view->hide (); - } else { - pan->menu_item->set_active (true); - } - - add_child (pan->view); + add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ()); if (_view) { _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost)); } - - processor->mark_automation_visible (what, true); } void -RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr i) +RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr) { if (!_hidden) { pan->menu_item->set_active (false); } - i->mark_automation_visible (pan->what, false); - if (!no_redraw) { - _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */ + request_redraw (); } } @@ -1882,21 +1890,25 @@ void RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr p) { boost::shared_ptr processor (p.lock ()); - if (!processor) { + + if (!processor || boost::dynamic_pointer_cast (processor)) { + /* The Amp processor is a special case and is dealt with separately */ return; } - set s; - boost::shared_ptr al; + set existing; - processor->what_has_visible_data (s); + processor->what_has_data (existing); - for (set::iterator i = s.begin(); i != s.end(); ++i) { + for (set::iterator i = existing.begin(); i != existing.end(); ++i) { + + Evoral::Parameter param (*i); + boost::shared_ptr al; - if ((al = find_processor_automation_curve (processor, *i)) != 0) { + if ((al = find_processor_automation_curve (processor, param)) != 0) { al->queue_reset (); } else { - add_processor_automation_curve (processor, (*i)); + add_processor_automation_curve (processor, param); } } } @@ -1906,29 +1918,23 @@ RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ { using namespace Menu_Helpers; - XMLProperty* prop; - XMLNode* node; - add_child (track); track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param)); - bool hideit = (!show); + _automation_tracks[param] = track; - if ((node = track->get_state_node()) != 0) { - if ((prop = node->property ("shown")) != 0) { - if (string_is_affirmative (prop->value())) { - hideit = false; - } - } + /* existing state overrides "show" argument */ + string s = track->gui_property ("visible"); + if (!s.empty()) { + show = string_is_affirmative (s); } - _automation_tracks[param] = track; - - track->set_visibility (!hideit); + /* this might or might not change the visibility status, so don't rely on it */ + track->set_marked_for_display (show); - if (!no_redraw) { - _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */ + if (show && !no_redraw) { + request_redraw (); } if (!EventTypeMap::instance().is_midi_parameter(param)) { @@ -1951,14 +1957,20 @@ RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr p return; } + /* we use this override to veto the Amp processor from the plugin menu, + as its automation lane can be accessed using the special "Fader" menu + option + */ + + if (boost::dynamic_pointer_cast (processor) != 0) { + return; + } + using namespace Menu_Helpers; ProcessorAutomationInfo *rai; list::iterator x; const std::set& automatable = processor->what_can_be_automated (); - std::set has_visible_automation; - - processor->what_has_visible_data(has_visible_automation); if (automatable.empty()) { return; @@ -1991,6 +2003,9 @@ RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr p items.clear (); + std::set has_visible_automation; + AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation); + for (std::set::const_iterator i = automatable.begin(); i != automatable.end(); ++i) { ProcessorAutomationNode* pan; @@ -2000,6 +2015,8 @@ RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr p items.push_back (CheckMenuElem (name)); mitem = dynamic_cast (&items.back()); + + _subplugin_menu_map[*i] = mitem; if (has_visible_automation.find((*i)) != has_visible_automation.end()) { mitem->set_active(true); @@ -2044,25 +2061,12 @@ RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAuto redraw = true; } - if (pan->view && showit != pan->view->marked_for_display()) { - - if (showit) { - pan->view->set_marked_for_display (true); - pan->view->canvas_display()->show(); - pan->view->canvas_background()->show(); - } else { - rai->processor->mark_automation_visible (pan->what, true); - pan->view->set_marked_for_display (false); - pan->view->hide (); - } - + if (pan->view && pan->view->set_marked_for_display (showit)) { redraw = true; - } - + if (redraw && !no_redraw) { - _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */ - + request_redraw (); } } @@ -2080,10 +2084,7 @@ RouteTimeAxisView::processors_changed (RouteProcessorChange c) (*i)->valid = false; } - subplugin_menu.items().clear (); - - _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu)); - _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves)); + setup_processor_menu_and_curves (); bool deleted_processor_automation = false; @@ -2106,7 +2107,7 @@ RouteTimeAxisView::processors_changed (RouteProcessorChange c) } if (deleted_processor_automation && !no_redraw) { - _route->gui_changed ("track_height", this); + request_redraw (); } } @@ -2140,14 +2141,18 @@ RouteTimeAxisView::update_rec_display () } void -RouteTimeAxisView::set_layer_display (LayerDisplay d) +RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection) { - if (_view) { - _view->set_layer_display (d); - } + if (apply_to_selection) { + _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false)); + } else { - ensure_xml_node (); - xml_node->add_property (N_("layer-display"), enum_2_string (d)); + if (_view) { + _view->set_layer_display (d); + } + + set_gui_property (X_("layer-display"), enum_2_string (d)); + } } LayerDisplay @@ -2264,7 +2269,7 @@ RouteTimeAxisView::set_underlay_state() if (prop) { PBD::ID id (prop->value()); - RouteTimeAxisView* v = _editor.get_route_view_by_id (id); + RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id); if (v) { add_underlay(v->view(), false); @@ -2276,7 +2281,7 @@ RouteTimeAxisView::set_underlay_state() } void -RouteTimeAxisView::add_underlay (StreamView* v, bool update_xml) +RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/) { if (!v) { return; @@ -2295,9 +2300,9 @@ RouteTimeAxisView::add_underlay (StreamView* v, bool update_xml) v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost)); +#ifdef GUI_OBJECT_STATE_FIX_REQUIRED if (update_xml) { if (!underlay_xml_node) { - ensure_xml_node(); underlay_xml_node = xml_node->add_child("Underlays"); } @@ -2305,6 +2310,7 @@ RouteTimeAxisView::add_underlay (StreamView* v, bool update_xml) XMLProperty* prop = node->add_property("id"); prop->set_value(v->trackview().route()->id().to_s()); } +#endif } } @@ -2364,10 +2370,115 @@ RouteTimeAxisView::set_button_names () Gtk::CheckMenuItem* RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param) { - ParameterMenuMap::iterator i = _parameter_menu_map.find (param); - if (i == _parameter_menu_map.end()) { + ParameterMenuMap::iterator i = _main_automation_menu_map.find (param); + if (i != _main_automation_menu_map.end()) { + return i->second; + } + + i = _subplugin_menu_map.find (param); + if (i != _subplugin_menu_map.end()) { + return i->second; + } + + return 0; +} + +void +RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show) +{ + boost::shared_ptr c = _route->gain_control(); + if (!c) { + error << "Route has no gain automation, unable to add automation track view." << endmsg; + return; + } + + gain_track.reset (new AutomationTimeAxisView (_session, + _route, _route->amp(), c, param, + _editor, + *this, + false, + parent_canvas, + _route->amp()->describe_parameter(param))); + + if (_view) { + _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost)); + } + + add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show); +} + +static +void add_region_to_list (RegionView* rv, Playlist::RegionList* l) +{ + l->push_back (rv->region()); +} + +RegionView* +RouteTimeAxisView::combine_regions () +{ + /* as of may 2011, we do not offer uncombine for MIDI tracks + */ + + if (!is_audio_track()) { return 0; } - return i->second; + if (!_view) { + return 0; + } + + Playlist::RegionList selected_regions; + boost::shared_ptr playlist = track()->playlist(); + + _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions)); + + if (selected_regions.size() < 2) { + return 0; + } + + playlist->clear_changes (); + boost::shared_ptr compound_region = playlist->combine (selected_regions); + + _session->add_command (new StatefulDiffCommand (playlist)); + /* make the new region be selected */ + + return _view->find_view (compound_region); +} + +void +RouteTimeAxisView::uncombine_regions () +{ + /* as of may 2011, we do not offer uncombine for MIDI tracks + */ + if (!is_audio_track()) { + return; + } + + if (!_view) { + return; + } + + Playlist::RegionList selected_regions; + boost::shared_ptr playlist = track()->playlist(); + + /* have to grab selected regions first because the uncombine is going + * to change that in the middle of the list traverse + */ + + _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions)); + + playlist->clear_changes (); + + for (Playlist::RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) { + playlist->uncombine (*i); + } + + _session->add_command (new StatefulDiffCommand (playlist)); } + +string +RouteTimeAxisView::state_id() const +{ + return string_compose ("rtav %1", _route->id().to_s()); +} +