X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Froute_time_axis.cc;h=992a19df0e0c75dd3c94aa2bfeb0f3a58e237e4e;hb=3c25d26e91d4703ba76c90d5175aa94e4ddd850b;hp=29794eb54f3fe52d40d23a5345713cf50dc975ec;hpb=b855e5f3220027502a3c88f189d511fe2a5a3c2b;p=ardour.git diff --git a/gtk2_ardour/route_time_axis.cc b/gtk2_ardour/route_time_axis.cc index 29794eb54f..992a19df0e 100644 --- a/gtk2_ardour/route_time_axis.cc +++ b/gtk2_ardour/route_time_axis.cc @@ -45,22 +45,29 @@ #include "ardour/amp.h" #include "ardour/meter.h" #include "ardour/event_type_map.h" +#include "ardour/pannable.h" +#include "ardour/panner.h" #include "ardour/processor.h" #include "ardour/profile.h" #include "ardour/route_group.h" #include "ardour/session.h" #include "ardour/session_playlists.h" + #include "evoral/Parameter.hpp" +#include "canvas/debug.h" + #include "ardour_ui.h" #include "ardour_button.h" +#include "audio_streamview.h" #include "debug.h" -#include "global_signals.h" #include "route_time_axis.h" #include "automation_time_axis.h" #include "enums.h" #include "gui_thread.h" +#include "item_counts.h" #include "keyboard.h" +#include "paste_context.h" #include "playlist_selector.h" #include "point_selection.h" #include "prompter.h" @@ -69,14 +76,17 @@ #include "rgb_macros.h" #include "selection.h" #include "streamview.h" +#include "tooltips.h" +#include "ui_config.h" #include "utils.h" #include "route_group_menu.h" #include "ardour/track.h" -#include "i18n.h" +#include "pbd/i18n.h" using namespace ARDOUR; +using namespace ARDOUR_UI_UTILS; using namespace PBD; using namespace Gtkmm2ext; using namespace Gtk; @@ -85,25 +95,45 @@ using namespace std; using std::list; RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, ArdourCanvas::Canvas& canvas) - : AxisView(sess) - , RouteUI(sess) + : RouteUI(sess) , TimeAxisView(sess,ed,(TimeAxisView*) 0, canvas) , _view (0) , parent_canvas (canvas) , no_redraw (false) , button_table (3, 3) - , route_group_button (_("g")) - , playlist_button (_("p")) - , automation_button (_("a")) + , route_group_button (S_("RTAV|G")) + , playlist_button (S_("RTAV|P")) + , automation_button (S_("RTAV|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, true, 125, 18) + , gm (sess, true, 75, 14) , _ignore_set_layer_display (false) + , gain_automation_item(NULL) + , trim_automation_item(NULL) + , mute_automation_item(NULL) + , pan_automation_item(NULL) +{ + number_label.set_name("tracknumber label"); + number_label.set_elements((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::Text|ArdourButton::Inactive)); + number_label.set_alignment(.5, .5); + number_label.set_fallthrough_to_parent (true); + + sess->config.ParameterChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::parameter_changed, this, _1), gui_context()); + UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::parameter_changed)); + + parameter_changed ("editor-stereo-only-meters"); +} + +void +RouteTimeAxisView::route_property_changed (const PBD::PropertyChange& what_changed) { + if (what_changed.contains (ARDOUR::Properties::name)) { + label_view (); + } } void @@ -111,11 +141,15 @@ RouteTimeAxisView::set_route (boost::shared_ptr rt) { RouteUI::set_route (rt); + CANVAS_DEBUG_NAME (_canvas_display, string_compose ("main for %1", rt->name())); + CANVAS_DEBUG_NAME (selection_group, string_compose ("selections for %1", rt->name())); + CANVAS_DEBUG_NAME (_ghost_group, string_compose ("ghosts for %1", rt->name())); + int meter_width = 3; if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) { meter_width = 6; } - gm.set_controls (_route, _route->shared_peak_meter(), _route->amp()); + gm.set_controls (_route, _route->shared_peak_meter(), _route->amp(), _route->gain_control()); gm.get_level_meter().set_no_show_all(); gm.get_level_meter().setup_meters(50, meter_width); gm.update_gain_sensitive (); @@ -135,9 +169,6 @@ RouteTimeAxisView::set_route (boost::shared_ptr rt) set_gui_property ("visible", false); } - mute_changed (0); - update_solo_display (); - timestretch_rect = 0; no_redraw = false; @@ -147,84 +178,127 @@ RouteTimeAxisView::set_route (boost::shared_ptr rt) playlist_button.set_name ("route button"); automation_button.set_name ("route button"); - 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)); + route_group_button.signal_button_press_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false); + playlist_button.signal_button_press_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click), false); + automation_button.signal_button_press_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click), false); if (is_track()) { - /* use icon */ - - switch (track()->mode()) { - case ARDOUR::Normal: - case ARDOUR::NonLayered: - rec_enable_button->set_image (::get_icon (X_("record_normal_red"))); - break; - case ARDOUR::Destructive: - rec_enable_button->set_image (::get_icon (X_("record_tape_red"))); - break; + if (ARDOUR::Profile->get_mixbus()) { + controls_table.attach (*rec_enable_button, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0); + } else { + controls_table.attach (*rec_enable_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0); } - controls_table.attach (*rec_enable_button, 5, 6, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0); - if (is_midi_track()) { - ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record (Right-click for Step Edit)")); + set_tooltip(*rec_enable_button, _("Record (Right-click for Step Edit)")); gm.set_fader_name ("MidiTrackFader"); } else { - ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record")); + set_tooltip(*rec_enable_button, _("Record")); gm.set_fader_name ("AudioTrackFader"); } - rec_enable_button->set_sensitive (_session->writable()); - /* set playlist button tip to the current playlist, and make it update when it changes */ update_playlist_tip (); track()->PlaylistChanged.connect (*this, invalidator (*this), ui_bind(&RouteTimeAxisView::update_playlist_tip, this), gui_context()); } else { gm.set_fader_name ("AudioBusFader"); + Gtk::Fixed *blank = manage(new Gtk::Fixed()); + controls_button_size_group->add_widget(*blank); + if (ARDOUR::Profile->get_mixbus() ) { + controls_table.attach (*blank, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0); + } else { + controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0); + } + blank->show(); } - Gtk::VBox *mtrbox = manage(new Gtk::VBox()); - mtrbox->pack_start(gm.get_level_meter(), false, false, 2); - controls_hbox.pack_start(*mtrbox, false, false, 4); - mtrbox->show(); + top_hbox.pack_end(gm.get_level_meter(), false, false, 2); + + if (!ARDOUR::Profile->get_mixbus()) { + controls_meters_size_group->add_widget (gm.get_level_meter()); + } _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context()); _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context()); _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context()); + _route->track_number_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::label_view, this), gui_context()); - controls_table.attach (*mute_button, 6, 7, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0); + if (ARDOUR::Profile->get_mixbus()) { + controls_table.attach (*mute_button, 1, 2, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0); + } else { + controls_table.attach (*mute_button, 3, 4, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0); + } + // mute button is always present, it is used to + // force the 'blank' placeholders to the proper size + controls_button_size_group->add_widget(*mute_button); - if (!_route->is_master()) { - controls_table.attach (*solo_button, 7, 8, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0); - } + if (!_route->is_master()) { + if (ARDOUR::Profile->get_mixbus()) { + controls_table.attach (*solo_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0); + } else { + controls_table.attach (*solo_button, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0); + } + } else { + Gtk::Fixed *blank = manage(new Gtk::Fixed()); + controls_button_size_group->add_widget(*blank); + if (ARDOUR::Profile->get_mixbus()) { + controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0); + } else { + controls_table.attach (*blank, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0); + } + blank->show(); + } - controls_table.attach (route_group_button, 7, 8, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0); - controls_table.attach (gm.get_gain_slider(), 0, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::AttachOptions (0), 3, 0); + if (ARDOUR::Profile->get_mixbus()) { + controls_table.attach (route_group_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0); + controls_table.attach (gm.get_gain_slider(), 3, 5, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0); + } + else if (!ARDOUR::Profile->get_trx()) { + controls_table.attach (route_group_button, 4, 5, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0); + controls_table.attach (gm.get_gain_slider(), 0, 2, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0); + } + + set_tooltip(*solo_button,_("Solo")); + set_tooltip(*mute_button,_("Mute")); + set_tooltip(route_group_button, _("Route Group")); - ARDOUR_UI::instance()->set_tip(*solo_button,_("Solo")); - ARDOUR_UI::instance()->set_tip(*mute_button,_("Mute")); - ARDOUR_UI::instance()->set_tip(route_group_button, _("Route Group")); + mute_button->set_tweaks(ArdourButton::TrackHeader); + solo_button->set_tweaks(ArdourButton::TrackHeader); + rec_enable_button->set_tweaks(ArdourButton::TrackHeader); + playlist_button.set_tweaks(ArdourButton::TrackHeader); + automation_button.set_tweaks(ArdourButton::TrackHeader); + route_group_button.set_tweaks(ArdourButton::TrackHeader); if (is_midi_track()) { - ARDOUR_UI::instance()->set_tip(automation_button, _("MIDI Controllers and Automation")); + set_tooltip(automation_button, _("MIDI Controllers and Automation")); } else { - ARDOUR_UI::instance()->set_tip(automation_button, _("Automation")); + set_tooltip(automation_button, _("Automation")); } + update_track_number_visibility(); label_view (); - controls_table.attach (automation_button, 6, 7, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND); + if (ARDOUR::Profile->get_mixbus()) { + controls_table.attach (automation_button, 1, 2, 2, 3, Gtk::SHRINK, Gtk::SHRINK); + } + else if (!ARDOUR::Profile->get_trx()) { + controls_table.attach (automation_button, 3, 4, 2, 3, Gtk::SHRINK, Gtk::SHRINK); + } if (is_track() && track()->mode() == ARDOUR::Normal) { - controls_table.attach (playlist_button, 5, 6, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND); + if (ARDOUR::Profile->get_mixbus()) { + controls_table.attach (playlist_button, 0, 1, 2, 3, Gtk::SHRINK, Gtk::SHRINK); + } + else if (!ARDOUR::Profile->get_trx()) { + controls_table.attach (playlist_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK); + } } _y_position = -1; _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context()); - _route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::route_property_changed, this, _1), gui_context()); if (is_track()) { @@ -239,26 +313,24 @@ RouteTimeAxisView::set_route (boost::shared_ptr rt) /* pick up the correct freeze state */ map_frozen (); - } + } _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_pixel)); - ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler)); + UIConfiguration::instance().ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler)); PropertyList* plist = new PropertyList(); - plist->add (ARDOUR::Properties::mute, true); - plist->add (ARDOUR::Properties::solo, true); + plist->add (ARDOUR::Properties::group_mute, true); + plist->add (ARDOUR::Properties::group_solo, true); route_group_menu = new RouteGroupMenu (_session, plist); - gm.get_gain_slider().signal_scroll_event().connect(sigc::mem_fun(*this, &RouteTimeAxisView::controls_ebox_scroll), false); - gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false); } RouteTimeAxisView::~RouteTimeAxisView () { - CatchDeletion (this); + cleanup_gui_properties (); for (list::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) { delete *i; @@ -273,6 +345,16 @@ RouteTimeAxisView::~RouteTimeAxisView () _automation_tracks.clear (); delete route_group_menu; + CatchDeletion (this); +} + +string +RouteTimeAxisView::name() const +{ + if (_route) { + return _route->name(); + } + return string(); } void @@ -297,7 +379,7 @@ RouteTimeAxisView::setup_processor_menu_and_curves () _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves)); } -gint +bool RouteTimeAxisView::route_group_click (GdkEventButton *ev) { if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) { @@ -311,9 +393,15 @@ RouteTimeAxisView::route_group_click (GdkEventButton *ev) r.push_back (route ()); route_group_menu->build (r); - route_group_menu->menu()->popup (ev->button, ev->time); + if (ev->button == 1) { + Gtkmm2ext::anchored_menu_popup(route_group_menu->menu(), + &route_group_button, + "", 1, ev->time); + } else { + route_group_menu->menu()->popup (ev->button, ev->time); + } - return false; + return true; } void @@ -325,19 +413,60 @@ RouteTimeAxisView::playlist_changed () void RouteTimeAxisView::label_view () { - string x = _route->name(); - - if (x != name_label.get_text()) { + string x = _route->name (); + if (x != name_label.get_text ()) { name_label.set_text (x); } + const int64_t track_number = _route->track_number (); + if (track_number == 0) { + number_label.set_text (""); + } else { + number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec)); + } +} + +void +RouteTimeAxisView::update_track_number_visibility () +{ + DisplaySuspender ds; + bool show_label = _session->config.get_track_name_number(); + + if (_route && _route->is_master()) { + show_label = false; + } + 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 (); + } else { + number_label.hide (); + } } void -RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed) +RouteTimeAxisView::parameter_changed (string const & p) { - if (what_changed.contains (ARDOUR::Properties::name)) { - label_view (); + if (p == "track-name-number") { + update_track_number_visibility(); + } else if (p == "editor-stereo-only-meters") { + if (UIConfiguration::instance().get_editor_stereo_only_meters()) { + gm.get_level_meter().set_max_audio_meter_count (2); + } else { + gm.get_level_meter().set_max_audio_meter_count (0); + } } } @@ -349,20 +478,32 @@ RouteTimeAxisView::take_name_changed (void *src) } } -void -RouteTimeAxisView::playlist_click () +bool +RouteTimeAxisView::playlist_click (GdkEventButton *ev) { - build_playlist_menu (); + if (ev->button != 1) { + return true; + } + + build_playlist_menu (); conditionally_add_to_selection (); - playlist_action_menu->popup (1, gtk_get_current_event_time()); + Gtkmm2ext::anchored_menu_popup(playlist_action_menu, &playlist_button, + "", 1, ev->time); + return true; } -void -RouteTimeAxisView::automation_click () +bool +RouteTimeAxisView::automation_click (GdkEventButton *ev) { + if (ev->button != 1) { + return true; + } + conditionally_add_to_selection (); build_automation_action_menu (false); - automation_action_menu->popup (1, gtk_get_current_event_time()); + Gtkmm2ext::anchored_menu_popup(automation_action_menu, &automation_button, + "", 1, ev->time); + return true; } void @@ -385,23 +526,64 @@ RouteTimeAxisView::build_automation_action_menu (bool for_selection) automation_action_menu->set_name ("ArdourContextMenu"); items.push_back (MenuElem (_("Show All Automation"), - sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection))); + sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection))); items.push_back (MenuElem (_("Show Existing Automation"), - sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection))); + sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection))); items.push_back (MenuElem (_("Hide All Automation"), - sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection))); + sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection))); /* Attach the plugin submenu. It may have previously been used elsewhere, - so it was detached above + so it was detached above */ - + if (!subplugin_menu.items().empty()) { items.push_back (SeparatorElem ()); items.push_back (MenuElem (_("Processor automation"), subplugin_menu)); items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);; } + + /* Add any route automation */ + + if (gain_track) { + items.push_back (CheckMenuElem (_("Fader"), sigc::mem_fun (*this, &RouteTimeAxisView::update_gain_track_visibility))); + gain_automation_item = dynamic_cast (&items.back ()); + gain_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) && + (gain_track && string_is_affirmative (gain_track->gui_property ("visible")))); + + _main_automation_menu_map[Evoral::Parameter(GainAutomation)] = gain_automation_item; + } + + if (trim_track) { + items.push_back (CheckMenuElem (_("Trim"), sigc::mem_fun (*this, &RouteTimeAxisView::update_trim_track_visibility))); + trim_automation_item = dynamic_cast (&items.back ()); + trim_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) && + (trim_track && string_is_affirmative (trim_track->gui_property ("visible")))); + + _main_automation_menu_map[Evoral::Parameter(TrimAutomation)] = trim_automation_item; + } + + if (mute_track) { + items.push_back (CheckMenuElem (_("Mute"), sigc::mem_fun (*this, &RouteTimeAxisView::update_mute_track_visibility))); + mute_automation_item = dynamic_cast (&items.back ()); + mute_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) && + (mute_track && string_is_affirmative (mute_track->gui_property ("visible")))); + + _main_automation_menu_map[Evoral::Parameter(MuteAutomation)] = mute_automation_item; + } + + if (!pan_tracks.empty()) { + items.push_back (CheckMenuElem (_("Pan"), sigc::mem_fun (*this, &RouteTimeAxisView::update_pan_track_visibility))); + pan_automation_item = dynamic_cast (&items.back ()); + pan_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) && + (!pan_tracks.empty() && string_is_affirmative (pan_tracks.front()->gui_property ("visible")))); + + set const & params = _route->pannable()->what_can_be_automated (); + for (set::const_iterator p = params.begin(); p != params.end(); ++p) { + _main_automation_menu_map[*p] = pan_automation_item; + } + } } void @@ -420,20 +602,21 @@ RouteTimeAxisView::build_display_menu () items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color))); + items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor))); + + items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration))); + + items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration))); + + items.push_back (SeparatorElem()); + 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))); - 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 (); @@ -449,21 +632,27 @@ RouteTimeAxisView::build_display_menu () int overlaid = 0; int stacked = 0; + int unchangeable = 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; } - switch (v->layer_display ()) { - case Overlaid: - ++overlaid; - break; - case Stacked: - case Expanded: - ++stacked; - break; + if (v->can_change_layer_display()) { + switch (v->layer_display ()) { + case Overlaid: + ++overlaid; + break; + case Stacked: + case Expanded: + ++stacked; + break; + } + } else { + unchangeable++; } } @@ -474,176 +663,179 @@ RouteTimeAxisView::build_display_menu () */ _ignore_set_layer_display = true; - + 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)); + if (unchangeable) { + i->set_sensitive (false); + } + layers_items.push_back (RadioMenuElem (layers_group, _("Stacked"))); 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), Stacked, true)); - + + if (unchangeable) { + i->set_sensitive (false); + } + _ignore_set_layer_display = false; items.push_back (MenuElem (_("Layers"), *layers_menu)); - if (!Profile->get_sae()) { - - Menu* alignment_menu = manage (new Menu); - MenuList& alignment_items = alignment_menu->items(); - alignment_menu->set_name ("ArdourContextMenu"); + Menu* alignment_menu = manage (new Menu); + MenuList& alignment_items = alignment_menu->items(); + alignment_menu->set_name ("ArdourContextMenu"); - RadioMenuItem::Group align_group; + RadioMenuItem::Group align_group; - /* Same verbose hacks as for the layering options above */ + /* Same verbose hacks as for the layering options above */ - int existing = 0; - int capture = 0; - int automatic = 0; - int styles = 0; - boost::shared_ptr first_track; + 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; - } + for (TrackSelection::const_iterator t = s.begin(); t != s.end(); ++t) { + RouteTimeAxisView* r = dynamic_cast (*t); + if (!r || !r->is_track ()) { + continue; + } - 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; - } + if (!first_track) { + first_track = r->track(); } - 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()->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; + } + } - switch (r->track()->mode()) { - case Normal: - ++normal; - break; - case Destructive: - ++tape; + bool inconsistent; + switch (styles) { + case 1: + case 2: + case 4: + inconsistent = false; + break; + default: + inconsistent = true; + break; + } + + 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 NonLayered: - ++non_layered; + case CaptureTime: + alignment_items.push_back (MenuElem (_("(Currently: Capture Time)"))); break; } + break; + default: + 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)); + 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)); - 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)); + 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)); - 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 (_("Alignment"), *alignment_menu)); - items.push_back (MenuElem (_("Mode"), *mode_menu)); + } else { + /* show nothing */ } +#ifdef XXX_OLD_DESTRUCTIVE_API_XXX + 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 t = s.begin(); t != s.end(); ++t) { + RouteTimeAxisView* r = dynamic_cast (*t); + 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 (_("Record Mode"), *mode_menu)); +#endif items.push_back (SeparatorElem()); @@ -653,7 +845,7 @@ RouteTimeAxisView::build_display_menu () } 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); @@ -661,19 +853,19 @@ RouteTimeAxisView::build_display_menu () r.push_back (rtv->route ()); } } - + if (r.empty ()) { r.push_back (route ()); } route_group_menu->build (r); items.push_back (MenuElem (_("Group"), *route_group_menu->menu ())); - + build_automation_action_menu (true); items.push_back (MenuElem (_("Automation"), *automation_action_menu)); - + items.push_back (SeparatorElem()); - + int active = 0; int inactive = 0; TrackSelection const & s = _editor.get_selection().tracks; @@ -699,18 +891,20 @@ RouteTimeAxisView::build_display_menu () } else if (active > 0 && inactive > 0) { i->set_inconsistent (true); } + i->set_sensitive(! _session->transport_rolling()); i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true)); 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))); + if (_route && !_route->is_master()) { + items.push_back (SeparatorElem()); + items.push_back (MenuElem (_("Duplicate..."), boost::bind (&ARDOUR_UI::start_duplicate_routes, ARDOUR_UI::instance()))); } + items.push_back (SeparatorElem()); + items.push_back (MenuElem (_("Remove"), sigc::mem_fun(_editor, &PublicEditor::remove_tracks))); } +#ifdef XXX_OLD_DESTRUCTIVE_API_XXX void RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection) { @@ -718,7 +912,7 @@ RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection) _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false)); } else { - bool needs_bounce; + bool needs_bounce = false; if (!track()->can_use_mode (mode, needs_bounce)) { @@ -732,24 +926,9 @@ RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection) } track()->set_mode (mode); - - rec_enable_button->remove (); - - switch (mode) { - case ARDOUR::NonLayered: - case ARDOUR::Normal: - rec_enable_button->set_image (::get_icon (X_("record_normal_red"))); - rec_enable_button->set_text (string()); - break; - case ARDOUR::Destructive: - rec_enable_button->set_image (::get_icon (X_("record_tape_red"))); - rec_enable_button->set_text (string()); - break; - } - - rec_enable_button->show_all (); } } +#endif void RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer) @@ -782,8 +961,8 @@ RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layer if (timestretch_rect == 0) { timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ()); - timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchFill()); - timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchOutline()); + timestretch_rect->set_fill_color (ArdourCanvas::HSV (UIConfiguration::instance().color ("time stretch fill")).mod (UIConfiguration::instance().modifier ("time stretch fill")).color()); + timestretch_rect->set_outline_color (UIConfiguration::instance().color ("time stretch outline")); } timestretch_rect->show (); @@ -827,7 +1006,7 @@ RouteTimeAxisView::show_selection (TimeSelection& ts) } void -RouteTimeAxisView::set_height (uint32_t h) +RouteTimeAxisView::set_height (uint32_t h, TrackHeightMode m) { int gmlen = h - 9; bool height_changed = (height == 0) || (h != height); @@ -838,7 +1017,7 @@ RouteTimeAxisView::set_height (uint32_t h) } gm.get_level_meter().setup_meters (gmlen, meter_width); - TimeAxisView::set_height (h); + TimeAxisView::set_height (h, m); if (_view) { _view->set_height ((double) current_height()); @@ -900,6 +1079,8 @@ RouteTimeAxisView::route_color_changed () if (_view) { _view->apply_color (color(), StreamView::RegionColor); } + + number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color())); } void @@ -961,20 +1142,25 @@ RouteTimeAxisView::rename_current_playlist () prompter.set_title (_("Rename Playlist")); prompter.set_prompt (_("New name for playlist:")); - prompter.set_initial_text (pl->name()); prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT); + prompter.set_initial_text (pl->name()); prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false); - switch (prompter.run ()) { - case Gtk::RESPONSE_ACCEPT: + while (true) { + if (prompter.run () != Gtk::RESPONSE_ACCEPT) { + break; + } prompter.get_result (name); if (name.length()) { - pl->set_name (name); + if (_session->playlists->by_name (name)) { + MessageDialog msg (_("Given playlist name is not unique.")); + msg.run (); + prompter.set_initial_text (Playlist::bump_name (name, *_session)); + } else { + pl->set_name (name); + break; + } } - break; - - default: - break; } } @@ -1014,7 +1200,7 @@ RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector } void -RouteTimeAxisView::use_copy_playlist (bool prompt, vector > const & playlists_before_op) +RouteTimeAxisView::use_new_playlist (bool prompt, vector > const & playlists_before_op, bool copy) { string name; @@ -1030,92 +1216,55 @@ RouteTimeAxisView::use_copy_playlist (bool prompt, vectorname(); - if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) { - name = resolve_new_group_playlist_name(name, playlists_before_op); + if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::group_select.property_id)) { + name = resolve_new_group_playlist_name(name,playlists_before_op); } while (_session->playlists->by_name(name)) { name = Playlist::bump_name (name, *_session); } - // TODO: The prompter "new" button should be de-activated if the user - // specifies a playlist name which already exists in the session. - if (prompt) { + // TODO: The prompter "new" button should be de-activated if the user + // specifies a playlist name which already exists in the session. ArdourPrompter prompter (true); - prompter.set_title (_("New Copy Playlist")); - prompter.set_prompt (_("Name for new playlist:")); + if (copy) { + prompter.set_title (_("New Copy Playlist")); + prompter.set_prompt (_("Name for playlist copy:")); + } else { + prompter.set_title (_("New Playlist")); + prompter.set_prompt (_("Name for new playlist:")); + } prompter.set_initial_text (name); prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT); prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true); prompter.show_all (); - switch (prompter.run ()) { - case Gtk::RESPONSE_ACCEPT: + while (true) { + if (prompter.run () != Gtk::RESPONSE_ACCEPT) { + return; + } prompter.get_result (name); - break; - - default: - return; + if (name.length()) { + if (_session->playlists->by_name (name)) { + MessageDialog msg (_("Given playlist name is not unique.")); + msg.run (); + prompter.set_initial_text (Playlist::bump_name (name, *_session)); + } else { + break; + } + } } } if (name.length()) { - tr->use_copy_playlist (); - tr->playlist()->set_name (name); - } -} - -void -RouteTimeAxisView::use_new_playlist (bool prompt, vector > const & playlists_before_op) -{ - string name; - - boost::shared_ptr tr = track (); - if (!tr || tr->destructive()) { - return; - } - - boost::shared_ptr pl = tr->playlist(); - if (!pl) { - return; - } - - name = pl->name(); - - if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) { - name = resolve_new_group_playlist_name(name,playlists_before_op); - } - - while (_session->playlists->by_name(name)) { - name = Playlist::bump_name (name, *_session); - } - - - if (prompt) { - - ArdourPrompter prompter (true); - - prompter.set_title (_("New Playlist")); - prompter.set_prompt (_("Name for new playlist:")); - prompter.set_initial_text (name); - prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT); - prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true); - - switch (prompter.run ()) { - case Gtk::RESPONSE_ACCEPT: - prompter.get_result (name); - break; - - default: - return; + if (copy) { + tr->use_copy_playlist (); + } else { + tr->use_new_playlist (); } - } - - if (name.length()) { - tr->use_new_playlist (); tr->playlist()->set_name (name); } } @@ -1158,15 +1307,22 @@ RouteTimeAxisView::selection_click (GdkEventButton* ev) if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) { /* special case: select/deselect all tracks */ + + _editor.begin_reversible_selection_op (X_("Selection Click")); + if (_editor.get_selection().selected (this)) { _editor.get_selection().clear_tracks (); } else { _editor.select_all_tracks (); } + _editor.commit_reversible_selection_op (); + return; } + _editor.begin_reversible_selection_op (X_("Selection Click")); + switch (ArdourKeyboard::selection_type (ev->state)) { case Selection::Toggle: _editor.get_selection().toggle (this); @@ -1184,6 +1340,8 @@ RouteTimeAxisView::selection_click (GdkEventButton* ev) _editor.get_selection().add (this); break; } + + _editor.commit_reversible_selection_op (); } void @@ -1192,6 +1350,10 @@ RouteTimeAxisView::set_selected_points (PointSelection& points) for (Children::iterator i = children.begin(); i != children.end(); ++i) { (*i)->set_selected_points (points); } + AudioStreamView* asv = dynamic_cast(_view); + if (asv) { + asv->set_selected_points (points); + } } void @@ -1206,7 +1368,7 @@ RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions) * @param results List to add things to. */ void -RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list& results) +RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list& results, bool within) { double speed = 1.0; @@ -1218,14 +1380,14 @@ RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top 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); + _view->get_selectables (start_adjusted, end_adjusted, top, bot, results, within); } /* pick up visible automation tracks */ for (Children::iterator i = children.begin(); i != children.end(); ++i) { if (!(*i)->hidden()) { - (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results); + (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results, within); } } } @@ -1252,12 +1414,6 @@ RouteTimeAxisView::route_group () const return _route->route_group(); } -string -RouteTimeAxisView::name() const -{ - return _route->name(); -} - boost::shared_ptr RouteTimeAxisView::playlist () const { @@ -1270,33 +1426,30 @@ RouteTimeAxisView::playlist () const } } -void -RouteTimeAxisView::name_entry_changed () +bool +RouteTimeAxisView::name_entry_changed (string const& str) { - TimeAxisView::name_entry_changed (); - - string x = name_entry->get_text (); - - if (x == _route->name()) { - return; + if (str == _route->name()) { + return true; } + string x = str; + strip_whitespace_edges (x); - if (x.length() == 0) { - name_entry->set_text (_route->name()); - return; + if (x.empty()) { + return false; } 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 (); + ARDOUR_UI::instance()->popup_error (string_compose (_("The name \"%1\" is reserved for %2"), x, PROGRAM_NAME)); + return false; } else if (RouteUI::verify_new_route_name (x)) { _route->set_name (x); - } else { - name_entry->grab_focus (); + return true; } + + return false; } boost::shared_ptr @@ -1323,6 +1476,41 @@ RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir) return -1; } +void +RouteTimeAxisView::fade_range (TimeSelection& selection) +{ + boost::shared_ptr what_we_got; + boost::shared_ptr tr = track (); + boost::shared_ptr playlist; + + if (tr == 0) { + /* route is a bus, not a track */ + return; + } + + playlist = tr->playlist(); + + TimeSelection time (selection); + float const speed = tr->speed(); + if (speed != 1.0f) { + for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) { + (*i).start = session_frame_to_track_frame((*i).start, speed); + (*i).end = session_frame_to_track_frame((*i).end, speed); + } + } + + playlist->clear_changes (); + playlist->clear_owned_changes (); + + playlist->fade_range (time); + + vector cmds; + playlist->rdiff (cmds); + _session->add_commands (cmds); + _session->add_command (new StatefulDiffCommand (playlist)); + +} + void RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op) { @@ -1352,17 +1540,25 @@ RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op) switch (op) { case Delete: if (playlist->cut (time) != 0) { + if (Config->get_edit_mode() == Ripple) + playlist->ripple(time.start(), -time.length(), NULL); + // no need to exclude any regions from rippling here + 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); + if (Config->get_edit_mode() == Ripple) + playlist->ripple(time.start(), -time.length(), NULL); + // no need to exclude any regions from rippling here + vector cmds; playlist->rdiff (cmds); _session->add_commands (cmds); @@ -1378,6 +1574,9 @@ RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op) case Clear: if ((what_we_got = playlist->cut (time)) != 0) { + if (Config->get_edit_mode() == Ripple) + playlist->ripple(time.start(), -time.length(), NULL); + // no need to exclude any regions from rippling here vector cmds; playlist->rdiff (cmds); @@ -1390,20 +1589,20 @@ RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op) } bool -RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth) +RouteTimeAxisView::paste (framepos_t pos, const Selection& selection, PasteContext& ctx, const int32_t sub_num) { if (!is_track()) { return false; } - boost::shared_ptr pl = playlist (); - PlaylistSelection::iterator p; - - for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {} + boost::shared_ptr pl = playlist (); + const ARDOUR::DataType type = pl->data_type(); + PlaylistSelection::const_iterator p = selection.playlists.get_nth(type, ctx.counts.n_playlists(type)); if (p == selection.playlists.end()) { return false; } + ctx.counts.increase_n_playlists(type); DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos)); @@ -1412,8 +1611,24 @@ RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, siz DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos)); } - pl->clear_changes (); - pl->paste (*p, pos, times); + /* add multi-paste offset if applicable */ + std::pair extent = (*p)->get_extent(); + const framecnt_t duration = extent.second - extent.first; + pos += _editor.get_paste_offset(pos, ctx.count, duration); + + pl->clear_changes (); + pl->clear_owned_changes (); + if (Config->get_edit_mode() == Ripple) { + std::pair extent = (*p)->get_extent_with_endspace(); + framecnt_t amount = extent.second - extent.first; + pl->ripple(pos, amount * ctx.times, boost::shared_ptr()); + } + pl->paste (*p, pos, ctx.times, sub_num); + + vector cmds; + pl->rdiff (cmds); + _session->add_commands (cmds); + _session->add_command (new StatefulDiffCommand (pl)); return true; @@ -1468,7 +1683,7 @@ RouteTimeAxisView::build_playlist_menu () 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() || !route_group()->enabled_property (ARDOUR::Properties::select.property_id)) { + if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::group_select.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))); @@ -1483,7 +1698,7 @@ RouteTimeAxisView::build_playlist_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 @@ -1509,29 +1724,29 @@ RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr } track()->use_playlist (pl); - + RouteGroup* rg = route_group(); - - if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) { + + if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::group_select.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; - + 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; @@ -1541,7 +1756,7 @@ RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr /* Don't change playlists of frozen tracks */ continue; } - + boost::shared_ptr ipl = session()->playlists->by_name(playlist_name); if (!ipl) { // No playlist for this track for this take yet, make it @@ -1558,30 +1773,30 @@ void RouteTimeAxisView::update_playlist_tip () { RouteGroup* rg = route_group (); - if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) { + if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::group_select.property_id)) { string group_string = "." + rg->name() + "."; - + string take_name = track()->playlist()->name(); string::size_type idx = take_name.find(group_string); - + if (idx != string::npos) { /* find the bit containing the take number / name */ take_name = take_name.substr (idx + group_string.length()); /* set the playlist button tooltip to the take name */ - ARDOUR_UI::instance()->set_tip ( + set_tooltip ( playlist_button, string_compose(_("Take: %1.%2"), - Glib::Markup::escape_text(rg->name()), - Glib::Markup::escape_text(take_name)) + Gtkmm2ext::markup_escape_text (rg->name()), + Gtkmm2ext::markup_escape_text (take_name)) ); - + return; } } /* set the playlist button tooltip to the playlist name */ - ARDOUR_UI::instance()->set_tip (playlist_button, _("Playlist") + std::string(": ") + Glib::Markup::escape_text(track()->playlist()->name())); + set_tooltip (playlist_button, _("Playlist") + std::string(": ") + Gtkmm2ext::markup_escape_text (track()->playlist()->name())); } @@ -1603,13 +1818,12 @@ RouteTimeAxisView::map_frozen () switch (track()->freeze_state()) { case Track::Frozen: playlist_button.set_sensitive (false); - rec_enable_button->set_sensitive (false); break; default: playlist_button.set_sensitive (true); - rec_enable_button->set_sensitive (true); break; } + RouteUI::map_frozen (); } void @@ -1617,11 +1831,11 @@ RouteTimeAxisView::color_handler () { //case cTimeStretchOutline: if (timestretch_rect) { - timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchOutline()); + timestretch_rect->set_outline_color (UIConfiguration::instance().color ("time stretch outline")); } //case cTimeStretchFill: if (timestretch_rect) { - timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchFill()); + timestretch_rect->set_fill_color (UIConfiguration::instance().color ("time stretch fill")); } reset_meter(); @@ -1679,6 +1893,126 @@ RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param) } } +void +RouteTimeAxisView::update_gain_track_visibility () +{ + bool const showit = gain_automation_item->get_active(); + + if (showit != string_is_affirmative (gain_track->gui_property ("visible"))) { + gain_track->set_marked_for_display (showit); + + /* now trigger a redisplay */ + + if (!no_redraw) { + _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */ + } + } +} + +void +RouteTimeAxisView::update_trim_track_visibility () +{ + bool const showit = trim_automation_item->get_active(); + + if (showit != string_is_affirmative (trim_track->gui_property ("visible"))) { + trim_track->set_marked_for_display (showit); + + /* now trigger a redisplay */ + + if (!no_redraw) { + _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */ + } + } +} + +void +RouteTimeAxisView::update_mute_track_visibility () +{ + bool const showit = mute_automation_item->get_active(); + + if (showit != string_is_affirmative (mute_track->gui_property ("visible"))) { + mute_track->set_marked_for_display (showit); + + /* now trigger a redisplay */ + + if (!no_redraw) { + _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */ + } + } +} + +void +RouteTimeAxisView::update_pan_track_visibility () +{ + bool const showit = pan_automation_item->get_active(); + bool changed = false; + + for (list >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) { + if ((*i)->set_marked_for_display (showit)) { + changed = true; + } + } + + if (changed) { + _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */ + } +} + +void +RouteTimeAxisView::ensure_pan_views (bool show) +{ + bool changed = false; + for (list >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) { + changed = true; + (*i)->set_marked_for_display (false); + } + if (changed) { + _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */ + } + pan_tracks.clear(); + + if (!_route->panner()) { + return; + } + + set params = _route->panner()->what_can_be_automated(); + set::iterator p; + + for (p = params.begin(); p != params.end(); ++p) { + boost::shared_ptr pan_control = _route->pannable()->automation_control(*p); + + if (pan_control->parameter().type() == NullAutomation) { + error << "Pan control has NULL automation type!" << endmsg; + continue; + } + + if (automation_child (pan_control->parameter ()).get () == 0) { + + /* we don't already have an AutomationTimeAxisView for this parameter */ + + std::string const name = _route->panner()->describe_parameter (pan_control->parameter ()); + + boost::shared_ptr t ( + new AutomationTimeAxisView (_session, + _route, + _route->pannable(), + pan_control, + pan_control->parameter (), + _editor, + *this, + false, + parent_canvas, + name) + ); + + pan_tracks.push_back (t); + add_automation_child (*p, t, show); + } else { + pan_tracks.push_back (automation_child (pan_control->parameter ())); + } + } +} + void RouteTimeAxisView::show_all_automation (bool apply_to_selection) @@ -1746,7 +2080,7 @@ RouteTimeAxisView::show_existing_automation (bool apply_to_selection) 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) { + if ((*i)->processor->control((*ii)->what)->list()->size() > 0) { (*ii)->menu_item->set_active (true); } } @@ -1861,7 +2195,7 @@ RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr << 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*/ + abort(); /*NOTREACHED*/ return; } @@ -1871,10 +2205,10 @@ RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr boost::shared_ptr control = boost::dynamic_pointer_cast(processor->control(what, true)); - + pan->view = boost::shared_ptr( new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (), - _editor, *this, false, parent_canvas, + _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)); @@ -1913,7 +2247,7 @@ RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptrwhat_has_data (existing); for (set::iterator i = existing.begin(); i != existing.end(); ++i) { - + Evoral::Parameter param (*i); boost::shared_ptr al; @@ -1938,7 +2272,7 @@ RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ /* existing state overrides "show" argument */ string s = track->gui_property ("visible"); - if (!s.empty()) { + if (!s.empty()) { show = string_is_affirmative (s); } @@ -1949,7 +2283,7 @@ RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ request_redraw (); } - if (!EventTypeMap::instance().is_midi_parameter(param)) { + if (!ARDOUR::parameter_is_midi((AutomationType)param.type())) { /* MIDI-related parameters are always in the menu, there's no reason to rebuild the menu just because we added a automation lane for one of them. But if we add a non-MIDI automation @@ -1995,14 +2329,10 @@ RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr p } if (x == processor_automation.end()) { - rai = new ProcessorAutomationInfo (processor); processor_automation.push_back (rai); - } else { - rai = *x; - } /* any older menu was deleted at the top of processors_changed() @@ -2025,9 +2355,13 @@ RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr p string name = processor->describe_parameter (*i); + if (name == X_("hidden")) { + continue; + } + 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()) { @@ -2051,6 +2385,10 @@ RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr p mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan)); } + if (items.size() == 0) { + return; + } + /* add the menu for this processor, because the subplugin menu is always cleared at the top of processors_changed(). this is the result of some poor design in gtkmm and/or @@ -2076,7 +2414,7 @@ RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAuto if (pan->view && pan->view->set_marked_for_display (showit)) { redraw = true; } - + if (redraw && !no_redraw) { request_redraw (); } @@ -2150,13 +2488,17 @@ RouteTimeAxisView::can_edit_name () const { /* we do not allow track name changes if it is record enabled */ - return !_route->record_enabled(); + boost::shared_ptr trk (boost::dynamic_pointer_cast (_route)); + if (!trk) { + return true; + } + return !trk->rec_enable_control()->get_value(); } void -RouteTimeAxisView::update_rec_display () +RouteTimeAxisView::blink_rec_display (bool onoff) { - RouteUI::update_rec_display (); + RouteUI::blink_rec_display (onoff); } void @@ -2165,7 +2507,7 @@ RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection) if (_ignore_set_layer_display) { return; } - + if (apply_to_selection) { _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false)); } else { @@ -2224,7 +2566,7 @@ RouteTimeAxisView::show_meter () void RouteTimeAxisView::reset_meter () { - if (Config->get_show_track_meters()) { + if (UIConfiguration::instance().get_show_track_meters()) { int meter_width = 3; if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) { meter_width = 6; @@ -2246,9 +2588,11 @@ RouteTimeAxisView::meter_changed () { ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed) reset_meter(); - if (_route && !no_redraw) { + if (_route && !no_redraw && UIConfiguration::instance().get_show_track_meters()) { request_redraw (); } + // reset peak when meter point changes + gm.reset_peak_display(); } void @@ -2298,7 +2642,7 @@ RouteTimeAxisView::set_underlay_state() continue; } - XMLProperty* prop = child_node->property ("id"); + XMLProperty const * prop = child_node->property ("id"); if (prop) { PBD::ID id (prop->value()); @@ -2325,7 +2669,7 @@ RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/) if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) { if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) { fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg; - /*NOTREACHED*/ + abort(); /*NOTREACHED*/ } _underlay_streams.push_back(v); @@ -2340,7 +2684,7 @@ RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/) } XMLNode* node = underlay_xml_node->add_child("Underlay"); - XMLProperty* prop = node->add_property("id"); + XMLProperty const * prop = node->add_property("id"); prop->set_value(v->trackview().route()->id().to_s()); } #endif @@ -2362,7 +2706,7 @@ RouteTimeAxisView::remove_underlay (StreamView* v) if (gm == other._underlay_mirrors.end()) { fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg; - /*NOTREACHED*/ + abort(); /*NOTREACHED*/ } v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost)); @@ -2379,7 +2723,7 @@ RouteTimeAxisView::remove_underlay (StreamView* v) void RouteTimeAxisView::set_button_names () { - if (_route && _route->solo_safe()) { + if (_route && _route->solo_safe_control()->solo_safe()) { solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive)); } else { solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive)); @@ -2387,19 +2731,19 @@ RouteTimeAxisView::set_button_names () if (Config->get_solo_control_is_listen_control()) { switch (Config->get_listen_position()) { case AfterFaderListen: - solo_button->set_text (_("A")); - ARDOUR_UI::instance()->set_tip (*solo_button, _("After-fade listen (AFL)")); + solo_button->set_text (S_("AfterFader|A")); + set_tooltip (*solo_button, _("After-fade listen (AFL)")); break; case PreFaderListen: - solo_button->set_text (_("P")); - ARDOUR_UI::instance()->set_tip (*solo_button, _("Pre-fade listen (PFL)")); + solo_button->set_text (S_("PreFader|P")); + set_tooltip (*solo_button, _("Pre-fade listen (PFL)")); break; } } else { - solo_button->set_text (_("s")); - ARDOUR_UI::instance()->set_tip (*solo_button, _("Solo")); + solo_button->set_text (S_("Solo|S")); + set_tooltip (*solo_button, _("Solo")); } - mute_button->set_text (_("m")); + mute_button->set_text (S_("Mute|M")); } Gtk::CheckMenuItem* @@ -2442,6 +2786,53 @@ RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show); } +void +RouteTimeAxisView::create_trim_automation_child (const Evoral::Parameter& param, bool show) +{ + boost::shared_ptr c = _route->trim()->gain_control(); + if (!c || ! _route->trim()->active()) { + return; + } + + trim_track.reset (new AutomationTimeAxisView (_session, + _route, _route->trim(), c, param, + _editor, + *this, + false, + parent_canvas, + _route->trim()->describe_parameter(param))); + + if (_view) { + _view->foreach_regionview (sigc::mem_fun (*trim_track.get(), &TimeAxisView::add_ghost)); + } + + add_automation_child (Evoral::Parameter(TrimAutomation), trim_track, show); +} + +void +RouteTimeAxisView::create_mute_automation_child (const Evoral::Parameter& param, bool show) +{ + boost::shared_ptr c = _route->mute_control(); + if (!c) { + error << "Route has no mute automation, unable to add automation track view." << endmsg; + return; + } + + mute_track.reset (new AutomationTimeAxisView (_session, + _route, _route, c, param, + _editor, + *this, + false, + parent_canvas, + _route->describe_parameter(param))); + + if (_view) { + _view->foreach_regionview (sigc::mem_fun (*mute_track.get(), &TimeAxisView::add_ghost)); + } + + add_automation_child (Evoral::Parameter(MuteAutomation), mute_track, show); +} + static void add_region_to_list (RegionView* rv, RegionList* l) { @@ -2522,7 +2913,7 @@ void RouteTimeAxisView::remove_child (boost::shared_ptr c) { TimeAxisView::remove_child (c); - + boost::shared_ptr a = boost::dynamic_pointer_cast (c); if (a) { for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) { @@ -2533,3 +2924,21 @@ RouteTimeAxisView::remove_child (boost::shared_ptr c) } } } + +Gdk::Color +RouteTimeAxisView::color () const +{ + return route_color (); +} + +bool +RouteTimeAxisView::marked_for_display () const +{ + return !_route->presentation_info().hidden(); +} + +bool +RouteTimeAxisView::set_marked_for_display (bool yn) +{ + return RouteUI::mark_hidden (!yn); +}