2 Copyright (C) 2006 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include <sigc++/bind.h>
31 #include "pbd/error.h"
32 #include "pbd/stl_delete.h"
33 #include "pbd/whitespace.h"
34 #include "pbd/memento_command.h"
35 #include "pbd/enumwriter.h"
36 #include "pbd/stateful_diff_command.h"
38 #include <gtkmm/menu.h>
39 #include <gtkmm/menuitem.h>
40 #include <gtkmm2ext/gtk_ui.h>
41 #include <gtkmm2ext/selector.h>
42 #include <gtkmm2ext/bindable_button.h>
43 #include <gtkmm2ext/utils.h>
45 #include "ardour/amp.h"
46 #include "ardour/meter.h"
47 #include "ardour/event_type_map.h"
48 #include "ardour/pannable.h"
49 #include "ardour/panner.h"
50 #include "ardour/processor.h"
51 #include "ardour/profile.h"
52 #include "ardour/route_group.h"
53 #include "ardour/session.h"
54 #include "ardour/session_playlists.h"
56 #include "evoral/Parameter.hpp"
58 #include "canvas/debug.h"
60 #include "ardour_ui.h"
61 #include "ardour_button.h"
62 #include "audio_streamview.h"
64 #include "route_time_axis.h"
65 #include "automation_time_axis.h"
67 #include "gui_thread.h"
68 #include "item_counts.h"
70 #include "paste_context.h"
71 #include "playlist_selector.h"
72 #include "point_selection.h"
74 #include "public_editor.h"
75 #include "region_view.h"
76 #include "rgb_macros.h"
77 #include "selection.h"
78 #include "streamview.h"
80 #include "ui_config.h"
82 #include "route_group_menu.h"
84 #include "ardour/track.h"
88 using namespace ARDOUR;
89 using namespace ARDOUR_UI_UTILS;
91 using namespace Gtkmm2ext;
93 using namespace Editing;
97 RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, ArdourCanvas::Canvas& canvas)
99 , TimeAxisView(sess,ed,(TimeAxisView*) 0, canvas)
101 , parent_canvas (canvas)
103 , button_table (3, 3)
104 , route_group_button (S_("RTAV|G"))
105 , playlist_button (S_("RTAV|P"))
106 , automation_button (S_("RTAV|A"))
107 , automation_action_menu (0)
108 , plugins_submenu_item (0)
109 , route_group_menu (0)
110 , playlist_action_menu (0)
112 , color_mode_menu (0)
113 , gm (sess, true, 75, 14)
114 , _ignore_set_layer_display (false)
115 , gain_automation_item(NULL)
116 , trim_automation_item(NULL)
117 , mute_automation_item(NULL)
118 , pan_automation_item(NULL)
120 number_label.set_name("tracknumber label");
121 number_label.set_elements((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::Text|ArdourButton::Inactive));
122 number_label.set_alignment(.5, .5);
123 number_label.set_fallthrough_to_parent (true);
125 sess->config.ParameterChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::parameter_changed, this, _1), gui_context());
126 UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::parameter_changed));
128 parameter_changed ("editor-stereo-only-meters");
132 RouteTimeAxisView::route_property_changed (const PBD::PropertyChange& what_changed)
134 if (what_changed.contains (ARDOUR::Properties::name)) {
140 RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
142 RouteUI::set_route (rt);
144 CANVAS_DEBUG_NAME (_canvas_display, string_compose ("main for %1", rt->name()));
145 CANVAS_DEBUG_NAME (selection_group, string_compose ("selections for %1", rt->name()));
146 CANVAS_DEBUG_NAME (_ghost_group, string_compose ("ghosts for %1", rt->name()));
149 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
152 gm.set_controls (_route, _route->shared_peak_meter(), _route->amp(), _route->gain_control());
153 gm.get_level_meter().set_no_show_all();
154 gm.get_level_meter().setup_meters(50, meter_width);
155 gm.update_gain_sensitive ();
157 string str = gui_property ("height");
159 set_height (atoi (str));
161 set_height (preset_height (HeightNormal));
164 if (!_route->is_auditioner()) {
165 if (gui_property ("visible").empty()) {
166 set_gui_property ("visible", true);
169 set_gui_property ("visible", false);
172 timestretch_rect = 0;
175 ignore_toggle = false;
177 route_group_button.set_name ("route button");
178 playlist_button.set_name ("route button");
179 automation_button.set_name ("route button");
181 route_group_button.signal_button_press_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
182 playlist_button.signal_button_press_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click), false);
183 automation_button.signal_button_press_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click), false);
187 if (ARDOUR::Profile->get_mixbus()) {
188 controls_table.attach (*rec_enable_button, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
190 controls_table.attach (*rec_enable_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
193 if (is_midi_track()) {
194 set_tooltip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
195 gm.set_fader_name ("MidiTrackFader");
197 set_tooltip(*rec_enable_button, _("Record"));
198 gm.set_fader_name ("AudioTrackFader");
201 /* set playlist button tip to the current playlist, and make it update when it changes */
202 update_playlist_tip ();
203 track()->PlaylistChanged.connect (*this, invalidator (*this), ui_bind(&RouteTimeAxisView::update_playlist_tip, this), gui_context());
206 gm.set_fader_name ("AudioBusFader");
207 Gtk::Fixed *blank = manage(new Gtk::Fixed());
208 controls_button_size_group->add_widget(*blank);
209 if (ARDOUR::Profile->get_mixbus() ) {
210 controls_table.attach (*blank, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
212 controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
217 top_hbox.pack_end(gm.get_level_meter(), false, false, 2);
219 if (!ARDOUR::Profile->get_mixbus()) {
220 controls_meters_size_group->add_widget (gm.get_level_meter());
223 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
224 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
225 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
226 _route->track_number_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::label_view, this), gui_context());
228 if (ARDOUR::Profile->get_mixbus()) {
229 controls_table.attach (*mute_button, 1, 2, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
231 controls_table.attach (*mute_button, 3, 4, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
233 // mute button is always present, it is used to
234 // force the 'blank' placeholders to the proper size
235 controls_button_size_group->add_widget(*mute_button);
237 if (!_route->is_master()) {
238 if (ARDOUR::Profile->get_mixbus()) {
239 controls_table.attach (*solo_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
241 controls_table.attach (*solo_button, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
244 Gtk::Fixed *blank = manage(new Gtk::Fixed());
245 controls_button_size_group->add_widget(*blank);
246 if (ARDOUR::Profile->get_mixbus()) {
247 controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
249 controls_table.attach (*blank, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
254 if (ARDOUR::Profile->get_mixbus()) {
255 controls_table.attach (route_group_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
256 controls_table.attach (gm.get_gain_slider(), 3, 5, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
258 else if (!ARDOUR::Profile->get_trx()) {
259 controls_table.attach (route_group_button, 4, 5, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
260 controls_table.attach (gm.get_gain_slider(), 0, 2, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
263 set_tooltip(*solo_button,_("Solo"));
264 set_tooltip(*mute_button,_("Mute"));
265 set_tooltip(route_group_button, _("Route Group"));
267 mute_button->set_tweaks(ArdourButton::TrackHeader);
268 solo_button->set_tweaks(ArdourButton::TrackHeader);
269 rec_enable_button->set_tweaks(ArdourButton::TrackHeader);
270 playlist_button.set_tweaks(ArdourButton::TrackHeader);
271 automation_button.set_tweaks(ArdourButton::TrackHeader);
272 route_group_button.set_tweaks(ArdourButton::TrackHeader);
274 if (is_midi_track()) {
275 set_tooltip(automation_button, _("MIDI Controllers and Automation"));
277 set_tooltip(automation_button, _("Automation"));
280 update_track_number_visibility();
283 if (ARDOUR::Profile->get_mixbus()) {
284 controls_table.attach (automation_button, 1, 2, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
286 else if (!ARDOUR::Profile->get_trx()) {
287 controls_table.attach (automation_button, 3, 4, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
290 if (is_track() && track()->mode() == ARDOUR::Normal) {
291 if (ARDOUR::Profile->get_mixbus()) {
292 controls_table.attach (playlist_button, 0, 1, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
294 else if (!ARDOUR::Profile->get_trx()) {
295 controls_table.attach (playlist_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
301 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
305 str = gui_property ("layer-display");
307 set_layer_display (LayerDisplay (string_2_enum (str, _view->layer_display ())));
310 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
311 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
313 /* pick up the correct freeze state */
318 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_pixel));
319 UIConfiguration::instance().ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
321 PropertyList* plist = new PropertyList();
323 plist->add (ARDOUR::Properties::group_mute, true);
324 plist->add (ARDOUR::Properties::group_solo, true);
326 route_group_menu = new RouteGroupMenu (_session, plist);
328 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
331 RouteTimeAxisView::~RouteTimeAxisView ()
333 cleanup_gui_properties ();
335 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
339 delete playlist_action_menu;
340 playlist_action_menu = 0;
345 _automation_tracks.clear ();
347 delete route_group_menu;
348 CatchDeletion (this);
352 RouteTimeAxisView::name() const
355 return _route->name();
361 RouteTimeAxisView::post_construct ()
363 /* map current state of the route */
365 update_diskstream_display ();
366 setup_processor_menu_and_curves ();
367 reset_processor_automation_curves ();
370 /** Set up the processor menu for the current set of processors, and
371 * display automation curves for any parameters which have data.
374 RouteTimeAxisView::setup_processor_menu_and_curves ()
376 _subplugin_menu_map.clear ();
377 subplugin_menu.items().clear ();
378 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
379 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
383 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
385 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
386 if (_route->route_group()) {
387 _route->route_group()->remove (_route);
393 r.push_back (route ());
395 route_group_menu->build (r);
396 if (ev->button == 1) {
397 Gtkmm2ext::anchored_menu_popup(route_group_menu->menu(),
401 route_group_menu->menu()->popup (ev->button, ev->time);
408 RouteTimeAxisView::playlist_changed ()
414 RouteTimeAxisView::label_view ()
416 string x = _route->name ();
417 if (x != name_label.get_text ()) {
418 name_label.set_text (x);
420 const int64_t track_number = _route->track_number ();
421 if (track_number == 0) {
422 number_label.set_text ("");
424 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
429 RouteTimeAxisView::update_track_number_visibility ()
432 bool show_label = _session->config.get_track_name_number();
434 if (_route && _route->is_master()) {
438 if (number_label.get_parent()) {
439 controls_table.remove (number_label);
442 if (ARDOUR::Profile->get_mixbus()) {
443 controls_table.attach (number_label, 3, 4, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
445 controls_table.attach (number_label, 0, 1, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
447 // see ArdourButton::on_size_request(), we should probably use a global size-group here instead.
448 // except the width of the number label is subtracted from the name-hbox, so we
449 // need to explictly calculate it anyway until the name-label & entry become ArdourWidgets.
450 int tnw = (2 + std::max(2u, _session->track_number_decimals())) * number_label.char_pixel_width();
452 number_label.set_size_request(tnw, -1);
453 number_label.show ();
455 number_label.hide ();
460 RouteTimeAxisView::parameter_changed (string const & p)
462 if (p == "track-name-number") {
463 update_track_number_visibility();
464 } else if (p == "editor-stereo-only-meters") {
465 if (UIConfiguration::instance().get_editor_stereo_only_meters()) {
466 gm.get_level_meter().set_max_audio_meter_count (2);
468 gm.get_level_meter().set_max_audio_meter_count (0);
474 RouteTimeAxisView::take_name_changed (void *src)
482 RouteTimeAxisView::playlist_click (GdkEventButton *ev)
484 if (ev->button != 1) {
488 build_playlist_menu ();
489 conditionally_add_to_selection ();
490 Gtkmm2ext::anchored_menu_popup(playlist_action_menu, &playlist_button,
496 RouteTimeAxisView::automation_click (GdkEventButton *ev)
498 if (ev->button != 1) {
502 conditionally_add_to_selection ();
503 build_automation_action_menu (false);
504 Gtkmm2ext::anchored_menu_popup(automation_action_menu, &automation_button,
510 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
512 using namespace Menu_Helpers;
514 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
515 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
518 detach_menu (subplugin_menu);
520 _main_automation_menu_map.clear ();
521 delete automation_action_menu;
522 automation_action_menu = new Menu;
524 MenuList& items = automation_action_menu->items();
526 automation_action_menu->set_name ("ArdourContextMenu");
528 items.push_back (MenuElem (_("Show All Automation"),
529 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
531 items.push_back (MenuElem (_("Show Existing Automation"),
532 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
534 items.push_back (MenuElem (_("Hide All Automation"),
535 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
537 /* Attach the plugin submenu. It may have previously been used elsewhere,
538 so it was detached above
541 if (!subplugin_menu.items().empty()) {
542 items.push_back (SeparatorElem ());
543 items.push_back (MenuElem (_("Processor automation"), subplugin_menu));
544 items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);;
547 /* Add any route automation */
550 items.push_back (CheckMenuElem (_("Fader"), sigc::mem_fun (*this, &RouteTimeAxisView::update_gain_track_visibility)));
551 gain_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
552 gain_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
553 (gain_track && string_is_affirmative (gain_track->gui_property ("visible"))));
555 _main_automation_menu_map[Evoral::Parameter(GainAutomation)] = gain_automation_item;
559 items.push_back (CheckMenuElem (_("Trim"), sigc::mem_fun (*this, &RouteTimeAxisView::update_trim_track_visibility)));
560 trim_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
561 trim_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
562 (trim_track && string_is_affirmative (trim_track->gui_property ("visible"))));
564 _main_automation_menu_map[Evoral::Parameter(TrimAutomation)] = trim_automation_item;
568 items.push_back (CheckMenuElem (_("Mute"), sigc::mem_fun (*this, &RouteTimeAxisView::update_mute_track_visibility)));
569 mute_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
570 mute_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
571 (mute_track && string_is_affirmative (mute_track->gui_property ("visible"))));
573 _main_automation_menu_map[Evoral::Parameter(MuteAutomation)] = mute_automation_item;
576 if (!pan_tracks.empty()) {
577 items.push_back (CheckMenuElem (_("Pan"), sigc::mem_fun (*this, &RouteTimeAxisView::update_pan_track_visibility)));
578 pan_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
579 pan_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
580 (!pan_tracks.empty() && string_is_affirmative (pan_tracks.front()->gui_property ("visible"))));
582 set<Evoral::Parameter> const & params = _route->pannable()->what_can_be_automated ();
583 for (set<Evoral::Parameter>::const_iterator p = params.begin(); p != params.end(); ++p) {
584 _main_automation_menu_map[*p] = pan_automation_item;
590 RouteTimeAxisView::build_display_menu ()
592 using namespace Menu_Helpers;
596 TimeAxisView::build_display_menu ();
598 /* now fill it with our stuff */
600 MenuList& items = display_menu->items();
601 display_menu->set_name ("ArdourContextMenu");
603 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
605 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
607 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
609 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
611 items.push_back (SeparatorElem());
614 detach_menu (*_size_menu);
617 items.push_back (MenuElem (_("Height"), *_size_menu));
618 items.push_back (SeparatorElem());
620 // Hook for derived classes to add type specific stuff
621 append_extra_display_menu_items ();
625 Menu* layers_menu = manage (new Menu);
626 MenuList &layers_items = layers_menu->items();
627 layers_menu->set_name("ArdourContextMenu");
629 RadioMenuItem::Group layers_group;
631 /* Find out how many overlaid/stacked tracks we have in the selection */
635 TrackSelection const & s = _editor.get_selection().tracks;
636 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
637 StreamView* v = (*i)->view ();
642 switch (v->layer_display ()) {
653 /* We're not connecting to signal_toggled() here; in the case where these two items are
654 set to be in the `inconsistent' state, it seems that one or other will end up active
655 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
656 select the active one, no toggled signal is emitted so nothing happens.
659 _ignore_set_layer_display = true;
661 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
662 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
663 i->set_active (overlaid != 0 && stacked == 0);
664 i->set_inconsistent (overlaid != 0 && stacked != 0);
665 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
667 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
668 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
669 i->set_active (overlaid == 0 && stacked != 0);
670 i->set_inconsistent (overlaid != 0 && stacked != 0);
671 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
673 _ignore_set_layer_display = false;
675 items.push_back (MenuElem (_("Layers"), *layers_menu));
677 Menu* alignment_menu = manage (new Menu);
678 MenuList& alignment_items = alignment_menu->items();
679 alignment_menu->set_name ("ArdourContextMenu");
681 RadioMenuItem::Group align_group;
683 /* Same verbose hacks as for the layering options above */
689 boost::shared_ptr<Track> first_track;
691 for (TrackSelection::const_iterator t = s.begin(); t != s.end(); ++t) {
692 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*t);
693 if (!r || !r->is_track ()) {
698 first_track = r->track();
701 switch (r->track()->alignment_choice()) {
705 switch (r->track()->alignment_style()) {
706 case ExistingMaterial:
714 case UseExistingMaterial:
730 inconsistent = false;
737 if (!inconsistent && first_track) {
739 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
740 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
741 i->set_active (automatic != 0 && existing == 0 && capture == 0);
742 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
744 switch (first_track->alignment_choice()) {
746 switch (first_track->alignment_style()) {
747 case ExistingMaterial:
748 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
751 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
759 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
760 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
761 i->set_active (existing != 0 && capture == 0 && automatic == 0);
762 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
764 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
765 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
766 i->set_active (existing == 0 && capture != 0 && automatic == 0);
767 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
769 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
775 #ifdef XXX_OLD_DESTRUCTIVE_API_XXX
776 Menu* mode_menu = manage (new Menu);
777 MenuList& mode_items = mode_menu->items ();
778 mode_menu->set_name ("ArdourContextMenu");
780 RadioMenuItem::Group mode_group;
786 for (TrackSelection::const_iterator t = s.begin(); t != s.end(); ++t) {
787 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*t);
788 if (!r || !r->is_track ()) {
792 switch (r->track()->mode()) {
805 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
806 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
807 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
808 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
809 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
811 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
812 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
813 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
814 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
815 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
817 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
818 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
819 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
820 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
821 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
823 items.push_back (MenuElem (_("Record Mode"), *mode_menu));
826 items.push_back (SeparatorElem());
828 build_playlist_menu ();
829 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
830 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
833 route_group_menu->detach ();
836 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
837 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
839 r.push_back (rtv->route ());
844 r.push_back (route ());
847 route_group_menu->build (r);
848 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
850 build_automation_action_menu (true);
851 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
853 items.push_back (SeparatorElem());
855 if (is_midi_track()) {
856 Menu* midi_menu = manage (new Menu);
857 MenuList& midi_items = midi_menu->items();
858 midi_menu->set_name (X_("ArdourContextMenu"));
860 midi_items.push_back (MenuElem (_("Channel Management"), sigc::mem_fun (*this, &RouteTimeAxisView::toggle_channel_selector)));
862 items.push_back (MenuElem (_("MIDI"), *midi_menu));
863 items.push_back (SeparatorElem());
868 TrackSelection const & s = _editor.get_selection().tracks;
869 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
870 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
875 if (r->route()->active()) {
882 items.push_back (CheckMenuElem (_("Active")));
883 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
884 bool click_sets_active = true;
885 if (active > 0 && inactive == 0) {
886 i->set_active (true);
887 click_sets_active = false;
888 } else if (active > 0 && inactive > 0) {
889 i->set_inconsistent (true);
891 i->set_sensitive(! _session->transport_rolling());
892 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
894 items.push_back (SeparatorElem());
895 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
896 if (_route && !_route->is_master()) {
897 items.push_back (SeparatorElem());
898 items.push_back (MenuElem (_("Duplicate..."), boost::bind (&ARDOUR_UI::start_duplicate_routes, ARDOUR_UI::instance())));
900 items.push_back (SeparatorElem());
901 items.push_back (MenuElem (_("Remove"), sigc::mem_fun(_editor, &PublicEditor::remove_tracks)));
904 #ifdef XXX_OLD_DESTRUCTIVE_API_XXX
906 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
908 if (apply_to_selection) {
909 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
912 bool needs_bounce = false;
914 if (!track()->can_use_mode (mode, needs_bounce)) {
920 cerr << "would bounce this one\n";
925 track()->set_mode (mode);
931 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
933 TimeAxisView::show_timestretch (start, end, layers, layer);
943 /* check that the time selection was made in our route, or our route group.
944 remember that route_group() == 0 implies the route is *not* in a edit group.
947 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
948 /* this doesn't apply to us */
952 /* ignore it if our edit group is not active */
954 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
959 if (timestretch_rect == 0) {
960 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
961 timestretch_rect->set_fill_color (ArdourCanvas::HSV (UIConfiguration::instance().color ("time stretch fill")).mod (UIConfiguration::instance().modifier ("time stretch fill")).color());
962 timestretch_rect->set_outline_color (UIConfiguration::instance().color ("time stretch outline"));
965 timestretch_rect->show ();
966 timestretch_rect->raise_to_top ();
968 double const x1 = start / _editor.get_current_zoom();
969 double const x2 = (end - 1) / _editor.get_current_zoom();
971 timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
972 x2, current_height() * (layers - layer) / layers));
976 RouteTimeAxisView::hide_timestretch ()
978 TimeAxisView::hide_timestretch ();
980 if (timestretch_rect) {
981 timestretch_rect->hide ();
986 RouteTimeAxisView::show_selection (TimeSelection& ts)
990 /* ignore it if our edit group is not active or if the selection was started
991 in some other track or route group (remember that route_group() == 0 means
992 that the track is not in an route group).
995 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
996 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
1002 TimeAxisView::show_selection (ts);
1006 RouteTimeAxisView::set_height (uint32_t h, TrackHeightMode m)
1009 bool height_changed = (height == 0) || (h != height);
1011 int meter_width = 3;
1012 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
1015 gm.get_level_meter().setup_meters (gmlen, meter_width);
1017 TimeAxisView::set_height (h, m);
1020 _view->set_height ((double) current_height());
1023 if (height >= preset_height (HeightNormal)) {
1027 gm.get_gain_slider().show();
1028 mute_button->show();
1029 if (!_route || _route->is_monitor()) {
1030 solo_button->hide();
1032 solo_button->show();
1034 if (rec_enable_button)
1035 rec_enable_button->show();
1037 route_group_button.show();
1038 automation_button.show();
1040 if (is_track() && track()->mode() == ARDOUR::Normal) {
1041 playlist_button.show();
1048 gm.get_gain_slider().hide();
1049 mute_button->show();
1050 if (!_route || _route->is_monitor()) {
1051 solo_button->hide();
1053 solo_button->show();
1055 if (rec_enable_button)
1056 rec_enable_button->show();
1058 route_group_button.hide ();
1059 automation_button.hide ();
1061 if (is_track() && track()->mode() == ARDOUR::Normal) {
1062 playlist_button.hide ();
1067 if (height_changed && !no_redraw) {
1068 /* only emit the signal if the height really changed */
1074 RouteTimeAxisView::route_color_changed ()
1077 _view->apply_color (color(), StreamView::RegionColor);
1080 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1084 RouteTimeAxisView::reset_samples_per_pixel ()
1086 set_samples_per_pixel (_editor.get_current_zoom());
1090 RouteTimeAxisView::set_samples_per_pixel (double fpp)
1095 speed = track()->speed();
1099 _view->set_samples_per_pixel (fpp * speed);
1102 TimeAxisView::set_samples_per_pixel (fpp * speed);
1106 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
1108 if (!mitem->get_active()) {
1109 /* this is one of the two calls made when these radio menu items change status. this one
1110 is for the item that became inactive, and we want to ignore it.
1115 if (apply_to_selection) {
1116 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
1119 track()->set_align_choice (choice);
1125 RouteTimeAxisView::rename_current_playlist ()
1127 ArdourPrompter prompter (true);
1130 boost::shared_ptr<Track> tr = track();
1131 if (!tr || tr->destructive()) {
1135 boost::shared_ptr<Playlist> pl = tr->playlist();
1140 prompter.set_title (_("Rename Playlist"));
1141 prompter.set_prompt (_("New name for playlist:"));
1142 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1143 prompter.set_initial_text (pl->name());
1144 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1147 if (prompter.run () != Gtk::RESPONSE_ACCEPT) {
1150 prompter.get_result (name);
1151 if (name.length()) {
1152 if (_session->playlists->by_name (name)) {
1153 MessageDialog msg (_("Given playlist name is not unique."));
1155 prompter.set_initial_text (Playlist::bump_name (name, *_session));
1157 pl->set_name (name);
1165 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1167 std::string ret (basename);
1169 std::string const group_string = "." + route_group()->name() + ".";
1171 // iterate through all playlists
1173 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1174 std::string tmp = (*i)->name();
1176 std::string::size_type idx = tmp.find(group_string);
1177 // find those which belong to this group
1178 if (idx != string::npos) {
1179 tmp = tmp.substr(idx + group_string.length());
1181 // and find the largest current number
1183 if (x > maxnumber) {
1192 snprintf (buf, sizeof(buf), "%d", maxnumber);
1194 ret = this->name() + "." + route_group()->name () + "." + buf;
1200 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op, bool copy)
1204 boost::shared_ptr<Track> tr = track ();
1205 if (!tr || tr->destructive()) {
1209 boost::shared_ptr<const Playlist> pl = tr->playlist();
1216 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1217 name = resolve_new_group_playlist_name(name,playlists_before_op);
1220 while (_session->playlists->by_name(name)) {
1221 name = Playlist::bump_name (name, *_session);
1225 // TODO: The prompter "new" button should be de-activated if the user
1226 // specifies a playlist name which already exists in the session.
1228 ArdourPrompter prompter (true);
1231 prompter.set_title (_("New Copy Playlist"));
1232 prompter.set_prompt (_("Name for playlist copy:"));
1234 prompter.set_title (_("New Playlist"));
1235 prompter.set_prompt (_("Name for new playlist:"));
1237 prompter.set_initial_text (name);
1238 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1239 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1240 prompter.show_all ();
1243 if (prompter.run () != Gtk::RESPONSE_ACCEPT) {
1246 prompter.get_result (name);
1247 if (name.length()) {
1248 if (_session->playlists->by_name (name)) {
1249 MessageDialog msg (_("Given playlist name is not unique."));
1251 prompter.set_initial_text (Playlist::bump_name (name, *_session));
1259 if (name.length()) {
1261 tr->use_copy_playlist ();
1263 tr->use_new_playlist ();
1265 tr->playlist()->set_name (name);
1270 RouteTimeAxisView::clear_playlist ()
1272 boost::shared_ptr<Track> tr = track ();
1273 if (!tr || tr->destructive()) {
1277 boost::shared_ptr<Playlist> pl = tr->playlist();
1282 _editor.clear_playlist (pl);
1286 RouteTimeAxisView::speed_changed ()
1288 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1292 RouteTimeAxisView::update_diskstream_display ()
1302 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1304 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1306 /* special case: select/deselect all tracks */
1308 _editor.begin_reversible_selection_op (X_("Selection Click"));
1310 if (_editor.get_selection().selected (this)) {
1311 _editor.get_selection().clear_tracks ();
1313 _editor.select_all_tracks ();
1316 _editor.commit_reversible_selection_op ();
1321 _editor.begin_reversible_selection_op (X_("Selection Click"));
1323 switch (ArdourKeyboard::selection_type (ev->state)) {
1324 case Selection::Toggle:
1325 _editor.get_selection().toggle (this);
1328 case Selection::Set:
1329 _editor.get_selection().set (this);
1332 case Selection::Extend:
1333 _editor.extend_selection_to_track (*this);
1336 case Selection::Add:
1337 _editor.get_selection().add (this);
1341 _editor.commit_reversible_selection_op ();
1345 RouteTimeAxisView::set_selected_points (PointSelection& points)
1347 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1348 (*i)->set_selected_points (points);
1350 AudioStreamView* asv = dynamic_cast<AudioStreamView*>(_view);
1352 asv->set_selected_points (points);
1357 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1360 _view->set_selected_regionviews (regions);
1364 /** Add the selectable things that we have to a list.
1365 * @param results List to add things to.
1368 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results, bool within)
1373 speed = track()->speed();
1376 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1377 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1379 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1380 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results, within);
1383 /* pick up visible automation tracks */
1385 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1386 if (!(*i)->hidden()) {
1387 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results, within);
1393 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1396 _view->get_inverted_selectables (sel, results);
1399 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1400 if (!(*i)->hidden()) {
1401 (*i)->get_inverted_selectables (sel, results);
1409 RouteTimeAxisView::route_group () const
1411 return _route->route_group();
1414 boost::shared_ptr<Playlist>
1415 RouteTimeAxisView::playlist () const
1417 boost::shared_ptr<Track> tr;
1419 if ((tr = track()) != 0) {
1420 return tr->playlist();
1422 return boost::shared_ptr<Playlist> ();
1427 RouteTimeAxisView::name_entry_changed (string const& str)
1429 if (str == _route->name()) {
1435 strip_whitespace_edges (x);
1441 if (_session->route_name_internal (x)) {
1442 ARDOUR_UI::instance()->popup_error (string_compose (_("The name \"%1\" is reserved for %2"), x, PROGRAM_NAME));
1444 } else if (RouteUI::verify_new_route_name (x)) {
1445 _route->set_name (x);
1452 boost::shared_ptr<Region>
1453 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1455 boost::shared_ptr<Playlist> pl = playlist ();
1458 return pl->find_next_region (pos, point, dir);
1461 return boost::shared_ptr<Region> ();
1465 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1467 boost::shared_ptr<Playlist> pl = playlist ();
1470 return pl->find_next_region_boundary (pos, dir);
1477 RouteTimeAxisView::fade_range (TimeSelection& selection)
1479 boost::shared_ptr<Playlist> what_we_got;
1480 boost::shared_ptr<Track> tr = track ();
1481 boost::shared_ptr<Playlist> playlist;
1484 /* route is a bus, not a track */
1488 playlist = tr->playlist();
1490 TimeSelection time (selection);
1491 float const speed = tr->speed();
1492 if (speed != 1.0f) {
1493 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1494 (*i).start = session_frame_to_track_frame((*i).start, speed);
1495 (*i).end = session_frame_to_track_frame((*i).end, speed);
1499 playlist->clear_changes ();
1500 playlist->clear_owned_changes ();
1502 playlist->fade_range (time);
1504 vector<Command*> cmds;
1505 playlist->rdiff (cmds);
1506 _session->add_commands (cmds);
1507 _session->add_command (new StatefulDiffCommand (playlist));
1512 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1514 boost::shared_ptr<Playlist> what_we_got;
1515 boost::shared_ptr<Track> tr = track ();
1516 boost::shared_ptr<Playlist> playlist;
1519 /* route is a bus, not a track */
1523 playlist = tr->playlist();
1525 TimeSelection time (selection.time);
1526 float const speed = tr->speed();
1527 if (speed != 1.0f) {
1528 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1529 (*i).start = session_frame_to_track_frame((*i).start, speed);
1530 (*i).end = session_frame_to_track_frame((*i).end, speed);
1534 playlist->clear_changes ();
1535 playlist->clear_owned_changes ();
1539 if (playlist->cut (time) != 0) {
1540 if (Config->get_edit_mode() == Ripple)
1541 playlist->ripple(time.start(), -time.length(), NULL);
1542 // no need to exclude any regions from rippling here
1544 vector<Command*> cmds;
1545 playlist->rdiff (cmds);
1546 _session->add_commands (cmds);
1548 _session->add_command (new StatefulDiffCommand (playlist));
1553 if ((what_we_got = playlist->cut (time)) != 0) {
1554 _editor.get_cut_buffer().add (what_we_got);
1555 if (Config->get_edit_mode() == Ripple)
1556 playlist->ripple(time.start(), -time.length(), NULL);
1557 // no need to exclude any regions from rippling here
1559 vector<Command*> cmds;
1560 playlist->rdiff (cmds);
1561 _session->add_commands (cmds);
1563 _session->add_command (new StatefulDiffCommand (playlist));
1567 if ((what_we_got = playlist->copy (time)) != 0) {
1568 _editor.get_cut_buffer().add (what_we_got);
1573 if ((what_we_got = playlist->cut (time)) != 0) {
1574 if (Config->get_edit_mode() == Ripple)
1575 playlist->ripple(time.start(), -time.length(), NULL);
1576 // no need to exclude any regions from rippling here
1578 vector<Command*> cmds;
1579 playlist->rdiff (cmds);
1580 _session->add_commands (cmds);
1581 _session->add_command (new StatefulDiffCommand (playlist));
1582 what_we_got->release ();
1589 RouteTimeAxisView::paste (framepos_t pos, const Selection& selection, PasteContext& ctx, const int32_t sub_num)
1595 boost::shared_ptr<Playlist> pl = playlist ();
1596 const ARDOUR::DataType type = pl->data_type();
1597 PlaylistSelection::const_iterator p = selection.playlists.get_nth(type, ctx.counts.n_playlists(type));
1599 if (p == selection.playlists.end()) {
1602 ctx.counts.increase_n_playlists(type);
1604 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1606 if (track()->speed() != 1.0f) {
1607 pos = session_frame_to_track_frame (pos, track()->speed());
1608 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1611 /* add multi-paste offset if applicable */
1612 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent();
1613 const framecnt_t duration = extent.second - extent.first;
1614 pos += _editor.get_paste_offset(pos, ctx.count, duration);
1616 pl->clear_changes ();
1617 pl->clear_owned_changes ();
1618 if (Config->get_edit_mode() == Ripple) {
1619 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent_with_endspace();
1620 framecnt_t amount = extent.second - extent.first;
1621 pl->ripple(pos, amount * ctx.times, boost::shared_ptr<Region>());
1623 pl->paste (*p, pos, ctx.times, sub_num);
1625 vector<Command*> cmds;
1627 _session->add_commands (cmds);
1629 _session->add_command (new StatefulDiffCommand (pl));
1635 struct PlaylistSorter {
1636 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1637 return a->sort_id() < b->sort_id();
1642 RouteTimeAxisView::build_playlist_menu ()
1644 using namespace Menu_Helpers;
1650 delete playlist_action_menu;
1651 playlist_action_menu = new Menu;
1652 playlist_action_menu->set_name ("ArdourContextMenu");
1654 MenuList& playlist_items = playlist_action_menu->items();
1655 playlist_action_menu->set_name ("ArdourContextMenu");
1656 playlist_items.clear();
1658 RadioMenuItem::Group playlist_group;
1659 boost::shared_ptr<Track> tr = track ();
1661 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1663 /* sort the playlists */
1665 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1667 /* add the playlists to the menu */
1668 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1669 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1670 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1671 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1673 if (tr->playlist()->id() == (*i)->id()) {
1679 playlist_items.push_back (SeparatorElem());
1680 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1681 playlist_items.push_back (SeparatorElem());
1683 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1684 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1685 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1688 // Use a label which tells the user what is happening
1689 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1690 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1694 playlist_items.push_back (SeparatorElem());
1695 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1696 playlist_items.push_back (SeparatorElem());
1698 playlist_items.push_back (MenuElem(_("Select from All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1702 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1704 assert (is_track());
1706 // exit if we were triggered by deactivating the old playlist
1707 if (!item->get_active()) {
1711 boost::shared_ptr<Playlist> pl (wpl.lock());
1717 if (track()->playlist() == pl) {
1718 // exit when use_playlist is called by the creation of the playlist menu
1719 // or the playlist choice is unchanged
1723 track()->use_playlist (pl);
1725 RouteGroup* rg = route_group();
1727 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1728 std::string group_string = "." + rg->name() + ".";
1730 std::string take_name = pl->name();
1731 std::string::size_type idx = take_name.find(group_string);
1733 if (idx == std::string::npos)
1736 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1738 boost::shared_ptr<RouteList> rl (rg->route_list());
1740 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1741 if ((*i) == this->route()) {
1745 std::string playlist_name = (*i)->name()+group_string+take_name;
1747 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1752 if (track->freeze_state() == Track::Frozen) {
1753 /* Don't change playlists of frozen tracks */
1757 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1759 // No playlist for this track for this take yet, make it
1760 track->use_new_playlist();
1761 track->playlist()->set_name(playlist_name);
1763 track->use_playlist(ipl);
1770 RouteTimeAxisView::update_playlist_tip ()
1772 RouteGroup* rg = route_group ();
1773 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1774 string group_string = "." + rg->name() + ".";
1776 string take_name = track()->playlist()->name();
1777 string::size_type idx = take_name.find(group_string);
1779 if (idx != string::npos) {
1780 /* find the bit containing the take number / name */
1781 take_name = take_name.substr (idx + group_string.length());
1783 /* set the playlist button tooltip to the take name */
1786 string_compose(_("Take: %1.%2"),
1787 Gtkmm2ext::markup_escape_text (rg->name()),
1788 Gtkmm2ext::markup_escape_text (take_name))
1795 /* set the playlist button tooltip to the playlist name */
1796 set_tooltip (playlist_button, _("Playlist") + std::string(": ") + Gtkmm2ext::markup_escape_text (track()->playlist()->name()));
1801 RouteTimeAxisView::show_playlist_selector ()
1803 _editor.playlist_selector().show_for (this);
1807 RouteTimeAxisView::map_frozen ()
1813 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1815 switch (track()->freeze_state()) {
1817 playlist_button.set_sensitive (false);
1820 playlist_button.set_sensitive (true);
1823 RouteUI::map_frozen ();
1827 RouteTimeAxisView::color_handler ()
1829 //case cTimeStretchOutline:
1830 if (timestretch_rect) {
1831 timestretch_rect->set_outline_color (UIConfiguration::instance().color ("time stretch outline"));
1833 //case cTimeStretchFill:
1834 if (timestretch_rect) {
1835 timestretch_rect->set_fill_color (UIConfiguration::instance().color ("time stretch fill"));
1841 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1842 * Will add track if necessary.
1845 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1847 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1848 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1851 /* it doesn't exist yet, so we don't care about the button state: just add it */
1852 create_automation_child (param, true);
1855 bool yn = menu->get_active();
1856 bool changed = false;
1858 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1860 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1861 will have done that for us.
1864 if (changed && !no_redraw) {
1872 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1874 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1880 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1882 if (menu && !_hidden) {
1883 ignore_toggle = true;
1884 menu->set_active (false);
1885 ignore_toggle = false;
1888 if (_route && !no_redraw) {
1894 RouteTimeAxisView::update_gain_track_visibility ()
1896 bool const showit = gain_automation_item->get_active();
1898 if (showit != string_is_affirmative (gain_track->gui_property ("visible"))) {
1899 gain_track->set_marked_for_display (showit);
1901 /* now trigger a redisplay */
1904 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1910 RouteTimeAxisView::update_trim_track_visibility ()
1912 bool const showit = trim_automation_item->get_active();
1914 if (showit != string_is_affirmative (trim_track->gui_property ("visible"))) {
1915 trim_track->set_marked_for_display (showit);
1917 /* now trigger a redisplay */
1920 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1926 RouteTimeAxisView::update_mute_track_visibility ()
1928 bool const showit = mute_automation_item->get_active();
1930 if (showit != string_is_affirmative (mute_track->gui_property ("visible"))) {
1931 mute_track->set_marked_for_display (showit);
1933 /* now trigger a redisplay */
1936 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1942 RouteTimeAxisView::update_pan_track_visibility ()
1944 bool const showit = pan_automation_item->get_active();
1945 bool changed = false;
1947 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1948 if ((*i)->set_marked_for_display (showit)) {
1954 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1959 RouteTimeAxisView::ensure_pan_views (bool show)
1961 bool changed = false;
1962 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1964 (*i)->set_marked_for_display (false);
1967 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1971 if (!_route->panner()) {
1975 set<Evoral::Parameter> params = _route->panner()->what_can_be_automated();
1976 set<Evoral::Parameter>::iterator p;
1978 for (p = params.begin(); p != params.end(); ++p) {
1979 boost::shared_ptr<ARDOUR::AutomationControl> pan_control = _route->pannable()->automation_control(*p);
1981 if (pan_control->parameter().type() == NullAutomation) {
1982 error << "Pan control has NULL automation type!" << endmsg;
1986 if (automation_child (pan_control->parameter ()).get () == 0) {
1988 /* we don't already have an AutomationTimeAxisView for this parameter */
1990 std::string const name = _route->panner()->describe_parameter (pan_control->parameter ());
1992 boost::shared_ptr<AutomationTimeAxisView> t (
1993 new AutomationTimeAxisView (_session,
1997 pan_control->parameter (),
2005 pan_tracks.push_back (t);
2006 add_automation_child (*p, t, show);
2008 pan_tracks.push_back (automation_child (pan_control->parameter ()));
2015 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
2017 if (apply_to_selection) {
2018 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
2022 /* Show our automation */
2024 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2025 i->second->set_marked_for_display (true);
2027 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2030 menu->set_active(true);
2035 /* Show processor automation */
2037 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2038 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2039 if ((*ii)->view == 0) {
2040 add_processor_automation_curve ((*i)->processor, (*ii)->what);
2043 (*ii)->menu_item->set_active (true);
2056 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
2058 if (apply_to_selection) {
2059 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
2063 /* Show our automation */
2065 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2066 if (i->second->has_automation()) {
2067 i->second->set_marked_for_display (true);
2069 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2071 menu->set_active(true);
2076 /* Show processor automation */
2078 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2079 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2080 if ((*i)->processor->control((*ii)->what)->list()->size() > 0) {
2081 (*ii)->menu_item->set_active (true);
2093 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
2095 if (apply_to_selection) {
2096 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
2100 /* Hide our automation */
2102 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2103 i->second->set_marked_for_display (false);
2105 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2108 menu->set_active (false);
2112 /* Hide processor automation */
2114 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2115 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2116 (*ii)->menu_item->set_active (false);
2127 RouteTimeAxisView::region_view_added (RegionView* rv)
2129 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
2130 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
2131 boost::shared_ptr<AutomationTimeAxisView> atv;
2133 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
2138 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
2139 (*i)->add_ghost(rv);
2143 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
2145 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
2151 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
2153 parent.remove_processor_automation_node (this);
2157 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
2160 remove_child (pan->view);
2164 RouteTimeAxisView::ProcessorAutomationNode*
2165 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2167 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2169 if ((*i)->processor == processor) {
2171 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2172 if ((*ii)->what == what) {
2182 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
2184 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2187 ProcessorAutomationNode* pan;
2189 if ((pan = find_processor_automation_node (processor, what)) == 0) {
2190 /* session state may never have been saved with new plugin */
2191 error << _("programming error: ")
2192 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
2193 processor->name(), what.type(), (int) what.channel(), what.id() )
2195 abort(); /*NOTREACHED*/
2203 boost::shared_ptr<AutomationControl> control
2204 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
2206 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
2207 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
2208 _editor, *this, false, parent_canvas,
2209 processor->describe_parameter (what), processor->name()));
2211 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
2213 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
2216 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
2221 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
2224 pan->menu_item->set_active (false);
2233 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
2235 boost::shared_ptr<Processor> processor (p.lock ());
2237 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
2238 /* The Amp processor is a special case and is dealt with separately */
2242 set<Evoral::Parameter> existing;
2244 processor->what_has_data (existing);
2246 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
2248 Evoral::Parameter param (*i);
2249 boost::shared_ptr<AutomationLine> al;
2251 if ((al = find_processor_automation_curve (processor, param)) != 0) {
2254 add_processor_automation_curve (processor, param);
2260 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
2262 using namespace Menu_Helpers;
2266 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
2268 _automation_tracks[param] = track;
2270 /* existing state overrides "show" argument */
2271 string s = track->gui_property ("visible");
2273 show = string_is_affirmative (s);
2276 /* this might or might not change the visibility status, so don't rely on it */
2277 track->set_marked_for_display (show);
2279 if (show && !no_redraw) {
2283 if (!ARDOUR::parameter_is_midi((AutomationType)param.type())) {
2284 /* MIDI-related parameters are always in the menu, there's no
2285 reason to rebuild the menu just because we added a automation
2286 lane for one of them. But if we add a non-MIDI automation
2287 lane, then we need to invalidate the display menu.
2289 delete display_menu;
2295 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2297 boost::shared_ptr<Processor> processor (p.lock ());
2299 if (!processor || !processor->display_to_user ()) {
2303 /* we use this override to veto the Amp processor from the plugin menu,
2304 as its automation lane can be accessed using the special "Fader" menu
2308 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
2312 using namespace Menu_Helpers;
2313 ProcessorAutomationInfo *rai;
2314 list<ProcessorAutomationInfo*>::iterator x;
2316 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2318 if (automatable.empty()) {
2322 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2323 if ((*x)->processor == processor) {
2328 if (x == processor_automation.end()) {
2329 rai = new ProcessorAutomationInfo (processor);
2330 processor_automation.push_back (rai);
2335 /* any older menu was deleted at the top of processors_changed()
2336 when we cleared the subplugin menu.
2339 rai->menu = manage (new Menu);
2340 MenuList& items = rai->menu->items();
2341 rai->menu->set_name ("ArdourContextMenu");
2345 std::set<Evoral::Parameter> has_visible_automation;
2346 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2348 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2350 ProcessorAutomationNode* pan;
2351 Gtk::CheckMenuItem* mitem;
2353 string name = processor->describe_parameter (*i);
2355 if (name == X_("hidden")) {
2359 items.push_back (CheckMenuElem (name));
2360 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2362 _subplugin_menu_map[*i] = mitem;
2364 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2365 mitem->set_active(true);
2368 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2372 pan = new ProcessorAutomationNode (*i, mitem, *this);
2374 rai->lines.push_back (pan);
2378 pan->menu_item = mitem;
2382 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2385 if (items.size() == 0) {
2389 /* add the menu for this processor, because the subplugin
2390 menu is always cleared at the top of processors_changed().
2391 this is the result of some poor design in gtkmm and/or
2395 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2400 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2401 RouteTimeAxisView::ProcessorAutomationNode* pan)
2403 bool showit = pan->menu_item->get_active();
2404 bool redraw = false;
2406 if (pan->view == 0 && showit) {
2407 add_processor_automation_curve (rai->processor, pan->what);
2411 if (pan->view && pan->view->set_marked_for_display (showit)) {
2415 if (redraw && !no_redraw) {
2421 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2423 if (c.type == RouteProcessorChange::MeterPointChange) {
2424 /* nothing to do if only the meter point has changed */
2428 using namespace Menu_Helpers;
2430 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2431 (*i)->valid = false;
2434 setup_processor_menu_and_curves ();
2436 bool deleted_processor_automation = false;
2438 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2440 list<ProcessorAutomationInfo*>::iterator tmp;
2448 processor_automation.erase (i);
2449 deleted_processor_automation = true;
2456 if (deleted_processor_automation && !no_redraw) {
2461 boost::shared_ptr<AutomationLine>
2462 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2464 ProcessorAutomationNode* pan;
2466 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2472 return boost::shared_ptr<AutomationLine>();
2476 RouteTimeAxisView::reset_processor_automation_curves ()
2478 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2484 RouteTimeAxisView::can_edit_name () const
2486 /* we do not allow track name changes if it is record enabled
2488 boost::shared_ptr<Track> trk (boost::dynamic_pointer_cast<Track> (_route));
2492 return !trk->rec_enable_control()->get_value();
2496 RouteTimeAxisView::blink_rec_display (bool onoff)
2498 RouteUI::blink_rec_display (onoff);
2502 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2504 if (_ignore_set_layer_display) {
2508 if (apply_to_selection) {
2509 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2513 _view->set_layer_display (d);
2516 set_gui_property (X_("layer-display"), enum_2_string (d));
2521 RouteTimeAxisView::layer_display () const
2524 return _view->layer_display ();
2527 /* we don't know, since we don't have a _view, so just return something */
2533 boost::shared_ptr<AutomationTimeAxisView>
2534 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2536 AutomationTracks::iterator i = _automation_tracks.find(param);
2537 if (i != _automation_tracks.end()) {
2540 return boost::shared_ptr<AutomationTimeAxisView>();
2545 RouteTimeAxisView::fast_update ()
2547 gm.get_level_meter().update_meters ();
2551 RouteTimeAxisView::hide_meter ()
2554 gm.get_level_meter().hide_meters ();
2558 RouteTimeAxisView::show_meter ()
2564 RouteTimeAxisView::reset_meter ()
2566 if (UIConfiguration::instance().get_show_track_meters()) {
2567 int meter_width = 3;
2568 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2571 gm.get_level_meter().setup_meters (height - 9, meter_width);
2578 RouteTimeAxisView::clear_meter ()
2580 gm.get_level_meter().clear_meters ();
2584 RouteTimeAxisView::meter_changed ()
2586 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2588 if (_route && !no_redraw && UIConfiguration::instance().get_show_track_meters()) {
2591 // reset peak when meter point changes
2592 gm.reset_peak_display();
2596 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2599 if (_route && !no_redraw) {
2605 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2607 using namespace Menu_Helpers;
2609 if (!_underlay_streams.empty()) {
2610 MenuList& parent_items = parent_menu->items();
2611 Menu* gs_menu = manage (new Menu);
2612 gs_menu->set_name ("ArdourContextMenu");
2613 MenuList& gs_items = gs_menu->items();
2615 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2617 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2618 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2619 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2625 RouteTimeAxisView::set_underlay_state()
2627 if (!underlay_xml_node) {
2631 XMLNodeList nlist = underlay_xml_node->children();
2632 XMLNodeConstIterator niter;
2633 XMLNode *child_node;
2635 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2636 child_node = *niter;
2638 if (child_node->name() != "Underlay") {
2642 XMLProperty const * prop = child_node->property ("id");
2644 PBD::ID id (prop->value());
2646 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2649 add_underlay(v->view(), false);
2658 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2664 RouteTimeAxisView& other = v->trackview();
2666 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2667 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2668 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2669 abort(); /*NOTREACHED*/
2672 _underlay_streams.push_back(v);
2673 other._underlay_mirrors.push_back(this);
2675 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2677 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2679 if (!underlay_xml_node) {
2680 underlay_xml_node = xml_node->add_child("Underlays");
2683 XMLNode* node = underlay_xml_node->add_child("Underlay");
2684 XMLProperty const * prop = node->add_property("id");
2685 prop->set_value(v->trackview().route()->id().to_s());
2692 RouteTimeAxisView::remove_underlay (StreamView* v)
2698 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2699 RouteTimeAxisView& other = v->trackview();
2701 if (it != _underlay_streams.end()) {
2702 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2704 if (gm == other._underlay_mirrors.end()) {
2705 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2706 abort(); /*NOTREACHED*/
2709 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2711 _underlay_streams.erase(it);
2712 other._underlay_mirrors.erase(gm);
2714 if (underlay_xml_node) {
2715 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2721 RouteTimeAxisView::set_button_names ()
2723 if (_route && _route->solo_safe_control()->solo_safe()) {
2724 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2726 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2728 if (Config->get_solo_control_is_listen_control()) {
2729 switch (Config->get_listen_position()) {
2730 case AfterFaderListen:
2731 solo_button->set_text (S_("AfterFader|A"));
2732 set_tooltip (*solo_button, _("After-fade listen (AFL)"));
2734 case PreFaderListen:
2735 solo_button->set_text (S_("PreFader|P"));
2736 set_tooltip (*solo_button, _("Pre-fade listen (PFL)"));
2740 solo_button->set_text (S_("Solo|S"));
2741 set_tooltip (*solo_button, _("Solo"));
2743 mute_button->set_text (S_("Mute|M"));
2747 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2749 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2750 if (i != _main_automation_menu_map.end()) {
2754 i = _subplugin_menu_map.find (param);
2755 if (i != _subplugin_menu_map.end()) {
2763 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2765 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2767 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2771 gain_track.reset (new AutomationTimeAxisView (_session,
2772 _route, _route->amp(), c, param,
2777 _route->amp()->describe_parameter(param)));
2780 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2783 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2787 RouteTimeAxisView::create_trim_automation_child (const Evoral::Parameter& param, bool show)
2789 boost::shared_ptr<AutomationControl> c = _route->trim()->gain_control();
2790 if (!c || ! _route->trim()->active()) {
2794 trim_track.reset (new AutomationTimeAxisView (_session,
2795 _route, _route->trim(), c, param,
2800 _route->trim()->describe_parameter(param)));
2803 _view->foreach_regionview (sigc::mem_fun (*trim_track.get(), &TimeAxisView::add_ghost));
2806 add_automation_child (Evoral::Parameter(TrimAutomation), trim_track, show);
2810 RouteTimeAxisView::create_mute_automation_child (const Evoral::Parameter& param, bool show)
2812 boost::shared_ptr<AutomationControl> c = _route->mute_control();
2814 error << "Route has no mute automation, unable to add automation track view." << endmsg;
2818 mute_track.reset (new AutomationTimeAxisView (_session,
2819 _route, _route, c, param,
2824 _route->describe_parameter(param)));
2827 _view->foreach_regionview (sigc::mem_fun (*mute_track.get(), &TimeAxisView::add_ghost));
2830 add_automation_child (Evoral::Parameter(MuteAutomation), mute_track, show);
2834 void add_region_to_list (RegionView* rv, RegionList* l)
2836 l->push_back (rv->region());
2840 RouteTimeAxisView::combine_regions ()
2842 /* as of may 2011, we do not offer uncombine for MIDI tracks
2845 if (!is_audio_track()) {
2853 RegionList selected_regions;
2854 boost::shared_ptr<Playlist> playlist = track()->playlist();
2856 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2858 if (selected_regions.size() < 2) {
2862 playlist->clear_changes ();
2863 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2865 _session->add_command (new StatefulDiffCommand (playlist));
2866 /* make the new region be selected */
2868 return _view->find_view (compound_region);
2872 RouteTimeAxisView::uncombine_regions ()
2874 /* as of may 2011, we do not offer uncombine for MIDI tracks
2876 if (!is_audio_track()) {
2884 RegionList selected_regions;
2885 boost::shared_ptr<Playlist> playlist = track()->playlist();
2887 /* have to grab selected regions first because the uncombine is going
2888 * to change that in the middle of the list traverse
2891 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2893 playlist->clear_changes ();
2895 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2896 playlist->uncombine (*i);
2899 _session->add_command (new StatefulDiffCommand (playlist));
2903 RouteTimeAxisView::state_id() const
2905 return string_compose ("rtav %1", _route->id().to_s());
2910 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2912 TimeAxisView::remove_child (c);
2914 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2916 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2917 if (i->second == a) {
2918 _automation_tracks.erase (i);
2926 RouteTimeAxisView::color () const
2928 return route_color ();
2932 RouteTimeAxisView::marked_for_display () const
2934 return !_route->presentation_info().hidden();
2938 RouteTimeAxisView::set_marked_for_display (bool yn)
2940 return RouteUI::mark_hidden (!yn);