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 "global_signals.h"
65 #include "route_time_axis.h"
66 #include "automation_time_axis.h"
68 #include "gui_thread.h"
69 #include "item_counts.h"
71 #include "paste_context.h"
72 #include "playlist_selector.h"
73 #include "point_selection.h"
75 #include "public_editor.h"
76 #include "region_view.h"
77 #include "rgb_macros.h"
78 #include "selection.h"
79 #include "streamview.h"
81 #include "route_group_menu.h"
83 #include "ardour/track.h"
87 using namespace ARDOUR;
88 using namespace ARDOUR_UI_UTILS;
90 using namespace Gtkmm2ext;
92 using namespace Editing;
96 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());
129 RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
131 RouteUI::set_route (rt);
133 CANVAS_DEBUG_NAME (_canvas_display, string_compose ("main for %1", rt->name()));
134 CANVAS_DEBUG_NAME (selection_group, string_compose ("selections for %1", rt->name()));
135 CANVAS_DEBUG_NAME (_ghost_group, string_compose ("ghosts for %1", rt->name()));
138 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
141 gm.set_controls (_route, _route->shared_peak_meter(), _route->amp());
142 gm.get_level_meter().set_no_show_all();
143 gm.get_level_meter().setup_meters(50, meter_width);
144 gm.update_gain_sensitive ();
146 string str = gui_property ("height");
148 set_height (atoi (str));
150 set_height (preset_height (HeightNormal));
153 if (!_route->is_auditioner()) {
154 if (gui_property ("visible").empty()) {
155 set_gui_property ("visible", true);
158 set_gui_property ("visible", false);
161 timestretch_rect = 0;
164 ignore_toggle = false;
166 route_group_button.set_name ("route button");
167 playlist_button.set_name ("route button");
168 automation_button.set_name ("route button");
170 route_group_button.signal_button_release_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
171 playlist_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click));
172 automation_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click));
176 if (ARDOUR::Profile->get_mixbus()) {
177 controls_table.attach (*rec_enable_button, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
179 controls_table.attach (*rec_enable_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
182 if (is_midi_track()) {
183 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
184 gm.set_fader_name ("MidiTrackFader");
186 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record"));
187 gm.set_fader_name ("AudioTrackFader");
190 rec_enable_button->set_sensitive (_session->writable());
192 /* set playlist button tip to the current playlist, and make it update when it changes */
193 update_playlist_tip ();
194 track()->PlaylistChanged.connect (*this, invalidator (*this), ui_bind(&RouteTimeAxisView::update_playlist_tip, this), gui_context());
197 gm.set_fader_name ("AudioBusFader");
198 Gtk::Fixed *blank = manage(new Gtk::Fixed());
199 controls_button_size_group->add_widget(*blank);
200 if (ARDOUR::Profile->get_mixbus() ) {
201 controls_table.attach (*blank, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
203 controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
208 top_hbox.pack_end(gm.get_level_meter(), false, false, 2);
210 if (!ARDOUR::Profile->get_mixbus()) {
211 controls_meters_size_group->add_widget (gm.get_level_meter());
214 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
215 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
216 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
217 _route->track_number_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::label_view, this), gui_context());
219 if (ARDOUR::Profile->get_mixbus()) {
220 controls_table.attach (*mute_button, 1, 2, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
222 controls_table.attach (*mute_button, 3, 4, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
224 // mute button is always present, it is used to
225 // force the 'blank' placeholders to the proper size
226 controls_button_size_group->add_widget(*mute_button);
228 if (!_route->is_master()) {
229 if (ARDOUR::Profile->get_mixbus()) {
230 controls_table.attach (*solo_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
232 controls_table.attach (*solo_button, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
235 Gtk::Fixed *blank = manage(new Gtk::Fixed());
236 controls_button_size_group->add_widget(*blank);
237 if (ARDOUR::Profile->get_mixbus()) {
238 controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
240 controls_table.attach (*blank, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
245 if (ARDOUR::Profile->get_mixbus()) {
246 controls_table.attach (route_group_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
247 controls_table.attach (gm.get_gain_slider(), 3, 5, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
249 else if (!ARDOUR::Profile->get_trx()) {
250 controls_table.attach (route_group_button, 4, 5, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
251 controls_table.attach (gm.get_gain_slider(), 0, 2, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
254 ARDOUR_UI::instance()->set_tip(*solo_button,_("Solo"));
255 ARDOUR_UI::instance()->set_tip(*mute_button,_("Mute"));
256 ARDOUR_UI::instance()->set_tip(route_group_button, _("Route Group"));
258 mute_button->set_tweaks(ArdourButton::TrackHeader);
259 solo_button->set_tweaks(ArdourButton::TrackHeader);
260 rec_enable_button->set_tweaks(ArdourButton::TrackHeader);
261 playlist_button.set_tweaks(ArdourButton::TrackHeader);
262 automation_button.set_tweaks(ArdourButton::TrackHeader);
263 route_group_button.set_tweaks(ArdourButton::TrackHeader);
265 if (is_midi_track()) {
266 ARDOUR_UI::instance()->set_tip(automation_button, _("MIDI Controllers and Automation"));
268 ARDOUR_UI::instance()->set_tip(automation_button, _("Automation"));
271 update_track_number_visibility();
274 if (ARDOUR::Profile->get_mixbus()) {
275 controls_table.attach (automation_button, 1, 2, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
277 else if (!ARDOUR::Profile->get_trx()) {
278 controls_table.attach (automation_button, 3, 4, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
281 if (is_track() && track()->mode() == ARDOUR::Normal) {
282 if (ARDOUR::Profile->get_mixbus()) {
283 controls_table.attach (playlist_button, 0, 1, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
285 else if (!ARDOUR::Profile->get_trx()) {
286 controls_table.attach (playlist_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
292 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
293 _route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::route_property_changed, this, _1), gui_context());
297 str = gui_property ("layer-display");
299 set_layer_display (LayerDisplay (string_2_enum (str, _view->layer_display ())));
302 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
303 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
305 /* pick up the correct freeze state */
310 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_pixel));
311 ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
313 PropertyList* plist = new PropertyList();
315 plist->add (ARDOUR::Properties::mute, true);
316 plist->add (ARDOUR::Properties::solo, true);
318 route_group_menu = new RouteGroupMenu (_session, plist);
320 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
323 RouteTimeAxisView::~RouteTimeAxisView ()
325 cleanup_gui_properties ();
327 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
331 delete playlist_action_menu;
332 playlist_action_menu = 0;
337 _automation_tracks.clear ();
339 delete route_group_menu;
340 CatchDeletion (this);
344 RouteTimeAxisView::post_construct ()
346 /* map current state of the route */
348 update_diskstream_display ();
349 setup_processor_menu_and_curves ();
350 reset_processor_automation_curves ();
353 /** Set up the processor menu for the current set of processors, and
354 * display automation curves for any parameters which have data.
357 RouteTimeAxisView::setup_processor_menu_and_curves ()
359 _subplugin_menu_map.clear ();
360 subplugin_menu.items().clear ();
361 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
362 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
366 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
368 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
369 if (_route->route_group()) {
370 _route->route_group()->remove (_route);
376 r.push_back (route ());
378 route_group_menu->build (r);
379 route_group_menu->menu()->popup (ev->button, ev->time);
385 RouteTimeAxisView::playlist_changed ()
391 RouteTimeAxisView::label_view ()
393 string x = _route->name ();
394 if (x != name_label.get_text ()) {
395 name_label.set_text (x);
397 const int64_t track_number = _route->track_number ();
398 if (track_number == 0) {
399 number_label.set_text ("");
401 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
406 RouteTimeAxisView::update_track_number_visibility ()
409 bool show_label = _session->config.get_track_name_number();
411 if (_route && _route->is_master()) {
415 if (number_label.get_parent()) {
416 controls_table.remove (number_label);
419 if (ARDOUR::Profile->get_mixbus()) {
420 controls_table.attach (number_label, 3, 4, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
422 controls_table.attach (number_label, 0, 1, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
424 // see ArdourButton::on_size_request(), we should probably use a global size-group here instead.
425 // except the width of the number label is subtracted from the name-hbox, so we
426 // need to explictly calculate it anyway until the name-label & entry become ArdourWidgets.
427 int tnw = (2 + std::max(2u, _session->track_number_decimals())) * number_label.char_pixel_width();
429 number_label.set_size_request(tnw, -1);
430 number_label.show ();
431 name_hbox.set_size_request(TimeAxisView::name_width_px - 2 - tnw, -1); // -2 = cellspacing
433 number_label.hide ();
434 name_hbox.set_size_request(TimeAxisView::name_width_px, -1);
439 RouteTimeAxisView::parameter_changed (string const & p)
441 if (p == "track-name-number") {
442 update_track_number_visibility();
447 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
449 if (what_changed.contains (ARDOUR::Properties::name)) {
455 RouteTimeAxisView::take_name_changed (void *src)
463 RouteTimeAxisView::playlist_click ()
465 build_playlist_menu ();
466 conditionally_add_to_selection ();
467 playlist_action_menu->popup (1, gtk_get_current_event_time());
471 RouteTimeAxisView::automation_click ()
473 conditionally_add_to_selection ();
474 build_automation_action_menu (false);
475 automation_action_menu->popup (1, gtk_get_current_event_time());
479 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
481 using namespace Menu_Helpers;
483 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
484 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
487 detach_menu (subplugin_menu);
489 _main_automation_menu_map.clear ();
490 delete automation_action_menu;
491 automation_action_menu = new Menu;
493 MenuList& items = automation_action_menu->items();
495 automation_action_menu->set_name ("ArdourContextMenu");
497 items.push_back (MenuElem (_("Show All Automation"),
498 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
500 items.push_back (MenuElem (_("Show Existing Automation"),
501 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
503 items.push_back (MenuElem (_("Hide All Automation"),
504 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
506 /* Attach the plugin submenu. It may have previously been used elsewhere,
507 so it was detached above
510 if (!subplugin_menu.items().empty()) {
511 items.push_back (SeparatorElem ());
512 items.push_back (MenuElem (_("Processor automation"), subplugin_menu));
513 items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);;
516 /* Add any route automation */
519 items.push_back (CheckMenuElem (_("Fader"), sigc::mem_fun (*this, &RouteTimeAxisView::update_gain_track_visibility)));
520 gain_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
521 gain_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
522 (gain_track && string_is_affirmative (gain_track->gui_property ("visible"))));
524 _main_automation_menu_map[Evoral::Parameter(GainAutomation)] = gain_automation_item;
528 items.push_back (CheckMenuElem (_("Trim"), sigc::mem_fun (*this, &RouteTimeAxisView::update_trim_track_visibility)));
529 trim_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
530 trim_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
531 (trim_track && string_is_affirmative (trim_track->gui_property ("visible"))));
533 _main_automation_menu_map[Evoral::Parameter(TrimAutomation)] = trim_automation_item;
537 items.push_back (CheckMenuElem (_("Mute"), sigc::mem_fun (*this, &RouteTimeAxisView::update_mute_track_visibility)));
538 mute_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
539 mute_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
540 (mute_track && string_is_affirmative (mute_track->gui_property ("visible"))));
542 _main_automation_menu_map[Evoral::Parameter(MuteAutomation)] = mute_automation_item;
545 if (!pan_tracks.empty()) {
546 items.push_back (CheckMenuElem (_("Pan"), sigc::mem_fun (*this, &RouteTimeAxisView::update_pan_track_visibility)));
547 pan_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
548 pan_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
549 (!pan_tracks.empty() && string_is_affirmative (pan_tracks.front()->gui_property ("visible"))));
551 set<Evoral::Parameter> const & params = _route->pannable()->what_can_be_automated ();
552 for (set<Evoral::Parameter>::const_iterator p = params.begin(); p != params.end(); ++p) {
553 _main_automation_menu_map[*p] = pan_automation_item;
559 RouteTimeAxisView::build_display_menu ()
561 using namespace Menu_Helpers;
565 TimeAxisView::build_display_menu ();
567 /* now fill it with our stuff */
569 MenuList& items = display_menu->items();
570 display_menu->set_name ("ArdourContextMenu");
572 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
574 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
576 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
578 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
580 items.push_back (SeparatorElem());
583 detach_menu (*_size_menu);
586 items.push_back (MenuElem (_("Height"), *_size_menu));
588 items.push_back (SeparatorElem());
590 if (!Profile->get_sae()) {
591 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
592 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
593 items.push_back (SeparatorElem());
596 // Hook for derived classes to add type specific stuff
597 append_extra_display_menu_items ();
601 Menu* layers_menu = manage (new Menu);
602 MenuList &layers_items = layers_menu->items();
603 layers_menu->set_name("ArdourContextMenu");
605 RadioMenuItem::Group layers_group;
607 /* Find out how many overlaid/stacked tracks we have in the selection */
611 TrackSelection const & s = _editor.get_selection().tracks;
612 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
613 StreamView* v = (*i)->view ();
618 switch (v->layer_display ()) {
629 /* We're not connecting to signal_toggled() here; in the case where these two items are
630 set to be in the `inconsistent' state, it seems that one or other will end up active
631 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
632 select the active one, no toggled signal is emitted so nothing happens.
635 _ignore_set_layer_display = true;
637 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
638 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
639 i->set_active (overlaid != 0 && stacked == 0);
640 i->set_inconsistent (overlaid != 0 && stacked != 0);
641 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
643 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
644 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
645 i->set_active (overlaid == 0 && stacked != 0);
646 i->set_inconsistent (overlaid != 0 && stacked != 0);
647 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
649 _ignore_set_layer_display = false;
651 items.push_back (MenuElem (_("Layers"), *layers_menu));
653 if (!Profile->get_sae()) {
655 Menu* alignment_menu = manage (new Menu);
656 MenuList& alignment_items = alignment_menu->items();
657 alignment_menu->set_name ("ArdourContextMenu");
659 RadioMenuItem::Group align_group;
661 /* Same verbose hacks as for the layering options above */
667 boost::shared_ptr<Track> first_track;
669 TrackSelection const & s = _editor.get_selection().tracks;
670 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
671 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
672 if (!r || !r->is_track ()) {
677 first_track = r->track();
680 switch (r->track()->alignment_choice()) {
684 switch (r->track()->alignment_style()) {
685 case ExistingMaterial:
693 case UseExistingMaterial:
709 inconsistent = false;
718 if (!inconsistent && first_track) {
720 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
721 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
722 i->set_active (automatic != 0 && existing == 0 && capture == 0);
723 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
725 switch (first_track->alignment_choice()) {
727 switch (first_track->alignment_style()) {
728 case ExistingMaterial:
729 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
732 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
740 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
741 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
742 i->set_active (existing != 0 && capture == 0 && automatic == 0);
743 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
745 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
746 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
747 i->set_active (existing == 0 && capture != 0 && automatic == 0);
748 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
750 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
756 Menu* mode_menu = manage (new Menu);
757 MenuList& mode_items = mode_menu->items ();
758 mode_menu->set_name ("ArdourContextMenu");
760 RadioMenuItem::Group mode_group;
766 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
767 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
768 if (!r || !r->is_track ()) {
772 switch (r->track()->mode()) {
785 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
786 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
787 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
788 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
789 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
791 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
792 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
793 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
794 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
795 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
797 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
798 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
799 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
800 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
801 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
803 items.push_back (MenuElem (_("Record Mode"), *mode_menu));
807 items.push_back (SeparatorElem());
809 build_playlist_menu ();
810 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
811 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
814 route_group_menu->detach ();
817 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
818 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
820 r.push_back (rtv->route ());
825 r.push_back (route ());
828 route_group_menu->build (r);
829 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
831 build_automation_action_menu (true);
832 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
834 items.push_back (SeparatorElem());
838 TrackSelection const & s = _editor.get_selection().tracks;
839 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
840 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
845 if (r->route()->active()) {
852 items.push_back (CheckMenuElem (_("Active")));
853 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
854 bool click_sets_active = true;
855 if (active > 0 && inactive == 0) {
856 i->set_active (true);
857 click_sets_active = false;
858 } else if (active > 0 && inactive > 0) {
859 i->set_inconsistent (true);
861 i->set_sensitive(! _session->transport_rolling());
862 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
864 items.push_back (SeparatorElem());
865 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
866 items.push_front (SeparatorElem());
867 items.push_front (MenuElem (_("Remove"), sigc::mem_fun(_editor, &PublicEditor::remove_tracks)));
871 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
873 if (apply_to_selection) {
874 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
877 bool needs_bounce = false;
879 if (!track()->can_use_mode (mode, needs_bounce)) {
885 cerr << "would bounce this one\n";
890 track()->set_mode (mode);
895 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
897 TimeAxisView::show_timestretch (start, end, layers, layer);
907 /* check that the time selection was made in our route, or our route group.
908 remember that route_group() == 0 implies the route is *not* in a edit group.
911 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
912 /* this doesn't apply to us */
916 /* ignore it if our edit group is not active */
918 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
923 if (timestretch_rect == 0) {
924 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
925 timestretch_rect->set_fill_color (ArdourCanvas::HSV (ARDOUR_UI::config()->color ("time stretch fill")).mod (ARDOUR_UI::config()->modifier ("time stretch fill")).color());
926 timestretch_rect->set_outline_color (ARDOUR_UI::config()->color ("time stretch outline"));
929 timestretch_rect->show ();
930 timestretch_rect->raise_to_top ();
932 double const x1 = start / _editor.get_current_zoom();
933 double const x2 = (end - 1) / _editor.get_current_zoom();
935 timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
936 x2, current_height() * (layers - layer) / layers));
940 RouteTimeAxisView::hide_timestretch ()
942 TimeAxisView::hide_timestretch ();
944 if (timestretch_rect) {
945 timestretch_rect->hide ();
950 RouteTimeAxisView::show_selection (TimeSelection& ts)
954 /* ignore it if our edit group is not active or if the selection was started
955 in some other track or route group (remember that route_group() == 0 means
956 that the track is not in an route group).
959 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
960 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
966 TimeAxisView::show_selection (ts);
970 RouteTimeAxisView::set_height (uint32_t h, TrackHeightMode m)
973 bool height_changed = (height == 0) || (h != height);
976 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
979 gm.get_level_meter().setup_meters (gmlen, meter_width);
981 TimeAxisView::set_height (h, m);
984 _view->set_height ((double) current_height());
987 if (height >= preset_height (HeightNormal)) {
991 gm.get_gain_slider().show();
993 if (!_route || _route->is_monitor()) {
998 if (rec_enable_button)
999 rec_enable_button->show();
1001 route_group_button.show();
1002 automation_button.show();
1004 if (is_track() && track()->mode() == ARDOUR::Normal) {
1005 playlist_button.show();
1012 gm.get_gain_slider().hide();
1013 mute_button->show();
1014 if (!_route || _route->is_monitor()) {
1015 solo_button->hide();
1017 solo_button->show();
1019 if (rec_enable_button)
1020 rec_enable_button->show();
1022 route_group_button.hide ();
1023 automation_button.hide ();
1025 if (is_track() && track()->mode() == ARDOUR::Normal) {
1026 playlist_button.hide ();
1031 if (height_changed && !no_redraw) {
1032 /* only emit the signal if the height really changed */
1038 RouteTimeAxisView::route_color_changed ()
1041 _view->apply_color (color(), StreamView::RegionColor);
1044 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1048 RouteTimeAxisView::reset_samples_per_pixel ()
1050 set_samples_per_pixel (_editor.get_current_zoom());
1054 RouteTimeAxisView::set_samples_per_pixel (double fpp)
1059 speed = track()->speed();
1063 _view->set_samples_per_pixel (fpp * speed);
1066 TimeAxisView::set_samples_per_pixel (fpp * speed);
1070 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
1072 if (!mitem->get_active()) {
1073 /* this is one of the two calls made when these radio menu items change status. this one
1074 is for the item that became inactive, and we want to ignore it.
1079 if (apply_to_selection) {
1080 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
1083 track()->set_align_choice (choice);
1089 RouteTimeAxisView::rename_current_playlist ()
1091 ArdourPrompter prompter (true);
1094 boost::shared_ptr<Track> tr = track();
1095 if (!tr || tr->destructive()) {
1099 boost::shared_ptr<Playlist> pl = tr->playlist();
1104 prompter.set_title (_("Rename Playlist"));
1105 prompter.set_prompt (_("New name for playlist:"));
1106 prompter.set_initial_text (pl->name());
1107 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1108 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1110 switch (prompter.run ()) {
1111 case Gtk::RESPONSE_ACCEPT:
1112 prompter.get_result (name);
1113 if (name.length()) {
1114 pl->set_name (name);
1124 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1126 std::string ret (basename);
1128 std::string const group_string = "." + route_group()->name() + ".";
1130 // iterate through all playlists
1132 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1133 std::string tmp = (*i)->name();
1135 std::string::size_type idx = tmp.find(group_string);
1136 // find those which belong to this group
1137 if (idx != string::npos) {
1138 tmp = tmp.substr(idx + group_string.length());
1140 // and find the largest current number
1142 if (x > maxnumber) {
1151 snprintf (buf, sizeof(buf), "%d", maxnumber);
1153 ret = this->name() + "." + route_group()->name () + "." + buf;
1159 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1163 boost::shared_ptr<Track> tr = track ();
1164 if (!tr || tr->destructive()) {
1168 boost::shared_ptr<const Playlist> pl = tr->playlist();
1175 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1176 name = resolve_new_group_playlist_name(name, playlists_before_op);
1179 while (_session->playlists->by_name(name)) {
1180 name = Playlist::bump_name (name, *_session);
1183 // TODO: The prompter "new" button should be de-activated if the user
1184 // specifies a playlist name which already exists in the session.
1188 ArdourPrompter prompter (true);
1190 prompter.set_title (_("New Copy Playlist"));
1191 prompter.set_prompt (_("Name for new playlist:"));
1192 prompter.set_initial_text (name);
1193 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1194 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1195 prompter.show_all ();
1197 switch (prompter.run ()) {
1198 case Gtk::RESPONSE_ACCEPT:
1199 prompter.get_result (name);
1207 if (name.length()) {
1208 tr->use_copy_playlist ();
1209 tr->playlist()->set_name (name);
1214 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1218 boost::shared_ptr<Track> tr = track ();
1219 if (!tr || tr->destructive()) {
1223 boost::shared_ptr<const Playlist> pl = tr->playlist();
1230 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1231 name = resolve_new_group_playlist_name(name,playlists_before_op);
1234 while (_session->playlists->by_name(name)) {
1235 name = Playlist::bump_name (name, *_session);
1241 ArdourPrompter prompter (true);
1243 prompter.set_title (_("New Playlist"));
1244 prompter.set_prompt (_("Name for new playlist:"));
1245 prompter.set_initial_text (name);
1246 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1247 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1249 switch (prompter.run ()) {
1250 case Gtk::RESPONSE_ACCEPT:
1251 prompter.get_result (name);
1259 if (name.length()) {
1260 tr->use_new_playlist ();
1261 tr->playlist()->set_name (name);
1266 RouteTimeAxisView::clear_playlist ()
1268 boost::shared_ptr<Track> tr = track ();
1269 if (!tr || tr->destructive()) {
1273 boost::shared_ptr<Playlist> pl = tr->playlist();
1278 _editor.clear_playlist (pl);
1282 RouteTimeAxisView::speed_changed ()
1284 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1288 RouteTimeAxisView::update_diskstream_display ()
1298 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1300 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1302 /* special case: select/deselect all tracks */
1304 _editor.begin_reversible_selection_op (X_("Selection Click"));
1306 if (_editor.get_selection().selected (this)) {
1307 _editor.get_selection().clear_tracks ();
1309 _editor.select_all_tracks ();
1312 _editor.commit_reversible_selection_op ();
1317 _editor.begin_reversible_selection_op (X_("Selection Click"));
1319 switch (ArdourKeyboard::selection_type (ev->state)) {
1320 case Selection::Toggle:
1321 _editor.get_selection().toggle (this);
1324 case Selection::Set:
1325 _editor.get_selection().set (this);
1328 case Selection::Extend:
1329 _editor.extend_selection_to_track (*this);
1332 case Selection::Add:
1333 _editor.get_selection().add (this);
1337 _editor.commit_reversible_selection_op ();
1341 RouteTimeAxisView::set_selected_points (PointSelection& points)
1343 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1344 (*i)->set_selected_points (points);
1346 AudioStreamView* asv = dynamic_cast<AudioStreamView*>(_view);
1348 asv->set_selected_points (points);
1353 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1356 _view->set_selected_regionviews (regions);
1360 /** Add the selectable things that we have to a list.
1361 * @param results List to add things to.
1364 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results, bool within)
1369 speed = track()->speed();
1372 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1373 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1375 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1376 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results, within);
1379 /* pick up visible automation tracks */
1381 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1382 if (!(*i)->hidden()) {
1383 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results, within);
1389 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1392 _view->get_inverted_selectables (sel, results);
1395 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1396 if (!(*i)->hidden()) {
1397 (*i)->get_inverted_selectables (sel, results);
1405 RouteTimeAxisView::route_group () const
1407 return _route->route_group();
1411 RouteTimeAxisView::name() const
1413 return _route->name();
1416 boost::shared_ptr<Playlist>
1417 RouteTimeAxisView::playlist () const
1419 boost::shared_ptr<Track> tr;
1421 if ((tr = track()) != 0) {
1422 return tr->playlist();
1424 return boost::shared_ptr<Playlist> ();
1429 RouteTimeAxisView::name_entry_changed ()
1431 TimeAxisView::name_entry_changed ();
1433 string x = name_entry->get_text ();
1435 if (x == _route->name()) {
1439 strip_whitespace_edges (x);
1441 if (x.length() == 0) {
1442 name_entry->set_text (_route->name());
1446 if (_session->route_name_internal (x)) {
1447 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1449 name_entry->grab_focus ();
1450 } else if (RouteUI::verify_new_route_name (x)) {
1451 _route->set_name (x);
1453 name_entry->grab_focus ();
1457 boost::shared_ptr<Region>
1458 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1460 boost::shared_ptr<Playlist> pl = playlist ();
1463 return pl->find_next_region (pos, point, dir);
1466 return boost::shared_ptr<Region> ();
1470 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1472 boost::shared_ptr<Playlist> pl = playlist ();
1475 return pl->find_next_region_boundary (pos, dir);
1482 RouteTimeAxisView::fade_range (TimeSelection& selection)
1484 boost::shared_ptr<Playlist> what_we_got;
1485 boost::shared_ptr<Track> tr = track ();
1486 boost::shared_ptr<Playlist> playlist;
1489 /* route is a bus, not a track */
1493 playlist = tr->playlist();
1495 TimeSelection time (selection);
1496 float const speed = tr->speed();
1497 if (speed != 1.0f) {
1498 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1499 (*i).start = session_frame_to_track_frame((*i).start, speed);
1500 (*i).end = session_frame_to_track_frame((*i).end, speed);
1504 playlist->clear_changes ();
1505 playlist->clear_owned_changes ();
1507 playlist->fade_range (time);
1509 vector<Command*> cmds;
1510 playlist->rdiff (cmds);
1511 _session->add_commands (cmds);
1512 _session->add_command (new StatefulDiffCommand (playlist));
1517 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1519 boost::shared_ptr<Playlist> what_we_got;
1520 boost::shared_ptr<Track> tr = track ();
1521 boost::shared_ptr<Playlist> playlist;
1524 /* route is a bus, not a track */
1528 playlist = tr->playlist();
1530 TimeSelection time (selection.time);
1531 float const speed = tr->speed();
1532 if (speed != 1.0f) {
1533 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1534 (*i).start = session_frame_to_track_frame((*i).start, speed);
1535 (*i).end = session_frame_to_track_frame((*i).end, speed);
1539 playlist->clear_changes ();
1540 playlist->clear_owned_changes ();
1544 if (playlist->cut (time) != 0) {
1545 if (Config->get_edit_mode() == Ripple)
1546 playlist->ripple(time.start(), -time.length(), NULL);
1547 // no need to exclude any regions from rippling here
1549 vector<Command*> cmds;
1550 playlist->rdiff (cmds);
1551 _session->add_commands (cmds);
1553 _session->add_command (new StatefulDiffCommand (playlist));
1558 if ((what_we_got = playlist->cut (time)) != 0) {
1559 _editor.get_cut_buffer().add (what_we_got);
1560 if (Config->get_edit_mode() == Ripple)
1561 playlist->ripple(time.start(), -time.length(), NULL);
1562 // no need to exclude any regions from rippling here
1564 vector<Command*> cmds;
1565 playlist->rdiff (cmds);
1566 _session->add_commands (cmds);
1568 _session->add_command (new StatefulDiffCommand (playlist));
1572 if ((what_we_got = playlist->copy (time)) != 0) {
1573 _editor.get_cut_buffer().add (what_we_got);
1578 if ((what_we_got = playlist->cut (time)) != 0) {
1579 if (Config->get_edit_mode() == Ripple)
1580 playlist->ripple(time.start(), -time.length(), NULL);
1581 // no need to exclude any regions from rippling here
1583 vector<Command*> cmds;
1584 playlist->rdiff (cmds);
1585 _session->add_commands (cmds);
1586 _session->add_command (new StatefulDiffCommand (playlist));
1587 what_we_got->release ();
1594 RouteTimeAxisView::paste (framepos_t pos, const Selection& selection, PasteContext& ctx)
1600 boost::shared_ptr<Playlist> pl = playlist ();
1601 const ARDOUR::DataType type = pl->data_type();
1602 PlaylistSelection::const_iterator p = selection.playlists.get_nth(type, ctx.counts.n_playlists(type));
1604 if (p == selection.playlists.end()) {
1607 ctx.counts.increase_n_playlists(type);
1609 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1611 if (track()->speed() != 1.0f) {
1612 pos = session_frame_to_track_frame (pos, track()->speed());
1613 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1616 /* add multi-paste offset if applicable */
1617 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent();
1618 const framecnt_t duration = extent.second - extent.first;
1619 pos += _editor.get_paste_offset(pos, ctx.count, duration);
1621 pl->clear_changes ();
1622 pl->clear_owned_changes ();
1623 if (Config->get_edit_mode() == Ripple) {
1624 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent_with_endspace();
1625 framecnt_t amount = extent.second - extent.first;
1626 pl->ripple(pos, amount * ctx.times, boost::shared_ptr<Region>());
1628 pl->paste (*p, pos, ctx.times);
1630 vector<Command*> cmds;
1632 _session->add_commands (cmds);
1634 _session->add_command (new StatefulDiffCommand (pl));
1640 struct PlaylistSorter {
1641 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1642 return a->sort_id() < b->sort_id();
1647 RouteTimeAxisView::build_playlist_menu ()
1649 using namespace Menu_Helpers;
1655 delete playlist_action_menu;
1656 playlist_action_menu = new Menu;
1657 playlist_action_menu->set_name ("ArdourContextMenu");
1659 MenuList& playlist_items = playlist_action_menu->items();
1660 playlist_action_menu->set_name ("ArdourContextMenu");
1661 playlist_items.clear();
1663 RadioMenuItem::Group playlist_group;
1664 boost::shared_ptr<Track> tr = track ();
1666 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1668 /* sort the playlists */
1670 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1672 /* add the playlists to the menu */
1673 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1674 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1675 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1676 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1678 if (tr->playlist()->id() == (*i)->id()) {
1684 playlist_items.push_back (SeparatorElem());
1685 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1686 playlist_items.push_back (SeparatorElem());
1688 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1689 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1690 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1693 // Use a label which tells the user what is happening
1694 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1695 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1699 playlist_items.push_back (SeparatorElem());
1700 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1701 playlist_items.push_back (SeparatorElem());
1703 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1707 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1709 assert (is_track());
1711 // exit if we were triggered by deactivating the old playlist
1712 if (!item->get_active()) {
1716 boost::shared_ptr<Playlist> pl (wpl.lock());
1722 if (track()->playlist() == pl) {
1723 // exit when use_playlist is called by the creation of the playlist menu
1724 // or the playlist choice is unchanged
1728 track()->use_playlist (pl);
1730 RouteGroup* rg = route_group();
1732 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1733 std::string group_string = "." + rg->name() + ".";
1735 std::string take_name = pl->name();
1736 std::string::size_type idx = take_name.find(group_string);
1738 if (idx == std::string::npos)
1741 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1743 boost::shared_ptr<RouteList> rl (rg->route_list());
1745 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1746 if ((*i) == this->route()) {
1750 std::string playlist_name = (*i)->name()+group_string+take_name;
1752 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1757 if (track->freeze_state() == Track::Frozen) {
1758 /* Don't change playlists of frozen tracks */
1762 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1764 // No playlist for this track for this take yet, make it
1765 track->use_new_playlist();
1766 track->playlist()->set_name(playlist_name);
1768 track->use_playlist(ipl);
1775 RouteTimeAxisView::update_playlist_tip ()
1777 RouteGroup* rg = route_group ();
1778 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1779 string group_string = "." + rg->name() + ".";
1781 string take_name = track()->playlist()->name();
1782 string::size_type idx = take_name.find(group_string);
1784 if (idx != string::npos) {
1785 /* find the bit containing the take number / name */
1786 take_name = take_name.substr (idx + group_string.length());
1788 /* set the playlist button tooltip to the take name */
1789 ARDOUR_UI::instance()->set_tip (
1791 string_compose(_("Take: %1.%2"),
1792 Glib::Markup::escape_text(rg->name()),
1793 Glib::Markup::escape_text(take_name))
1800 /* set the playlist button tooltip to the playlist name */
1801 ARDOUR_UI::instance()->set_tip (playlist_button, _("Playlist") + std::string(": ") + Glib::Markup::escape_text(track()->playlist()->name()));
1806 RouteTimeAxisView::show_playlist_selector ()
1808 _editor.playlist_selector().show_for (this);
1812 RouteTimeAxisView::map_frozen ()
1818 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1820 switch (track()->freeze_state()) {
1822 playlist_button.set_sensitive (false);
1823 rec_enable_button->set_sensitive (false);
1826 playlist_button.set_sensitive (true);
1827 rec_enable_button->set_sensitive (true);
1833 RouteTimeAxisView::color_handler ()
1835 //case cTimeStretchOutline:
1836 if (timestretch_rect) {
1837 timestretch_rect->set_outline_color (ARDOUR_UI::config()->color ("time stretch outline"));
1839 //case cTimeStretchFill:
1840 if (timestretch_rect) {
1841 timestretch_rect->set_fill_color (ARDOUR_UI::config()->color ("time stretch fill"));
1847 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1848 * Will add track if necessary.
1851 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1853 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1854 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1857 /* it doesn't exist yet, so we don't care about the button state: just add it */
1858 create_automation_child (param, true);
1861 bool yn = menu->get_active();
1862 bool changed = false;
1864 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1866 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1867 will have done that for us.
1870 if (changed && !no_redraw) {
1878 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1880 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1886 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1888 if (menu && !_hidden) {
1889 ignore_toggle = true;
1890 menu->set_active (false);
1891 ignore_toggle = false;
1894 if (_route && !no_redraw) {
1900 RouteTimeAxisView::update_gain_track_visibility ()
1902 bool const showit = gain_automation_item->get_active();
1904 if (showit != string_is_affirmative (gain_track->gui_property ("visible"))) {
1905 gain_track->set_marked_for_display (showit);
1907 /* now trigger a redisplay */
1910 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1916 RouteTimeAxisView::update_trim_track_visibility ()
1918 bool const showit = trim_automation_item->get_active();
1920 if (showit != string_is_affirmative (trim_track->gui_property ("visible"))) {
1921 trim_track->set_marked_for_display (showit);
1923 /* now trigger a redisplay */
1926 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1932 RouteTimeAxisView::update_mute_track_visibility ()
1934 bool const showit = mute_automation_item->get_active();
1936 if (showit != string_is_affirmative (mute_track->gui_property ("visible"))) {
1937 mute_track->set_marked_for_display (showit);
1939 /* now trigger a redisplay */
1942 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1948 RouteTimeAxisView::update_pan_track_visibility ()
1950 bool const showit = pan_automation_item->get_active();
1951 bool changed = false;
1953 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1954 if ((*i)->set_marked_for_display (showit)) {
1960 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1965 RouteTimeAxisView::ensure_pan_views (bool show)
1967 bool changed = false;
1968 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1970 (*i)->set_marked_for_display (false);
1973 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1977 if (!_route->panner()) {
1981 set<Evoral::Parameter> params = _route->panner()->what_can_be_automated();
1982 set<Evoral::Parameter>::iterator p;
1984 for (p = params.begin(); p != params.end(); ++p) {
1985 boost::shared_ptr<ARDOUR::AutomationControl> pan_control = _route->pannable()->automation_control(*p);
1987 if (pan_control->parameter().type() == NullAutomation) {
1988 error << "Pan control has NULL automation type!" << endmsg;
1992 if (automation_child (pan_control->parameter ()).get () == 0) {
1994 /* we don't already have an AutomationTimeAxisView for this parameter */
1996 std::string const name = _route->panner()->describe_parameter (pan_control->parameter ());
1998 boost::shared_ptr<AutomationTimeAxisView> t (
1999 new AutomationTimeAxisView (_session,
2003 pan_control->parameter (),
2011 pan_tracks.push_back (t);
2012 add_automation_child (*p, t, show);
2014 pan_tracks.push_back (automation_child (pan_control->parameter ()));
2021 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
2023 if (apply_to_selection) {
2024 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
2028 /* Show our automation */
2030 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2031 i->second->set_marked_for_display (true);
2033 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2036 menu->set_active(true);
2041 /* Show processor automation */
2043 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2044 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2045 if ((*ii)->view == 0) {
2046 add_processor_automation_curve ((*i)->processor, (*ii)->what);
2049 (*ii)->menu_item->set_active (true);
2062 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
2064 if (apply_to_selection) {
2065 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
2069 /* Show our automation */
2071 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2072 if (i->second->has_automation()) {
2073 i->second->set_marked_for_display (true);
2075 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2077 menu->set_active(true);
2082 /* Show processor automation */
2084 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2085 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2086 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
2087 (*ii)->menu_item->set_active (true);
2099 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
2101 if (apply_to_selection) {
2102 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
2106 /* Hide our automation */
2108 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2109 i->second->set_marked_for_display (false);
2111 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2114 menu->set_active (false);
2118 /* Hide processor automation */
2120 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2121 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2122 (*ii)->menu_item->set_active (false);
2133 RouteTimeAxisView::region_view_added (RegionView* rv)
2135 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
2136 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
2137 boost::shared_ptr<AutomationTimeAxisView> atv;
2139 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
2144 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
2145 (*i)->add_ghost(rv);
2149 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
2151 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
2157 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
2159 parent.remove_processor_automation_node (this);
2163 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
2166 remove_child (pan->view);
2170 RouteTimeAxisView::ProcessorAutomationNode*
2171 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2173 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2175 if ((*i)->processor == processor) {
2177 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2178 if ((*ii)->what == what) {
2188 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
2190 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2193 ProcessorAutomationNode* pan;
2195 if ((pan = find_processor_automation_node (processor, what)) == 0) {
2196 /* session state may never have been saved with new plugin */
2197 error << _("programming error: ")
2198 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
2199 processor->name(), what.type(), (int) what.channel(), what.id() )
2201 abort(); /*NOTREACHED*/
2209 boost::shared_ptr<AutomationControl> control
2210 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
2212 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
2213 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
2214 _editor, *this, false, parent_canvas,
2215 processor->describe_parameter (what), processor->name()));
2217 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
2219 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
2222 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
2227 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
2230 pan->menu_item->set_active (false);
2239 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
2241 boost::shared_ptr<Processor> processor (p.lock ());
2243 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
2244 /* The Amp processor is a special case and is dealt with separately */
2248 set<Evoral::Parameter> existing;
2250 processor->what_has_data (existing);
2252 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
2254 Evoral::Parameter param (*i);
2255 boost::shared_ptr<AutomationLine> al;
2257 if ((al = find_processor_automation_curve (processor, param)) != 0) {
2260 add_processor_automation_curve (processor, param);
2266 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
2268 using namespace Menu_Helpers;
2272 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
2274 _automation_tracks[param] = track;
2276 /* existing state overrides "show" argument */
2277 string s = track->gui_property ("visible");
2279 show = string_is_affirmative (s);
2282 /* this might or might not change the visibility status, so don't rely on it */
2283 track->set_marked_for_display (show);
2285 if (show && !no_redraw) {
2289 if (!ARDOUR::parameter_is_midi((AutomationType)param.type())) {
2290 /* MIDI-related parameters are always in the menu, there's no
2291 reason to rebuild the menu just because we added a automation
2292 lane for one of them. But if we add a non-MIDI automation
2293 lane, then we need to invalidate the display menu.
2295 delete display_menu;
2301 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2303 boost::shared_ptr<Processor> processor (p.lock ());
2305 if (!processor || !processor->display_to_user ()) {
2309 /* we use this override to veto the Amp processor from the plugin menu,
2310 as its automation lane can be accessed using the special "Fader" menu
2314 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
2318 using namespace Menu_Helpers;
2319 ProcessorAutomationInfo *rai;
2320 list<ProcessorAutomationInfo*>::iterator x;
2322 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2324 if (automatable.empty()) {
2328 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2329 if ((*x)->processor == processor) {
2334 if (x == processor_automation.end()) {
2336 rai = new ProcessorAutomationInfo (processor);
2337 processor_automation.push_back (rai);
2345 /* any older menu was deleted at the top of processors_changed()
2346 when we cleared the subplugin menu.
2349 rai->menu = manage (new Menu);
2350 MenuList& items = rai->menu->items();
2351 rai->menu->set_name ("ArdourContextMenu");
2355 std::set<Evoral::Parameter> has_visible_automation;
2356 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2358 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2360 ProcessorAutomationNode* pan;
2361 Gtk::CheckMenuItem* mitem;
2363 string name = processor->describe_parameter (*i);
2365 items.push_back (CheckMenuElem (name));
2366 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2368 _subplugin_menu_map[*i] = mitem;
2370 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2371 mitem->set_active(true);
2374 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2378 pan = new ProcessorAutomationNode (*i, mitem, *this);
2380 rai->lines.push_back (pan);
2384 pan->menu_item = mitem;
2388 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2391 /* add the menu for this processor, because the subplugin
2392 menu is always cleared at the top of processors_changed().
2393 this is the result of some poor design in gtkmm and/or
2397 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2402 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2403 RouteTimeAxisView::ProcessorAutomationNode* pan)
2405 bool showit = pan->menu_item->get_active();
2406 bool redraw = false;
2408 if (pan->view == 0 && showit) {
2409 add_processor_automation_curve (rai->processor, pan->what);
2413 if (pan->view && pan->view->set_marked_for_display (showit)) {
2417 if (redraw && !no_redraw) {
2423 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2425 if (c.type == RouteProcessorChange::MeterPointChange) {
2426 /* nothing to do if only the meter point has changed */
2430 using namespace Menu_Helpers;
2432 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2433 (*i)->valid = false;
2436 setup_processor_menu_and_curves ();
2438 bool deleted_processor_automation = false;
2440 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2442 list<ProcessorAutomationInfo*>::iterator tmp;
2450 processor_automation.erase (i);
2451 deleted_processor_automation = true;
2458 if (deleted_processor_automation && !no_redraw) {
2463 boost::shared_ptr<AutomationLine>
2464 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2466 ProcessorAutomationNode* pan;
2468 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2474 return boost::shared_ptr<AutomationLine>();
2478 RouteTimeAxisView::reset_processor_automation_curves ()
2480 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2486 RouteTimeAxisView::can_edit_name () const
2488 /* we do not allow track name changes if it is record enabled
2490 return !_route->record_enabled();
2494 RouteTimeAxisView::blink_rec_display (bool onoff)
2496 RouteUI::blink_rec_display (onoff);
2500 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2502 if (_ignore_set_layer_display) {
2506 if (apply_to_selection) {
2507 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2511 _view->set_layer_display (d);
2514 set_gui_property (X_("layer-display"), enum_2_string (d));
2519 RouteTimeAxisView::layer_display () const
2522 return _view->layer_display ();
2525 /* we don't know, since we don't have a _view, so just return something */
2531 boost::shared_ptr<AutomationTimeAxisView>
2532 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2534 AutomationTracks::iterator i = _automation_tracks.find(param);
2535 if (i != _automation_tracks.end()) {
2538 return boost::shared_ptr<AutomationTimeAxisView>();
2543 RouteTimeAxisView::fast_update ()
2545 gm.get_level_meter().update_meters ();
2549 RouteTimeAxisView::hide_meter ()
2552 gm.get_level_meter().hide_meters ();
2556 RouteTimeAxisView::show_meter ()
2562 RouteTimeAxisView::reset_meter ()
2564 if (ARDOUR_UI::config()->get_show_track_meters()) {
2565 int meter_width = 3;
2566 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2569 gm.get_level_meter().setup_meters (height - 9, meter_width);
2576 RouteTimeAxisView::clear_meter ()
2578 gm.get_level_meter().clear_meters ();
2582 RouteTimeAxisView::meter_changed ()
2584 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2586 if (_route && !no_redraw && ARDOUR_UI::config()->get_show_track_meters()) {
2589 // reset peak when meter point changes
2590 gm.reset_peak_display();
2594 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2597 if (_route && !no_redraw) {
2603 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2605 using namespace Menu_Helpers;
2607 if (!_underlay_streams.empty()) {
2608 MenuList& parent_items = parent_menu->items();
2609 Menu* gs_menu = manage (new Menu);
2610 gs_menu->set_name ("ArdourContextMenu");
2611 MenuList& gs_items = gs_menu->items();
2613 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2615 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2616 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2617 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2623 RouteTimeAxisView::set_underlay_state()
2625 if (!underlay_xml_node) {
2629 XMLNodeList nlist = underlay_xml_node->children();
2630 XMLNodeConstIterator niter;
2631 XMLNode *child_node;
2633 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2634 child_node = *niter;
2636 if (child_node->name() != "Underlay") {
2640 XMLProperty* prop = child_node->property ("id");
2642 PBD::ID id (prop->value());
2644 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2647 add_underlay(v->view(), false);
2656 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2662 RouteTimeAxisView& other = v->trackview();
2664 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2665 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2666 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2667 abort(); /*NOTREACHED*/
2670 _underlay_streams.push_back(v);
2671 other._underlay_mirrors.push_back(this);
2673 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2675 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2677 if (!underlay_xml_node) {
2678 underlay_xml_node = xml_node->add_child("Underlays");
2681 XMLNode* node = underlay_xml_node->add_child("Underlay");
2682 XMLProperty* prop = node->add_property("id");
2683 prop->set_value(v->trackview().route()->id().to_s());
2690 RouteTimeAxisView::remove_underlay (StreamView* v)
2696 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2697 RouteTimeAxisView& other = v->trackview();
2699 if (it != _underlay_streams.end()) {
2700 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2702 if (gm == other._underlay_mirrors.end()) {
2703 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2704 abort(); /*NOTREACHED*/
2707 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2709 _underlay_streams.erase(it);
2710 other._underlay_mirrors.erase(gm);
2712 if (underlay_xml_node) {
2713 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2719 RouteTimeAxisView::set_button_names ()
2721 if (_route && _route->solo_safe()) {
2722 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2724 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2726 if (Config->get_solo_control_is_listen_control()) {
2727 switch (Config->get_listen_position()) {
2728 case AfterFaderListen:
2729 solo_button->set_text (S_("AfterFader|A"));
2730 ARDOUR_UI::instance()->set_tip (*solo_button, _("After-fade listen (AFL)"));
2732 case PreFaderListen:
2733 solo_button->set_text (S_("PreFader|P"));
2734 ARDOUR_UI::instance()->set_tip (*solo_button, _("Pre-fade listen (PFL)"));
2738 solo_button->set_text (S_("Solo|S"));
2739 ARDOUR_UI::instance()->set_tip (*solo_button, _("Solo"));
2741 mute_button->set_text (S_("Mute|M"));
2745 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2747 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2748 if (i != _main_automation_menu_map.end()) {
2752 i = _subplugin_menu_map.find (param);
2753 if (i != _subplugin_menu_map.end()) {
2761 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2763 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2765 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2769 gain_track.reset (new AutomationTimeAxisView (_session,
2770 _route, _route->amp(), c, param,
2775 _route->amp()->describe_parameter(param)));
2778 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2781 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2785 RouteTimeAxisView::create_trim_automation_child (const Evoral::Parameter& param, bool show)
2787 boost::shared_ptr<AutomationControl> c = _route->trim()->gain_control();
2788 if (!c || ! _route->trim()->active()) {
2792 trim_track.reset (new AutomationTimeAxisView (_session,
2793 _route, _route->trim(), c, param,
2798 _route->trim()->describe_parameter(param)));
2801 _view->foreach_regionview (sigc::mem_fun (*trim_track.get(), &TimeAxisView::add_ghost));
2804 add_automation_child (Evoral::Parameter(TrimAutomation), trim_track, show);
2808 RouteTimeAxisView::create_mute_automation_child (const Evoral::Parameter& param, bool show)
2810 boost::shared_ptr<AutomationControl> c = _route->mute_control();
2812 error << "Route has no mute automation, unable to add automation track view." << endmsg;
2816 mute_track.reset (new AutomationTimeAxisView (_session,
2817 _route, _route, c, param,
2822 _route->describe_parameter(param)));
2825 _view->foreach_regionview (sigc::mem_fun (*mute_track.get(), &TimeAxisView::add_ghost));
2828 add_automation_child (Evoral::Parameter(MuteAutomation), mute_track, show);
2832 void add_region_to_list (RegionView* rv, RegionList* l)
2834 l->push_back (rv->region());
2838 RouteTimeAxisView::combine_regions ()
2840 /* as of may 2011, we do not offer uncombine for MIDI tracks
2843 if (!is_audio_track()) {
2851 RegionList selected_regions;
2852 boost::shared_ptr<Playlist> playlist = track()->playlist();
2854 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2856 if (selected_regions.size() < 2) {
2860 playlist->clear_changes ();
2861 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2863 _session->add_command (new StatefulDiffCommand (playlist));
2864 /* make the new region be selected */
2866 return _view->find_view (compound_region);
2870 RouteTimeAxisView::uncombine_regions ()
2872 /* as of may 2011, we do not offer uncombine for MIDI tracks
2874 if (!is_audio_track()) {
2882 RegionList selected_regions;
2883 boost::shared_ptr<Playlist> playlist = track()->playlist();
2885 /* have to grab selected regions first because the uncombine is going
2886 * to change that in the middle of the list traverse
2889 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2891 playlist->clear_changes ();
2893 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2894 playlist->uncombine (*i);
2897 _session->add_command (new StatefulDiffCommand (playlist));
2901 RouteTimeAxisView::state_id() const
2903 return string_compose ("rtav %1", _route->id().to_s());
2908 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2910 TimeAxisView::remove_child (c);
2912 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2914 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2915 if (i->second == a) {
2916 _automation_tracks.erase (i);