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/processor.h"
49 #include "ardour/profile.h"
50 #include "ardour/route_group.h"
51 #include "ardour/session.h"
52 #include "ardour/session_playlists.h"
54 #include "evoral/Parameter.hpp"
56 #include "canvas/debug.h"
58 #include "ardour_ui.h"
59 #include "ardour_button.h"
61 #include "global_signals.h"
62 #include "route_time_axis.h"
63 #include "automation_time_axis.h"
65 #include "gui_thread.h"
67 #include "playlist_selector.h"
68 #include "point_selection.h"
70 #include "public_editor.h"
71 #include "region_view.h"
72 #include "rgb_macros.h"
73 #include "selection.h"
74 #include "streamview.h"
76 #include "route_group_menu.h"
78 #include "ardour/track.h"
82 using namespace ARDOUR;
83 using namespace ARDOUR_UI_UTILS;
85 using namespace Gtkmm2ext;
87 using namespace Editing;
91 RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, ArdourCanvas::Canvas& canvas)
94 , TimeAxisView(sess,ed,(TimeAxisView*) 0, canvas)
96 , parent_canvas (canvas)
99 , route_group_button (_("G"))
100 , playlist_button (_("P"))
101 , automation_button (_("A"))
102 , automation_action_menu (0)
103 , plugins_submenu_item (0)
104 , route_group_menu (0)
105 , playlist_action_menu (0)
107 , color_mode_menu (0)
108 , gm (sess, true, 75, 20)
109 , _ignore_set_layer_display (false)
111 number_label.set_name("route button");
112 number_label.set_alignment(.5, .5);
113 number_label.set_fallthrough_to_parent (true);
115 sess->config.ParameterChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::parameter_changed, this, _1), gui_context());
119 RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
121 RouteUI::set_route (rt);
123 CANVAS_DEBUG_NAME (_canvas_display, string_compose ("main for %1", rt->name()));
124 CANVAS_DEBUG_NAME (selection_group, string_compose ("selections for %1", rt->name()));
125 CANVAS_DEBUG_NAME (_ghost_group, string_compose ("ghosts for %1", rt->name()));
128 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
131 gm.set_controls (_route, _route->shared_peak_meter(), _route->amp());
132 gm.get_level_meter().set_no_show_all();
133 gm.get_level_meter().setup_meters(50, meter_width);
134 gm.update_gain_sensitive ();
136 string str = gui_property ("height");
138 set_height (atoi (str));
140 set_height (preset_height (HeightNormal));
143 if (!_route->is_auditioner()) {
144 if (gui_property ("visible").empty()) {
145 set_gui_property ("visible", true);
148 set_gui_property ("visible", false);
152 update_solo_display ();
154 timestretch_rect = 0;
157 ignore_toggle = false;
159 route_group_button.set_name ("route button");
160 playlist_button.set_name ("route button");
161 automation_button.set_name ("route button");
163 route_group_button.signal_button_release_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
164 playlist_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click));
165 automation_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click));
171 switch (track()->mode()) {
173 case ARDOUR::NonLayered:
174 rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
176 case ARDOUR::Destructive:
177 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
181 controls_table.attach (*rec_enable_button, 0, 1, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
183 if (is_midi_track()) {
184 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
185 gm.set_fader_name ("MidiTrackFader");
187 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record"));
188 gm.set_fader_name ("AudioTrackFader");
191 rec_enable_button->set_sensitive (_session->writable());
193 /* set playlist button tip to the current playlist, and make it update when it changes */
194 update_playlist_tip ();
195 track()->PlaylistChanged.connect (*this, invalidator (*this), ui_bind(&RouteTimeAxisView::update_playlist_tip, this), gui_context());
198 gm.set_fader_name ("AudioBusFader");
201 Gtk::VBox *mtrbox = manage(new Gtk::VBox());
202 mtrbox->pack_start(gm.get_level_meter(), false, false, 2);
203 top_hbox.pack_end(*mtrbox, false, false, 4);
206 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
207 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
208 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
209 _route->track_number_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::label_view, this), gui_context());
211 controls_table.attach (*mute_button, 1, 2, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
213 if (!_route->is_master()) {
214 controls_table.attach (*solo_button, 2, 3, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
217 if (!ARDOUR::Profile->get_trx()) {
218 controls_table.attach (route_group_button, 2, 3, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
219 name_table.attach (gm.get_gain_slider(), 0, 1, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 2);
222 ARDOUR_UI::instance()->set_tip(*solo_button,_("Solo"));
223 ARDOUR_UI::instance()->set_tip(*mute_button,_("Mute"));
224 ARDOUR_UI::instance()->set_tip(route_group_button, _("Route Group"));
226 if (is_midi_track()) {
227 ARDOUR_UI::instance()->set_tip(automation_button, _("MIDI Controllers and Automation"));
229 ARDOUR_UI::instance()->set_tip(automation_button, _("Automation"));
232 update_track_number_visibility();
235 if (!ARDOUR::Profile->get_trx()) {
236 controls_table.attach (automation_button, 1, 2, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
239 if (!ARDOUR::Profile->get_trx() && is_track() && track()->mode() == ARDOUR::Normal) {
240 controls_table.attach (playlist_button, 0, 1, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
245 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
246 _route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::route_property_changed, this, _1), gui_context());
250 str = gui_property ("layer-display");
252 set_layer_display (LayerDisplay (string_2_enum (str, _view->layer_display ())));
255 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
256 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
258 /* pick up the correct freeze state */
263 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_pixel));
264 ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
266 PropertyList* plist = new PropertyList();
268 plist->add (ARDOUR::Properties::mute, true);
269 plist->add (ARDOUR::Properties::solo, true);
271 route_group_menu = new RouteGroupMenu (_session, plist);
273 gm.get_gain_slider().signal_scroll_event().connect(sigc::mem_fun(*this, &RouteTimeAxisView::controls_ebox_scroll), false);
275 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
278 RouteTimeAxisView::~RouteTimeAxisView ()
280 CatchDeletion (this);
282 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
286 delete playlist_action_menu;
287 playlist_action_menu = 0;
292 _automation_tracks.clear ();
294 delete route_group_menu;
298 RouteTimeAxisView::post_construct ()
300 /* map current state of the route */
302 update_diskstream_display ();
303 setup_processor_menu_and_curves ();
304 reset_processor_automation_curves ();
307 /** Set up the processor menu for the current set of processors, and
308 * display automation curves for any parameters which have data.
311 RouteTimeAxisView::setup_processor_menu_and_curves ()
313 _subplugin_menu_map.clear ();
314 subplugin_menu.items().clear ();
315 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
316 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
320 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
322 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
323 if (_route->route_group()) {
324 _route->route_group()->remove (_route);
330 r.push_back (route ());
332 route_group_menu->build (r);
333 route_group_menu->menu()->popup (ev->button, ev->time);
339 RouteTimeAxisView::playlist_changed ()
345 RouteTimeAxisView::label_view ()
347 string x = _route->name ();
348 if (x != name_label.get_text ()) {
349 name_label.set_text (x);
351 const int64_t track_number = _route->track_number ();
352 if (track_number == 0) {
353 number_label.set_text ("");
355 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
360 RouteTimeAxisView::update_track_number_visibility ()
362 bool show_label = _session->config.get_track_name_number();
364 if (_route && _route->is_master()) {
368 if (number_label.get_parent()) {
369 name_hbox.remove (number_label);
372 // controls_table.resize ( 2, 4 );
373 name_hbox.pack_start(number_label, false, false, 2);
374 // controls_table.attach (number_label, 3, 4, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
375 const int tnw = std::max(2u, _session->track_number_decimals()) * 8; // TODO 8 = max_width_of_digit_0_to_9()
376 number_label.set_size_request(3 + tnw, -1);
377 number_label.show ();
379 // controls_table.resize ( 2, 3 );
380 number_label.hide ();
385 RouteTimeAxisView::parameter_changed (string const & p)
387 if (p == "track-name-number") {
388 update_track_number_visibility();
393 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
395 if (what_changed.contains (ARDOUR::Properties::name)) {
401 RouteTimeAxisView::take_name_changed (void *src)
409 RouteTimeAxisView::playlist_click ()
411 build_playlist_menu ();
412 conditionally_add_to_selection ();
413 playlist_action_menu->popup (1, gtk_get_current_event_time());
417 RouteTimeAxisView::automation_click ()
419 conditionally_add_to_selection ();
420 build_automation_action_menu (false);
421 automation_action_menu->popup (1, gtk_get_current_event_time());
425 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
427 using namespace Menu_Helpers;
429 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
430 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
433 detach_menu (subplugin_menu);
435 _main_automation_menu_map.clear ();
436 delete automation_action_menu;
437 automation_action_menu = new Menu;
439 MenuList& items = automation_action_menu->items();
441 automation_action_menu->set_name ("ArdourContextMenu");
443 items.push_back (MenuElem (_("Show All Automation"),
444 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
446 items.push_back (MenuElem (_("Show Existing Automation"),
447 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
449 items.push_back (MenuElem (_("Hide All Automation"),
450 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
452 /* Attach the plugin submenu. It may have previously been used elsewhere,
453 so it was detached above
456 if (!subplugin_menu.items().empty()) {
457 items.push_back (SeparatorElem ());
458 items.push_back (MenuElem (_("Processor automation"), subplugin_menu));
459 items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);;
464 RouteTimeAxisView::build_display_menu ()
466 using namespace Menu_Helpers;
470 TimeAxisView::build_display_menu ();
472 /* now fill it with our stuff */
474 MenuList& items = display_menu->items();
475 display_menu->set_name ("ArdourContextMenu");
477 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
479 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
481 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
483 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
485 items.push_back (SeparatorElem());
488 detach_menu (*_size_menu);
491 items.push_back (MenuElem (_("Height"), *_size_menu));
493 items.push_back (SeparatorElem());
495 if (!Profile->get_sae()) {
496 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
497 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
498 items.push_back (SeparatorElem());
501 // Hook for derived classes to add type specific stuff
502 append_extra_display_menu_items ();
506 Menu* layers_menu = manage (new Menu);
507 MenuList &layers_items = layers_menu->items();
508 layers_menu->set_name("ArdourContextMenu");
510 RadioMenuItem::Group layers_group;
512 /* Find out how many overlaid/stacked tracks we have in the selection */
516 TrackSelection const & s = _editor.get_selection().tracks;
517 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
518 StreamView* v = (*i)->view ();
523 switch (v->layer_display ()) {
534 /* We're not connecting to signal_toggled() here; in the case where these two items are
535 set to be in the `inconsistent' state, it seems that one or other will end up active
536 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
537 select the active one, no toggled signal is emitted so nothing happens.
540 _ignore_set_layer_display = true;
542 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
543 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
544 i->set_active (overlaid != 0 && stacked == 0);
545 i->set_inconsistent (overlaid != 0 && stacked != 0);
546 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
548 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
549 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
550 i->set_active (overlaid == 0 && stacked != 0);
551 i->set_inconsistent (overlaid != 0 && stacked != 0);
552 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
554 _ignore_set_layer_display = false;
556 items.push_back (MenuElem (_("Layers"), *layers_menu));
558 if (!Profile->get_sae()) {
560 Menu* alignment_menu = manage (new Menu);
561 MenuList& alignment_items = alignment_menu->items();
562 alignment_menu->set_name ("ArdourContextMenu");
564 RadioMenuItem::Group align_group;
566 /* Same verbose hacks as for the layering options above */
572 boost::shared_ptr<Track> first_track;
574 TrackSelection const & s = _editor.get_selection().tracks;
575 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
576 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
577 if (!r || !r->is_track ()) {
582 first_track = r->track();
585 switch (r->track()->alignment_choice()) {
589 switch (r->track()->alignment_style()) {
590 case ExistingMaterial:
598 case UseExistingMaterial:
614 inconsistent = false;
623 if (!inconsistent && first_track) {
625 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
626 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
627 i->set_active (automatic != 0 && existing == 0 && capture == 0);
628 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
630 switch (first_track->alignment_choice()) {
632 switch (first_track->alignment_style()) {
633 case ExistingMaterial:
634 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
637 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
645 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
646 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
647 i->set_active (existing != 0 && capture == 0 && automatic == 0);
648 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
650 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
651 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
652 i->set_active (existing == 0 && capture != 0 && automatic == 0);
653 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
655 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
661 Menu* mode_menu = manage (new Menu);
662 MenuList& mode_items = mode_menu->items ();
663 mode_menu->set_name ("ArdourContextMenu");
665 RadioMenuItem::Group mode_group;
671 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
672 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
673 if (!r || !r->is_track ()) {
677 switch (r->track()->mode()) {
690 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
691 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
692 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
693 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
694 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
696 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
697 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
698 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
699 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
700 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
702 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
703 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
704 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
705 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
706 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
708 items.push_back (MenuElem (_("Mode"), *mode_menu));
712 items.push_back (SeparatorElem());
714 build_playlist_menu ();
715 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
716 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
719 route_group_menu->detach ();
722 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
723 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
725 r.push_back (rtv->route ());
730 r.push_back (route ());
733 route_group_menu->build (r);
734 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
736 build_automation_action_menu (true);
737 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
739 items.push_back (SeparatorElem());
743 TrackSelection const & s = _editor.get_selection().tracks;
744 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
745 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
750 if (r->route()->active()) {
757 items.push_back (CheckMenuElem (_("Active")));
758 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
759 bool click_sets_active = true;
760 if (active > 0 && inactive == 0) {
761 i->set_active (true);
762 click_sets_active = false;
763 } else if (active > 0 && inactive > 0) {
764 i->set_inconsistent (true);
766 i->set_sensitive(! _session->transport_rolling());
767 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
769 items.push_back (SeparatorElem());
770 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
771 if (!Profile->get_sae()) {
772 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
774 items.push_front (SeparatorElem());
775 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
780 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
782 if (apply_to_selection) {
783 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
786 bool needs_bounce = false;
788 if (!track()->can_use_mode (mode, needs_bounce)) {
794 cerr << "would bounce this one\n";
799 track()->set_mode (mode);
801 rec_enable_button->remove ();
804 case ARDOUR::NonLayered:
806 rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
807 rec_enable_button->set_text (string());
809 case ARDOUR::Destructive:
810 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
811 rec_enable_button->set_text (string());
815 rec_enable_button->show_all ();
820 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
822 TimeAxisView::show_timestretch (start, end, layers, layer);
832 /* check that the time selection was made in our route, or our route group.
833 remember that route_group() == 0 implies the route is *not* in a edit group.
836 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
837 /* this doesn't apply to us */
841 /* ignore it if our edit group is not active */
843 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
848 if (timestretch_rect == 0) {
849 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
850 timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchFill());
851 timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchOutline());
854 timestretch_rect->show ();
855 timestretch_rect->raise_to_top ();
857 double const x1 = start / _editor.get_current_zoom();
858 double const x2 = (end - 1) / _editor.get_current_zoom();
860 timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
861 x2, current_height() * (layers - layer) / layers));
865 RouteTimeAxisView::hide_timestretch ()
867 TimeAxisView::hide_timestretch ();
869 if (timestretch_rect) {
870 timestretch_rect->hide ();
875 RouteTimeAxisView::show_selection (TimeSelection& ts)
879 /* ignore it if our edit group is not active or if the selection was started
880 in some other track or route group (remember that route_group() == 0 means
881 that the track is not in an route group).
884 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
885 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
891 TimeAxisView::show_selection (ts);
895 RouteTimeAxisView::set_height (uint32_t h)
898 bool height_changed = (height == 0) || (h != height);
901 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
904 gm.get_level_meter().setup_meters (gmlen, meter_width);
906 TimeAxisView::set_height (h);
909 _view->set_height ((double) current_height());
912 if (height >= preset_height (HeightNormal)) {
916 gm.get_gain_slider().show();
918 if (!_route || _route->is_monitor()) {
923 if (rec_enable_button)
924 rec_enable_button->show();
926 route_group_button.show();
927 automation_button.show();
929 if (is_track() && track()->mode() == ARDOUR::Normal) {
930 playlist_button.show();
937 gm.get_gain_slider().hide();
939 if (!_route || _route->is_monitor()) {
944 if (rec_enable_button)
945 rec_enable_button->show();
947 route_group_button.hide ();
948 automation_button.hide ();
950 if (is_track() && track()->mode() == ARDOUR::Normal) {
951 playlist_button.hide ();
956 if (height_changed && !no_redraw) {
957 /* only emit the signal if the height really changed */
963 RouteTimeAxisView::route_color_changed ()
966 _view->apply_color (color(), StreamView::RegionColor);
969 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
973 RouteTimeAxisView::reset_samples_per_pixel ()
975 set_samples_per_pixel (_editor.get_current_zoom());
979 RouteTimeAxisView::set_samples_per_pixel (double fpp)
984 speed = track()->speed();
988 _view->set_samples_per_pixel (fpp * speed);
991 TimeAxisView::set_samples_per_pixel (fpp * speed);
995 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
997 if (!mitem->get_active()) {
998 /* this is one of the two calls made when these radio menu items change status. this one
999 is for the item that became inactive, and we want to ignore it.
1004 if (apply_to_selection) {
1005 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
1008 track()->set_align_choice (choice);
1014 RouteTimeAxisView::rename_current_playlist ()
1016 ArdourPrompter prompter (true);
1019 boost::shared_ptr<Track> tr = track();
1020 if (!tr || tr->destructive()) {
1024 boost::shared_ptr<Playlist> pl = tr->playlist();
1029 prompter.set_title (_("Rename Playlist"));
1030 prompter.set_prompt (_("New name for playlist:"));
1031 prompter.set_initial_text (pl->name());
1032 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1033 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1035 switch (prompter.run ()) {
1036 case Gtk::RESPONSE_ACCEPT:
1037 prompter.get_result (name);
1038 if (name.length()) {
1039 pl->set_name (name);
1049 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1051 std::string ret (basename);
1053 std::string const group_string = "." + route_group()->name() + ".";
1055 // iterate through all playlists
1057 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1058 std::string tmp = (*i)->name();
1060 std::string::size_type idx = tmp.find(group_string);
1061 // find those which belong to this group
1062 if (idx != string::npos) {
1063 tmp = tmp.substr(idx + group_string.length());
1065 // and find the largest current number
1067 if (x > maxnumber) {
1076 snprintf (buf, sizeof(buf), "%d", maxnumber);
1078 ret = this->name() + "." + route_group()->name () + "." + buf;
1084 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1088 boost::shared_ptr<Track> tr = track ();
1089 if (!tr || tr->destructive()) {
1093 boost::shared_ptr<const Playlist> pl = tr->playlist();
1100 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1101 name = resolve_new_group_playlist_name(name, playlists_before_op);
1104 while (_session->playlists->by_name(name)) {
1105 name = Playlist::bump_name (name, *_session);
1108 // TODO: The prompter "new" button should be de-activated if the user
1109 // specifies a playlist name which already exists in the session.
1113 ArdourPrompter prompter (true);
1115 prompter.set_title (_("New Copy Playlist"));
1116 prompter.set_prompt (_("Name for new playlist:"));
1117 prompter.set_initial_text (name);
1118 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1119 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1120 prompter.show_all ();
1122 switch (prompter.run ()) {
1123 case Gtk::RESPONSE_ACCEPT:
1124 prompter.get_result (name);
1132 if (name.length()) {
1133 tr->use_copy_playlist ();
1134 tr->playlist()->set_name (name);
1139 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1143 boost::shared_ptr<Track> tr = track ();
1144 if (!tr || tr->destructive()) {
1148 boost::shared_ptr<const Playlist> pl = tr->playlist();
1155 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1156 name = resolve_new_group_playlist_name(name,playlists_before_op);
1159 while (_session->playlists->by_name(name)) {
1160 name = Playlist::bump_name (name, *_session);
1166 ArdourPrompter prompter (true);
1168 prompter.set_title (_("New Playlist"));
1169 prompter.set_prompt (_("Name for new playlist:"));
1170 prompter.set_initial_text (name);
1171 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1172 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1174 switch (prompter.run ()) {
1175 case Gtk::RESPONSE_ACCEPT:
1176 prompter.get_result (name);
1184 if (name.length()) {
1185 tr->use_new_playlist ();
1186 tr->playlist()->set_name (name);
1191 RouteTimeAxisView::clear_playlist ()
1193 boost::shared_ptr<Track> tr = track ();
1194 if (!tr || tr->destructive()) {
1198 boost::shared_ptr<Playlist> pl = tr->playlist();
1203 _editor.clear_playlist (pl);
1207 RouteTimeAxisView::speed_changed ()
1209 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1213 RouteTimeAxisView::update_diskstream_display ()
1223 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1225 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1227 /* special case: select/deselect all tracks */
1228 if (_editor.get_selection().selected (this)) {
1229 _editor.get_selection().clear_tracks ();
1231 _editor.select_all_tracks ();
1237 switch (ArdourKeyboard::selection_type (ev->state)) {
1238 case Selection::Toggle:
1239 _editor.get_selection().toggle (this);
1242 case Selection::Set:
1243 _editor.get_selection().set (this);
1246 case Selection::Extend:
1247 _editor.extend_selection_to_track (*this);
1250 case Selection::Add:
1251 _editor.get_selection().add (this);
1257 RouteTimeAxisView::set_selected_points (PointSelection& points)
1259 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1260 (*i)->set_selected_points (points);
1265 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1268 _view->set_selected_regionviews (regions);
1272 /** Add the selectable things that we have to a list.
1273 * @param results List to add things to.
1276 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1281 speed = track()->speed();
1284 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1285 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1287 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1288 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1291 /* pick up visible automation tracks */
1293 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1294 if (!(*i)->hidden()) {
1295 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1301 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1304 _view->get_inverted_selectables (sel, results);
1307 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1308 if (!(*i)->hidden()) {
1309 (*i)->get_inverted_selectables (sel, results);
1317 RouteTimeAxisView::route_group () const
1319 return _route->route_group();
1323 RouteTimeAxisView::name() const
1325 return _route->name();
1328 boost::shared_ptr<Playlist>
1329 RouteTimeAxisView::playlist () const
1331 boost::shared_ptr<Track> tr;
1333 if ((tr = track()) != 0) {
1334 return tr->playlist();
1336 return boost::shared_ptr<Playlist> ();
1341 RouteTimeAxisView::name_entry_changed ()
1343 TimeAxisView::name_entry_changed ();
1345 string x = name_entry->get_text ();
1347 if (x == _route->name()) {
1351 strip_whitespace_edges (x);
1353 if (x.length() == 0) {
1354 name_entry->set_text (_route->name());
1358 if (_session->route_name_internal (x)) {
1359 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1361 name_entry->grab_focus ();
1362 } else if (RouteUI::verify_new_route_name (x)) {
1363 _route->set_name (x);
1365 name_entry->grab_focus ();
1369 boost::shared_ptr<Region>
1370 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1372 boost::shared_ptr<Playlist> pl = playlist ();
1375 return pl->find_next_region (pos, point, dir);
1378 return boost::shared_ptr<Region> ();
1382 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1384 boost::shared_ptr<Playlist> pl = playlist ();
1387 return pl->find_next_region_boundary (pos, dir);
1394 RouteTimeAxisView::fade_range (TimeSelection& selection)
1396 boost::shared_ptr<Playlist> what_we_got;
1397 boost::shared_ptr<Track> tr = track ();
1398 boost::shared_ptr<Playlist> playlist;
1401 /* route is a bus, not a track */
1405 playlist = tr->playlist();
1407 TimeSelection time (selection);
1408 float const speed = tr->speed();
1409 if (speed != 1.0f) {
1410 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1411 (*i).start = session_frame_to_track_frame((*i).start, speed);
1412 (*i).end = session_frame_to_track_frame((*i).end, speed);
1416 playlist->clear_changes ();
1417 playlist->clear_owned_changes ();
1419 playlist->fade_range (time);
1421 vector<Command*> cmds;
1422 playlist->rdiff (cmds);
1423 _session->add_commands (cmds);
1424 _session->add_command (new StatefulDiffCommand (playlist));
1429 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1431 boost::shared_ptr<Playlist> what_we_got;
1432 boost::shared_ptr<Track> tr = track ();
1433 boost::shared_ptr<Playlist> playlist;
1436 /* route is a bus, not a track */
1440 playlist = tr->playlist();
1442 TimeSelection time (selection.time);
1443 float const speed = tr->speed();
1444 if (speed != 1.0f) {
1445 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1446 (*i).start = session_frame_to_track_frame((*i).start, speed);
1447 (*i).end = session_frame_to_track_frame((*i).end, speed);
1451 playlist->clear_changes ();
1452 playlist->clear_owned_changes ();
1456 if (playlist->cut (time) != 0) {
1457 if (Config->get_edit_mode() == Ripple)
1458 playlist->ripple(time.start(), -time.length(), NULL);
1459 // no need to exclude any regions from rippling here
1461 vector<Command*> cmds;
1462 playlist->rdiff (cmds);
1463 _session->add_commands (cmds);
1465 _session->add_command (new StatefulDiffCommand (playlist));
1470 if ((what_we_got = playlist->cut (time)) != 0) {
1471 _editor.get_cut_buffer().add (what_we_got);
1472 if (Config->get_edit_mode() == Ripple)
1473 playlist->ripple(time.start(), -time.length(), NULL);
1474 // no need to exclude any regions from rippling here
1476 vector<Command*> cmds;
1477 playlist->rdiff (cmds);
1478 _session->add_commands (cmds);
1480 _session->add_command (new StatefulDiffCommand (playlist));
1484 if ((what_we_got = playlist->copy (time)) != 0) {
1485 _editor.get_cut_buffer().add (what_we_got);
1490 if ((what_we_got = playlist->cut (time)) != 0) {
1491 if (Config->get_edit_mode() == Ripple)
1492 playlist->ripple(time.start(), -time.length(), NULL);
1493 // no need to exclude any regions from rippling here
1495 vector<Command*> cmds;
1496 playlist->rdiff (cmds);
1497 _session->add_commands (cmds);
1498 _session->add_command (new StatefulDiffCommand (playlist));
1499 what_we_got->release ();
1506 RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
1512 boost::shared_ptr<Playlist> pl = playlist ();
1513 PlaylistSelection::iterator p;
1515 for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
1517 if (p == selection.playlists.end()) {
1521 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1523 if (track()->speed() != 1.0f) {
1524 pos = session_frame_to_track_frame (pos, track()->speed());
1525 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1528 pl->clear_changes ();
1529 if (Config->get_edit_mode() == Ripple) {
1530 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent_with_endspace();
1531 framecnt_t amount = extent.second - extent.first;
1532 pl->ripple(pos, amount * times, boost::shared_ptr<Region>());
1534 pl->paste (*p, pos, times);
1536 vector<Command*> cmds;
1538 _session->add_commands (cmds);
1540 _session->add_command (new StatefulDiffCommand (pl));
1546 struct PlaylistSorter {
1547 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1548 return a->sort_id() < b->sort_id();
1553 RouteTimeAxisView::build_playlist_menu ()
1555 using namespace Menu_Helpers;
1561 delete playlist_action_menu;
1562 playlist_action_menu = new Menu;
1563 playlist_action_menu->set_name ("ArdourContextMenu");
1565 MenuList& playlist_items = playlist_action_menu->items();
1566 playlist_action_menu->set_name ("ArdourContextMenu");
1567 playlist_items.clear();
1569 RadioMenuItem::Group playlist_group;
1570 boost::shared_ptr<Track> tr = track ();
1572 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1574 /* sort the playlists */
1576 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1578 /* add the playlists to the menu */
1579 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1580 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1581 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1582 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1584 if (tr->playlist()->id() == (*i)->id()) {
1590 playlist_items.push_back (SeparatorElem());
1591 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1592 playlist_items.push_back (SeparatorElem());
1594 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1595 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1596 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1599 // Use a label which tells the user what is happening
1600 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1601 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1605 playlist_items.push_back (SeparatorElem());
1606 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1607 playlist_items.push_back (SeparatorElem());
1609 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1613 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1615 assert (is_track());
1617 // exit if we were triggered by deactivating the old playlist
1618 if (!item->get_active()) {
1622 boost::shared_ptr<Playlist> pl (wpl.lock());
1628 if (track()->playlist() == pl) {
1629 // exit when use_playlist is called by the creation of the playlist menu
1630 // or the playlist choice is unchanged
1634 track()->use_playlist (pl);
1636 RouteGroup* rg = route_group();
1638 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1639 std::string group_string = "." + rg->name() + ".";
1641 std::string take_name = pl->name();
1642 std::string::size_type idx = take_name.find(group_string);
1644 if (idx == std::string::npos)
1647 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1649 boost::shared_ptr<RouteList> rl (rg->route_list());
1651 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1652 if ((*i) == this->route()) {
1656 std::string playlist_name = (*i)->name()+group_string+take_name;
1658 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1663 if (track->freeze_state() == Track::Frozen) {
1664 /* Don't change playlists of frozen tracks */
1668 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1670 // No playlist for this track for this take yet, make it
1671 track->use_new_playlist();
1672 track->playlist()->set_name(playlist_name);
1674 track->use_playlist(ipl);
1681 RouteTimeAxisView::update_playlist_tip ()
1683 RouteGroup* rg = route_group ();
1684 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1685 string group_string = "." + rg->name() + ".";
1687 string take_name = track()->playlist()->name();
1688 string::size_type idx = take_name.find(group_string);
1690 if (idx != string::npos) {
1691 /* find the bit containing the take number / name */
1692 take_name = take_name.substr (idx + group_string.length());
1694 /* set the playlist button tooltip to the take name */
1695 ARDOUR_UI::instance()->set_tip (
1697 string_compose(_("Take: %1.%2"),
1698 Glib::Markup::escape_text(rg->name()),
1699 Glib::Markup::escape_text(take_name))
1706 /* set the playlist button tooltip to the playlist name */
1707 ARDOUR_UI::instance()->set_tip (playlist_button, _("Playlist") + std::string(": ") + Glib::Markup::escape_text(track()->playlist()->name()));
1712 RouteTimeAxisView::show_playlist_selector ()
1714 _editor.playlist_selector().show_for (this);
1718 RouteTimeAxisView::map_frozen ()
1724 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1726 switch (track()->freeze_state()) {
1728 playlist_button.set_sensitive (false);
1729 rec_enable_button->set_sensitive (false);
1732 playlist_button.set_sensitive (true);
1733 rec_enable_button->set_sensitive (true);
1739 RouteTimeAxisView::color_handler ()
1741 //case cTimeStretchOutline:
1742 if (timestretch_rect) {
1743 timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchOutline());
1745 //case cTimeStretchFill:
1746 if (timestretch_rect) {
1747 timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchFill());
1753 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1754 * Will add track if necessary.
1757 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1759 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1760 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1763 /* it doesn't exist yet, so we don't care about the button state: just add it */
1764 create_automation_child (param, true);
1767 bool yn = menu->get_active();
1768 bool changed = false;
1770 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1772 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1773 will have done that for us.
1776 if (changed && !no_redraw) {
1784 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1786 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1792 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1794 if (menu && !_hidden) {
1795 ignore_toggle = true;
1796 menu->set_active (false);
1797 ignore_toggle = false;
1800 if (_route && !no_redraw) {
1807 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1809 if (apply_to_selection) {
1810 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1814 /* Show our automation */
1816 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1817 i->second->set_marked_for_display (true);
1819 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1822 menu->set_active(true);
1827 /* Show processor automation */
1829 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1830 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1831 if ((*ii)->view == 0) {
1832 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1835 (*ii)->menu_item->set_active (true);
1848 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1850 if (apply_to_selection) {
1851 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1855 /* Show our automation */
1857 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1858 if (i->second->has_automation()) {
1859 i->second->set_marked_for_display (true);
1861 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1863 menu->set_active(true);
1868 /* Show processor automation */
1870 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1871 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1872 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1873 (*ii)->menu_item->set_active (true);
1885 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1887 if (apply_to_selection) {
1888 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1892 /* Hide our automation */
1894 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1895 i->second->set_marked_for_display (false);
1897 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1900 menu->set_active (false);
1904 /* Hide processor automation */
1906 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1907 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1908 (*ii)->menu_item->set_active (false);
1919 RouteTimeAxisView::region_view_added (RegionView* rv)
1921 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1922 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1923 boost::shared_ptr<AutomationTimeAxisView> atv;
1925 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1930 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1931 (*i)->add_ghost(rv);
1935 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1937 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1943 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1945 parent.remove_processor_automation_node (this);
1949 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1952 remove_child (pan->view);
1956 RouteTimeAxisView::ProcessorAutomationNode*
1957 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1959 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1961 if ((*i)->processor == processor) {
1963 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1964 if ((*ii)->what == what) {
1974 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
1976 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1979 ProcessorAutomationNode* pan;
1981 if ((pan = find_processor_automation_node (processor, what)) == 0) {
1982 /* session state may never have been saved with new plugin */
1983 error << _("programming error: ")
1984 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
1985 processor->name(), what.type(), (int) what.channel(), what.id() )
1995 boost::shared_ptr<AutomationControl> control
1996 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
1998 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
1999 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
2000 _editor, *this, false, parent_canvas,
2001 processor->describe_parameter (what), processor->name()));
2003 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
2005 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
2008 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
2013 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
2016 pan->menu_item->set_active (false);
2025 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
2027 boost::shared_ptr<Processor> processor (p.lock ());
2029 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
2030 /* The Amp processor is a special case and is dealt with separately */
2034 set<Evoral::Parameter> existing;
2036 processor->what_has_data (existing);
2038 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
2040 Evoral::Parameter param (*i);
2041 boost::shared_ptr<AutomationLine> al;
2043 if ((al = find_processor_automation_curve (processor, param)) != 0) {
2046 add_processor_automation_curve (processor, param);
2052 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
2054 using namespace Menu_Helpers;
2058 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
2060 _automation_tracks[param] = track;
2062 /* existing state overrides "show" argument */
2063 string s = track->gui_property ("visible");
2065 show = string_is_affirmative (s);
2068 /* this might or might not change the visibility status, so don't rely on it */
2069 track->set_marked_for_display (show);
2071 if (show && !no_redraw) {
2075 if (!EventTypeMap::instance().is_midi_parameter(param)) {
2076 /* MIDI-related parameters are always in the menu, there's no
2077 reason to rebuild the menu just because we added a automation
2078 lane for one of them. But if we add a non-MIDI automation
2079 lane, then we need to invalidate the display menu.
2081 delete display_menu;
2087 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2089 boost::shared_ptr<Processor> processor (p.lock ());
2091 if (!processor || !processor->display_to_user ()) {
2095 /* we use this override to veto the Amp processor from the plugin menu,
2096 as its automation lane can be accessed using the special "Fader" menu
2100 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
2104 using namespace Menu_Helpers;
2105 ProcessorAutomationInfo *rai;
2106 list<ProcessorAutomationInfo*>::iterator x;
2108 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2110 if (automatable.empty()) {
2114 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2115 if ((*x)->processor == processor) {
2120 if (x == processor_automation.end()) {
2122 rai = new ProcessorAutomationInfo (processor);
2123 processor_automation.push_back (rai);
2131 /* any older menu was deleted at the top of processors_changed()
2132 when we cleared the subplugin menu.
2135 rai->menu = manage (new Menu);
2136 MenuList& items = rai->menu->items();
2137 rai->menu->set_name ("ArdourContextMenu");
2141 std::set<Evoral::Parameter> has_visible_automation;
2142 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2144 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2146 ProcessorAutomationNode* pan;
2147 Gtk::CheckMenuItem* mitem;
2149 string name = processor->describe_parameter (*i);
2151 items.push_back (CheckMenuElem (name));
2152 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2154 _subplugin_menu_map[*i] = mitem;
2156 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2157 mitem->set_active(true);
2160 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2164 pan = new ProcessorAutomationNode (*i, mitem, *this);
2166 rai->lines.push_back (pan);
2170 pan->menu_item = mitem;
2174 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2177 /* add the menu for this processor, because the subplugin
2178 menu is always cleared at the top of processors_changed().
2179 this is the result of some poor design in gtkmm and/or
2183 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2188 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2189 RouteTimeAxisView::ProcessorAutomationNode* pan)
2191 bool showit = pan->menu_item->get_active();
2192 bool redraw = false;
2194 if (pan->view == 0 && showit) {
2195 add_processor_automation_curve (rai->processor, pan->what);
2199 if (pan->view && pan->view->set_marked_for_display (showit)) {
2203 if (redraw && !no_redraw) {
2209 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2211 if (c.type == RouteProcessorChange::MeterPointChange) {
2212 /* nothing to do if only the meter point has changed */
2216 using namespace Menu_Helpers;
2218 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2219 (*i)->valid = false;
2222 setup_processor_menu_and_curves ();
2224 bool deleted_processor_automation = false;
2226 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2228 list<ProcessorAutomationInfo*>::iterator tmp;
2236 processor_automation.erase (i);
2237 deleted_processor_automation = true;
2244 if (deleted_processor_automation && !no_redraw) {
2249 boost::shared_ptr<AutomationLine>
2250 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2252 ProcessorAutomationNode* pan;
2254 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2260 return boost::shared_ptr<AutomationLine>();
2264 RouteTimeAxisView::reset_processor_automation_curves ()
2266 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2272 RouteTimeAxisView::can_edit_name () const
2274 /* we do not allow track name changes if it is record enabled
2276 return !_route->record_enabled();
2280 RouteTimeAxisView::blink_rec_display (bool onoff)
2282 RouteUI::blink_rec_display (onoff);
2286 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2288 if (_ignore_set_layer_display) {
2292 if (apply_to_selection) {
2293 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2297 _view->set_layer_display (d);
2300 set_gui_property (X_("layer-display"), enum_2_string (d));
2305 RouteTimeAxisView::layer_display () const
2308 return _view->layer_display ();
2311 /* we don't know, since we don't have a _view, so just return something */
2317 boost::shared_ptr<AutomationTimeAxisView>
2318 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2320 AutomationTracks::iterator i = _automation_tracks.find(param);
2321 if (i != _automation_tracks.end()) {
2324 return boost::shared_ptr<AutomationTimeAxisView>();
2329 RouteTimeAxisView::fast_update ()
2331 gm.get_level_meter().update_meters ();
2335 RouteTimeAxisView::hide_meter ()
2338 gm.get_level_meter().hide_meters ();
2342 RouteTimeAxisView::show_meter ()
2348 RouteTimeAxisView::reset_meter ()
2350 if (Config->get_show_track_meters()) {
2351 int meter_width = 3;
2352 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2355 gm.get_level_meter().setup_meters (height - 9, meter_width);
2362 RouteTimeAxisView::clear_meter ()
2364 gm.get_level_meter().clear_meters ();
2368 RouteTimeAxisView::meter_changed ()
2370 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2372 if (_route && !no_redraw) {
2375 // reset peak when meter point changes
2376 gm.reset_peak_display();
2380 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2383 if (_route && !no_redraw) {
2389 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2391 using namespace Menu_Helpers;
2393 if (!_underlay_streams.empty()) {
2394 MenuList& parent_items = parent_menu->items();
2395 Menu* gs_menu = manage (new Menu);
2396 gs_menu->set_name ("ArdourContextMenu");
2397 MenuList& gs_items = gs_menu->items();
2399 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2401 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2402 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2403 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2409 RouteTimeAxisView::set_underlay_state()
2411 if (!underlay_xml_node) {
2415 XMLNodeList nlist = underlay_xml_node->children();
2416 XMLNodeConstIterator niter;
2417 XMLNode *child_node;
2419 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2420 child_node = *niter;
2422 if (child_node->name() != "Underlay") {
2426 XMLProperty* prop = child_node->property ("id");
2428 PBD::ID id (prop->value());
2430 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2433 add_underlay(v->view(), false);
2442 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2448 RouteTimeAxisView& other = v->trackview();
2450 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2451 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2452 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2456 _underlay_streams.push_back(v);
2457 other._underlay_mirrors.push_back(this);
2459 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2461 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2463 if (!underlay_xml_node) {
2464 underlay_xml_node = xml_node->add_child("Underlays");
2467 XMLNode* node = underlay_xml_node->add_child("Underlay");
2468 XMLProperty* prop = node->add_property("id");
2469 prop->set_value(v->trackview().route()->id().to_s());
2476 RouteTimeAxisView::remove_underlay (StreamView* v)
2482 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2483 RouteTimeAxisView& other = v->trackview();
2485 if (it != _underlay_streams.end()) {
2486 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2488 if (gm == other._underlay_mirrors.end()) {
2489 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2493 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2495 _underlay_streams.erase(it);
2496 other._underlay_mirrors.erase(gm);
2498 if (underlay_xml_node) {
2499 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2505 RouteTimeAxisView::set_button_names ()
2507 if (_route && _route->solo_safe()) {
2508 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2510 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2512 if (Config->get_solo_control_is_listen_control()) {
2513 switch (Config->get_listen_position()) {
2514 case AfterFaderListen:
2515 solo_button->set_text (_("A"));
2516 ARDOUR_UI::instance()->set_tip (*solo_button, _("After-fade listen (AFL)"));
2518 case PreFaderListen:
2519 solo_button->set_text (_("P"));
2520 ARDOUR_UI::instance()->set_tip (*solo_button, _("Pre-fade listen (PFL)"));
2524 solo_button->set_text (_("S"));
2525 ARDOUR_UI::instance()->set_tip (*solo_button, _("Solo"));
2527 mute_button->set_text (_("M"));
2531 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2533 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2534 if (i != _main_automation_menu_map.end()) {
2538 i = _subplugin_menu_map.find (param);
2539 if (i != _subplugin_menu_map.end()) {
2547 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2549 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2551 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2555 gain_track.reset (new AutomationTimeAxisView (_session,
2556 _route, _route->amp(), c, param,
2561 _route->amp()->describe_parameter(param)));
2564 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2567 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2571 RouteTimeAxisView::create_mute_automation_child (const Evoral::Parameter& param, bool show)
2573 boost::shared_ptr<AutomationControl> c = _route->mute_control();
2575 error << "Route has no mute automation, unable to add automation track view." << endmsg;
2579 mute_track.reset (new AutomationTimeAxisView (_session,
2580 _route, _route, c, param,
2585 _route->describe_parameter(param)));
2588 _view->foreach_regionview (sigc::mem_fun (*mute_track.get(), &TimeAxisView::add_ghost));
2591 add_automation_child (Evoral::Parameter(MuteAutomation), mute_track, show);
2595 void add_region_to_list (RegionView* rv, RegionList* l)
2597 l->push_back (rv->region());
2601 RouteTimeAxisView::combine_regions ()
2603 /* as of may 2011, we do not offer uncombine for MIDI tracks
2606 if (!is_audio_track()) {
2614 RegionList selected_regions;
2615 boost::shared_ptr<Playlist> playlist = track()->playlist();
2617 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2619 if (selected_regions.size() < 2) {
2623 playlist->clear_changes ();
2624 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2626 _session->add_command (new StatefulDiffCommand (playlist));
2627 /* make the new region be selected */
2629 return _view->find_view (compound_region);
2633 RouteTimeAxisView::uncombine_regions ()
2635 /* as of may 2011, we do not offer uncombine for MIDI tracks
2637 if (!is_audio_track()) {
2645 RegionList selected_regions;
2646 boost::shared_ptr<Playlist> playlist = track()->playlist();
2648 /* have to grab selected regions first because the uncombine is going
2649 * to change that in the middle of the list traverse
2652 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2654 playlist->clear_changes ();
2656 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2657 playlist->uncombine (*i);
2660 _session->add_command (new StatefulDiffCommand (playlist));
2664 RouteTimeAxisView::state_id() const
2666 return string_compose ("rtav %1", _route->id().to_s());
2671 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2673 TimeAxisView::remove_child (c);
2675 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2677 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2678 if (i->second == a) {
2679 _automation_tracks.erase (i);