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/event_type_map.h"
47 #include "ardour/processor.h"
48 #include "ardour/profile.h"
49 #include "ardour/route_group.h"
50 #include "ardour/session.h"
51 #include "ardour/session_playlists.h"
52 #include "evoral/Parameter.hpp"
54 #include "ardour_ui.h"
55 #include "ardour_button.h"
57 #include "global_signals.h"
58 #include "route_time_axis.h"
59 #include "automation_time_axis.h"
60 #include "canvas_impl.h"
62 #include "gui_thread.h"
64 #include "playlist_selector.h"
65 #include "point_selection.h"
67 #include "public_editor.h"
68 #include "region_view.h"
69 #include "rgb_macros.h"
70 #include "selection.h"
71 #include "simplerect.h"
72 #include "streamview.h"
74 #include "route_group_menu.h"
76 #include "ardour/track.h"
80 using namespace ARDOUR;
82 using namespace Gtkmm2ext;
84 using namespace Editing;
88 RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, Canvas& canvas)
91 , TimeAxisView(sess,ed,(TimeAxisView*) 0, canvas)
93 , parent_canvas (canvas)
96 , route_group_button (_("g"))
97 , playlist_button (_("p"))
98 , automation_button (_("a"))
99 , automation_action_menu (0)
100 , plugins_submenu_item (0)
101 , route_group_menu (0)
102 , playlist_action_menu (0)
104 , color_mode_menu (0)
105 , gm (sess, true, 125, 18)
106 , _ignore_set_layer_display (false)
111 RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
113 RouteUI::set_route (rt);
115 gm.set_controls (_route, _route->shared_peak_meter(), _route->amp());
116 gm.get_level_meter().set_no_show_all();
117 gm.get_level_meter().setup_meters(50);
118 gm.update_gain_sensitive ();
120 string str = gui_property ("height");
122 set_height (atoi (str));
124 set_height (preset_height (HeightNormal));
127 if (!_route->is_hidden()) {
128 if (gui_property ("visible").empty()) {
129 set_gui_property ("visible", true);
132 set_gui_property ("visible", false);
136 update_solo_display ();
138 timestretch_rect = 0;
141 ignore_toggle = false;
143 route_group_button.set_name ("route button");
144 playlist_button.set_name ("route button");
145 automation_button.set_name ("route button");
147 route_group_button.signal_button_release_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
148 playlist_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click));
149 automation_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click));
155 switch (track()->mode()) {
157 case ARDOUR::NonLayered:
158 rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
160 case ARDOUR::Destructive:
161 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
165 controls_table.attach (*rec_enable_button, 5, 6, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
167 if (is_midi_track()) {
168 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
169 gm.set_fader_name ("MidiTrackFader");
171 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record"));
172 gm.set_fader_name ("AudioTrackFader");
175 rec_enable_button->set_sensitive (_session->writable());
177 /* set playlist button tip to the current playlist, and make it update when it changes */
178 update_playlist_tip ();
179 track()->PlaylistChanged.connect (*this, invalidator (*this), ui_bind(&RouteTimeAxisView::update_playlist_tip, this), gui_context());
182 gm.set_fader_name ("AudioBusFader");
185 controls_hbox.pack_start(gm.get_level_meter(), false, false);
186 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
187 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
188 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
190 controls_table.attach (*mute_button, 6, 7, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
192 if (!_route->is_master()) {
193 controls_table.attach (*solo_button, 7, 8, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
196 controls_table.attach (route_group_button, 7, 8, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
197 controls_table.attach (gm.get_gain_slider(), 0, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::AttachOptions (0), 3, 0);
199 ARDOUR_UI::instance()->set_tip(*solo_button,_("Solo"));
200 ARDOUR_UI::instance()->set_tip(*mute_button,_("Mute"));
201 ARDOUR_UI::instance()->set_tip(route_group_button, _("Route Group"));
203 if (is_midi_track()) {
204 ARDOUR_UI::instance()->set_tip(automation_button, _("MIDI Controllers and Automation"));
206 ARDOUR_UI::instance()->set_tip(automation_button, _("Automation"));
211 controls_table.attach (automation_button, 6, 7, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
213 if (is_track() && track()->mode() == ARDOUR::Normal) {
214 controls_table.attach (playlist_button, 5, 6, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
219 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
220 _route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::route_property_changed, this, _1), gui_context());
224 str = gui_property ("layer-display");
226 set_layer_display (LayerDisplay (string_2_enum (str, _view->layer_display ())));
229 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
230 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
232 /* pick up the correct freeze state */
237 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_unit));
238 _editor.HorizontalPositionChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::horizontal_position_changed));
239 ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
241 PropertyList* plist = new PropertyList();
243 plist->add (ARDOUR::Properties::mute, true);
244 plist->add (ARDOUR::Properties::solo, true);
246 route_group_menu = new RouteGroupMenu (_session, plist);
248 // gm.get_gain_slider().signal_scroll_event().connect(sigc::mem_fun(*this, &RouteTimeAxisView::controls_ebox_scroll), false);
250 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
253 RouteTimeAxisView::~RouteTimeAxisView ()
255 CatchDeletion (this);
257 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
261 delete playlist_action_menu;
262 playlist_action_menu = 0;
267 _automation_tracks.clear ();
269 delete route_group_menu;
273 RouteTimeAxisView::post_construct ()
275 /* map current state of the route */
277 update_diskstream_display ();
278 setup_processor_menu_and_curves ();
279 reset_processor_automation_curves ();
282 /** Set up the processor menu for the current set of processors, and
283 * display automation curves for any parameters which have data.
286 RouteTimeAxisView::setup_processor_menu_and_curves ()
288 _subplugin_menu_map.clear ();
289 subplugin_menu.items().clear ();
290 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
291 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
295 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
297 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
298 if (_route->route_group()) {
299 _route->route_group()->remove (_route);
305 r.push_back (route ());
307 route_group_menu->build (r);
308 route_group_menu->menu()->popup (ev->button, ev->time);
314 RouteTimeAxisView::playlist_changed ()
320 RouteTimeAxisView::label_view ()
322 string x = _route->name();
324 if (x != name_label.get_text()) {
325 name_label.set_text (x);
331 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
333 if (what_changed.contains (ARDOUR::Properties::name)) {
339 RouteTimeAxisView::take_name_changed (void *src)
347 RouteTimeAxisView::playlist_click ()
349 build_playlist_menu ();
350 conditionally_add_to_selection ();
351 playlist_action_menu->popup (1, gtk_get_current_event_time());
355 RouteTimeAxisView::automation_click ()
357 conditionally_add_to_selection ();
358 build_automation_action_menu (false);
359 automation_action_menu->popup (1, gtk_get_current_event_time());
363 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
365 using namespace Menu_Helpers;
367 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
368 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
371 detach_menu (subplugin_menu);
373 _main_automation_menu_map.clear ();
374 delete automation_action_menu;
375 automation_action_menu = new Menu;
377 MenuList& items = automation_action_menu->items();
379 automation_action_menu->set_name ("ArdourContextMenu");
381 items.push_back (MenuElem (_("Show All Automation"),
382 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
384 items.push_back (MenuElem (_("Show Existing Automation"),
385 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
387 items.push_back (MenuElem (_("Hide All Automation"),
388 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
390 items.push_back (SeparatorElem ());
392 /* Attach the plugin submenu. It may have previously been used elsewhere,
393 so it was detached above
396 if (!subplugin_menu.items().empty()) {
397 items.push_back (MenuElem (_("Plugins"), subplugin_menu));
398 items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);;
403 RouteTimeAxisView::build_display_menu ()
405 using namespace Menu_Helpers;
409 TimeAxisView::build_display_menu ();
411 /* now fill it with our stuff */
413 MenuList& items = display_menu->items();
414 display_menu->set_name ("ArdourContextMenu");
416 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
419 detach_menu (*_size_menu);
422 items.push_back (MenuElem (_("Height"), *_size_menu));
424 items.push_back (SeparatorElem());
426 if (!Profile->get_sae()) {
427 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
428 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
429 items.push_back (SeparatorElem());
432 // Hook for derived classes to add type specific stuff
433 append_extra_display_menu_items ();
437 Menu* layers_menu = manage (new Menu);
438 MenuList &layers_items = layers_menu->items();
439 layers_menu->set_name("ArdourContextMenu");
441 RadioMenuItem::Group layers_group;
443 /* Find out how many overlaid/stacked tracks we have in the selection */
447 TrackSelection const & s = _editor.get_selection().tracks;
448 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
449 StreamView* v = (*i)->view ();
454 switch (v->layer_display ()) {
465 /* We're not connecting to signal_toggled() here; in the case where these two items are
466 set to be in the `inconsistent' state, it seems that one or other will end up active
467 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
468 select the active one, no toggled signal is emitted so nothing happens.
471 _ignore_set_layer_display = true;
473 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
474 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
475 i->set_active (overlaid != 0 && stacked == 0);
476 i->set_inconsistent (overlaid != 0 && stacked != 0);
477 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
479 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
480 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
481 i->set_active (overlaid == 0 && stacked != 0);
482 i->set_inconsistent (overlaid != 0 && stacked != 0);
483 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
485 _ignore_set_layer_display = false;
487 items.push_back (MenuElem (_("Layers"), *layers_menu));
489 if (!Profile->get_sae()) {
491 Menu* alignment_menu = manage (new Menu);
492 MenuList& alignment_items = alignment_menu->items();
493 alignment_menu->set_name ("ArdourContextMenu");
495 RadioMenuItem::Group align_group;
497 /* Same verbose hacks as for the layering options above */
503 boost::shared_ptr<Track> first_track;
505 TrackSelection const & s = _editor.get_selection().tracks;
506 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
507 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
508 if (!r || !r->is_track ()) {
513 first_track = r->track();
516 switch (r->track()->alignment_choice()) {
520 switch (r->track()->alignment_style()) {
521 case ExistingMaterial:
529 case UseExistingMaterial:
545 inconsistent = false;
554 if (!inconsistent && first_track) {
556 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
557 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
558 i->set_active (automatic != 0 && existing == 0 && capture == 0);
559 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
561 switch (first_track->alignment_choice()) {
563 switch (first_track->alignment_style()) {
564 case ExistingMaterial:
565 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
568 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
576 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
577 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
578 i->set_active (existing != 0 && capture == 0 && automatic == 0);
579 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
581 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
582 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
583 i->set_active (existing == 0 && capture != 0 && automatic == 0);
584 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
586 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
592 Menu* mode_menu = manage (new Menu);
593 MenuList& mode_items = mode_menu->items ();
594 mode_menu->set_name ("ArdourContextMenu");
596 RadioMenuItem::Group mode_group;
602 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
603 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
604 if (!r || !r->is_track ()) {
608 switch (r->track()->mode()) {
621 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
622 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
623 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
624 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
625 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
627 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
628 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
629 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
630 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
631 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
633 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
634 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
635 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
636 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
637 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
639 items.push_back (MenuElem (_("Mode"), *mode_menu));
642 color_mode_menu = build_color_mode_menu();
643 if (color_mode_menu) {
644 items.push_back (MenuElem (_("Color Mode"), *color_mode_menu));
647 items.push_back (SeparatorElem());
649 build_playlist_menu ();
650 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
651 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
654 route_group_menu->detach ();
657 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
658 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
660 r.push_back (rtv->route ());
665 r.push_back (route ());
668 route_group_menu->build (r);
669 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
671 build_automation_action_menu (true);
672 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
674 items.push_back (SeparatorElem());
678 TrackSelection const & s = _editor.get_selection().tracks;
679 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
680 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
685 if (r->route()->active()) {
692 items.push_back (CheckMenuElem (_("Active")));
693 CheckMenuItem* i = dynamic_cast<CheckMenuItem *> (&items.back());
694 bool click_sets_active = true;
695 if (active > 0 && inactive == 0) {
696 i->set_active (true);
697 click_sets_active = false;
698 } else if (active > 0 && inactive > 0) {
699 i->set_inconsistent (true);
701 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
703 items.push_back (SeparatorElem());
704 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
705 if (!Profile->get_sae()) {
706 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
708 items.push_front (SeparatorElem());
709 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
714 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
716 if (apply_to_selection) {
717 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
722 if (!track()->can_use_mode (mode, needs_bounce)) {
728 cerr << "would bounce this one\n";
733 track()->set_mode (mode);
735 rec_enable_button->remove ();
738 case ARDOUR::NonLayered:
740 rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
741 rec_enable_button->set_text (string());
743 case ARDOUR::Destructive:
744 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
745 rec_enable_button->set_text (string());
749 rec_enable_button->show_all ();
754 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
756 TimeAxisView::show_timestretch (start, end, layers, layer);
766 /* check that the time selection was made in our route, or our route group.
767 remember that route_group() == 0 implies the route is *not* in a edit group.
770 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
771 /* this doesn't apply to us */
775 /* ignore it if our edit group is not active */
777 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
782 if (timestretch_rect == 0) {
783 timestretch_rect = new SimpleRect (*canvas_display ());
784 timestretch_rect->property_x1() = 0.0;
785 timestretch_rect->property_y1() = 0.0;
786 timestretch_rect->property_x2() = 0.0;
787 timestretch_rect->property_y2() = 0.0;
788 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
789 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
792 timestretch_rect->show ();
793 timestretch_rect->raise_to_top ();
795 double const x1 = start / _editor.get_current_zoom();
796 double const x2 = (end - 1) / _editor.get_current_zoom();
798 timestretch_rect->property_x1() = x1;
799 timestretch_rect->property_y1() = current_height() * (layers - layer - 1) / layers;
800 timestretch_rect->property_x2() = x2;
801 timestretch_rect->property_y2() = current_height() * (layers - layer) / layers;
805 RouteTimeAxisView::hide_timestretch ()
807 TimeAxisView::hide_timestretch ();
809 if (timestretch_rect) {
810 timestretch_rect->hide ();
815 RouteTimeAxisView::show_selection (TimeSelection& ts)
819 /* ignore it if our edit group is not active or if the selection was started
820 in some other track or route group (remember that route_group() == 0 means
821 that the track is not in an route group).
824 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
825 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
831 TimeAxisView::show_selection (ts);
835 RouteTimeAxisView::set_height (uint32_t h)
838 bool height_changed = (height == 0) || (h != height);
839 gm.get_level_meter().setup_meters (gmlen);
841 TimeAxisView::set_height (h);
844 _view->set_height ((double) current_height());
847 if (height >= preset_height (HeightNormal)) {
851 gm.get_gain_slider().show();
853 if (!_route || _route->is_monitor()) {
858 if (rec_enable_button)
859 rec_enable_button->show();
861 route_group_button.show();
862 automation_button.show();
864 if (is_track() && track()->mode() == ARDOUR::Normal) {
865 playlist_button.show();
872 gm.get_gain_slider().hide();
874 if (!_route || _route->is_monitor()) {
879 if (rec_enable_button)
880 rec_enable_button->show();
882 route_group_button.hide ();
883 automation_button.hide ();
885 if (is_track() && track()->mode() == ARDOUR::Normal) {
886 playlist_button.hide ();
891 if (height_changed && !no_redraw) {
892 /* only emit the signal if the height really changed */
898 RouteTimeAxisView::route_color_changed ()
901 _view->apply_color (color(), StreamView::RegionColor);
906 RouteTimeAxisView::reset_samples_per_unit ()
908 set_samples_per_unit (_editor.get_current_zoom());
912 RouteTimeAxisView::horizontal_position_changed ()
915 _view->horizontal_position_changed ();
920 RouteTimeAxisView::set_samples_per_unit (double spu)
925 speed = track()->speed();
929 _view->set_samples_per_unit (spu * speed);
932 TimeAxisView::set_samples_per_unit (spu * speed);
936 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
938 if (!mitem->get_active()) {
939 /* this is one of the two calls made when these radio menu items change status. this one
940 is for the item that became inactive, and we want to ignore it.
945 if (apply_to_selection) {
946 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
949 track()->set_align_choice (choice);
955 RouteTimeAxisView::rename_current_playlist ()
957 ArdourPrompter prompter (true);
960 boost::shared_ptr<Track> tr = track();
961 if (!tr || tr->destructive()) {
965 boost::shared_ptr<Playlist> pl = tr->playlist();
970 prompter.set_title (_("Rename Playlist"));
971 prompter.set_prompt (_("New name for playlist:"));
972 prompter.set_initial_text (pl->name());
973 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
974 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
976 switch (prompter.run ()) {
977 case Gtk::RESPONSE_ACCEPT:
978 prompter.get_result (name);
990 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
992 std::string ret (basename);
994 std::string const group_string = "." + route_group()->name() + ".";
996 // iterate through all playlists
998 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
999 std::string tmp = (*i)->name();
1001 std::string::size_type idx = tmp.find(group_string);
1002 // find those which belong to this group
1003 if (idx != string::npos) {
1004 tmp = tmp.substr(idx + group_string.length());
1006 // and find the largest current number
1007 int x = atoi(tmp.c_str());
1008 if (x > maxnumber) {
1017 snprintf (buf, sizeof(buf), "%d", maxnumber);
1019 ret = this->name() + "." + route_group()->name () + "." + buf;
1025 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1029 boost::shared_ptr<Track> tr = track ();
1030 if (!tr || tr->destructive()) {
1034 boost::shared_ptr<const Playlist> pl = tr->playlist();
1041 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1042 name = resolve_new_group_playlist_name(name, playlists_before_op);
1045 while (_session->playlists->by_name(name)) {
1046 name = Playlist::bump_name (name, *_session);
1049 // TODO: The prompter "new" button should be de-activated if the user
1050 // specifies a playlist name which already exists in the session.
1054 ArdourPrompter prompter (true);
1056 prompter.set_title (_("New Copy Playlist"));
1057 prompter.set_prompt (_("Name for new playlist:"));
1058 prompter.set_initial_text (name);
1059 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1060 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1061 prompter.show_all ();
1063 switch (prompter.run ()) {
1064 case Gtk::RESPONSE_ACCEPT:
1065 prompter.get_result (name);
1073 if (name.length()) {
1074 tr->use_copy_playlist ();
1075 tr->playlist()->set_name (name);
1080 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1084 boost::shared_ptr<Track> tr = track ();
1085 if (!tr || tr->destructive()) {
1089 boost::shared_ptr<const Playlist> pl = tr->playlist();
1096 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1097 name = resolve_new_group_playlist_name(name,playlists_before_op);
1100 while (_session->playlists->by_name(name)) {
1101 name = Playlist::bump_name (name, *_session);
1107 ArdourPrompter prompter (true);
1109 prompter.set_title (_("New Playlist"));
1110 prompter.set_prompt (_("Name for new playlist:"));
1111 prompter.set_initial_text (name);
1112 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1113 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1115 switch (prompter.run ()) {
1116 case Gtk::RESPONSE_ACCEPT:
1117 prompter.get_result (name);
1125 if (name.length()) {
1126 tr->use_new_playlist ();
1127 tr->playlist()->set_name (name);
1132 RouteTimeAxisView::clear_playlist ()
1134 boost::shared_ptr<Track> tr = track ();
1135 if (!tr || tr->destructive()) {
1139 boost::shared_ptr<Playlist> pl = tr->playlist();
1144 _editor.clear_playlist (pl);
1148 RouteTimeAxisView::speed_changed ()
1150 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_unit, this));
1154 RouteTimeAxisView::update_diskstream_display ()
1164 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1166 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1168 /* special case: select/deselect all tracks */
1169 if (_editor.get_selection().selected (this)) {
1170 _editor.get_selection().clear_tracks ();
1172 _editor.select_all_tracks ();
1178 switch (ArdourKeyboard::selection_type (ev->state)) {
1179 case Selection::Toggle:
1180 _editor.get_selection().toggle (this);
1183 case Selection::Set:
1184 _editor.get_selection().set (this);
1187 case Selection::Extend:
1188 _editor.extend_selection_to_track (*this);
1191 case Selection::Add:
1192 _editor.get_selection().add (this);
1198 RouteTimeAxisView::set_selected_points (PointSelection& points)
1200 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1201 (*i)->set_selected_points (points);
1206 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1209 _view->set_selected_regionviews (regions);
1213 /** Add the selectable things that we have to a list.
1214 * @param results List to add things to.
1217 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1222 speed = track()->speed();
1225 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1226 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1228 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1229 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1232 /* pick up visible automation tracks */
1234 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1235 if (!(*i)->hidden()) {
1236 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1242 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1245 _view->get_inverted_selectables (sel, results);
1248 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1249 if (!(*i)->hidden()) {
1250 (*i)->get_inverted_selectables (sel, results);
1258 RouteTimeAxisView::route_group () const
1260 return _route->route_group();
1264 RouteTimeAxisView::name() const
1266 return _route->name();
1269 boost::shared_ptr<Playlist>
1270 RouteTimeAxisView::playlist () const
1272 boost::shared_ptr<Track> tr;
1274 if ((tr = track()) != 0) {
1275 return tr->playlist();
1277 return boost::shared_ptr<Playlist> ();
1282 RouteTimeAxisView::name_entry_changed ()
1284 TimeAxisView::name_entry_changed ();
1286 string x = name_entry->get_text ();
1288 if (x == _route->name()) {
1292 strip_whitespace_edges (x);
1294 if (x.length() == 0) {
1295 name_entry->set_text (_route->name());
1299 if (_session->route_name_internal (x)) {
1300 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1302 name_entry->grab_focus ();
1303 } else if (RouteUI::verify_new_route_name (x)) {
1304 _route->set_name (x);
1306 name_entry->grab_focus ();
1310 boost::shared_ptr<Region>
1311 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1313 boost::shared_ptr<Playlist> pl = playlist ();
1316 return pl->find_next_region (pos, point, dir);
1319 return boost::shared_ptr<Region> ();
1323 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1325 boost::shared_ptr<Playlist> pl = playlist ();
1328 return pl->find_next_region_boundary (pos, dir);
1335 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1337 boost::shared_ptr<Playlist> what_we_got;
1338 boost::shared_ptr<Track> tr = track ();
1339 boost::shared_ptr<Playlist> playlist;
1342 /* route is a bus, not a track */
1346 playlist = tr->playlist();
1348 TimeSelection time (selection.time);
1349 float const speed = tr->speed();
1350 if (speed != 1.0f) {
1351 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1352 (*i).start = session_frame_to_track_frame((*i).start, speed);
1353 (*i).end = session_frame_to_track_frame((*i).end, speed);
1357 playlist->clear_changes ();
1358 playlist->clear_owned_changes ();
1362 if (playlist->cut (time) != 0) {
1363 vector<Command*> cmds;
1364 playlist->rdiff (cmds);
1365 _session->add_commands (cmds);
1367 _session->add_command (new StatefulDiffCommand (playlist));
1372 if ((what_we_got = playlist->cut (time)) != 0) {
1373 _editor.get_cut_buffer().add (what_we_got);
1374 vector<Command*> cmds;
1375 playlist->rdiff (cmds);
1376 _session->add_commands (cmds);
1378 _session->add_command (new StatefulDiffCommand (playlist));
1382 if ((what_we_got = playlist->copy (time)) != 0) {
1383 _editor.get_cut_buffer().add (what_we_got);
1388 if ((what_we_got = playlist->cut (time)) != 0) {
1390 vector<Command*> cmds;
1391 playlist->rdiff (cmds);
1392 _session->add_commands (cmds);
1393 _session->add_command (new StatefulDiffCommand (playlist));
1394 what_we_got->release ();
1401 RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
1407 boost::shared_ptr<Playlist> pl = playlist ();
1408 PlaylistSelection::iterator p;
1410 for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
1412 if (p == selection.playlists.end()) {
1416 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1418 if (track()->speed() != 1.0f) {
1419 pos = session_frame_to_track_frame (pos, track()->speed());
1420 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1423 pl->clear_changes ();
1424 pl->paste (*p, pos, times);
1425 _session->add_command (new StatefulDiffCommand (pl));
1431 struct PlaylistSorter {
1432 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1433 return a->sort_id() < b->sort_id();
1438 RouteTimeAxisView::build_playlist_menu ()
1440 using namespace Menu_Helpers;
1446 delete playlist_action_menu;
1447 playlist_action_menu = new Menu;
1448 playlist_action_menu->set_name ("ArdourContextMenu");
1450 MenuList& playlist_items = playlist_action_menu->items();
1451 playlist_action_menu->set_name ("ArdourContextMenu");
1452 playlist_items.clear();
1454 RadioMenuItem::Group playlist_group;
1455 boost::shared_ptr<Track> tr = track ();
1457 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1459 /* sort the playlists */
1461 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1463 /* add the playlists to the menu */
1464 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1465 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1466 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1467 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1469 if (tr->playlist()->id() == (*i)->id()) {
1475 playlist_items.push_back (SeparatorElem());
1476 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1477 playlist_items.push_back (SeparatorElem());
1479 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1480 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1481 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1484 // Use a label which tells the user what is happening
1485 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1486 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1490 playlist_items.push_back (SeparatorElem());
1491 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1492 playlist_items.push_back (SeparatorElem());
1494 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1498 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1500 assert (is_track());
1502 // exit if we were triggered by deactivating the old playlist
1503 if (!item->get_active()) {
1507 boost::shared_ptr<Playlist> pl (wpl.lock());
1513 if (track()->playlist() == pl) {
1514 // exit when use_playlist is called by the creation of the playlist menu
1515 // or the playlist choice is unchanged
1519 track()->use_playlist (pl);
1521 RouteGroup* rg = route_group();
1523 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1524 std::string group_string = "." + rg->name() + ".";
1526 std::string take_name = pl->name();
1527 std::string::size_type idx = take_name.find(group_string);
1529 if (idx == std::string::npos)
1532 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1534 boost::shared_ptr<RouteList> rl (rg->route_list());
1536 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1537 if ((*i) == this->route()) {
1541 std::string playlist_name = (*i)->name()+group_string+take_name;
1543 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1548 if (track->freeze_state() == Track::Frozen) {
1549 /* Don't change playlists of frozen tracks */
1553 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1555 // No playlist for this track for this take yet, make it
1556 track->use_new_playlist();
1557 track->playlist()->set_name(playlist_name);
1559 track->use_playlist(ipl);
1566 RouteTimeAxisView::update_playlist_tip ()
1568 RouteGroup* rg = route_group ();
1569 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1570 string group_string = "." + rg->name() + ".";
1572 string take_name = track()->playlist()->name();
1573 string::size_type idx = take_name.find(group_string);
1575 if (idx != string::npos) {
1576 /* find the bit containing the take number / name */
1577 take_name = take_name.substr (idx + group_string.length());
1579 /* set the playlist button tooltip to the take name */
1580 ARDOUR_UI::instance()->set_tip (
1582 string_compose(_("Take: %1.%2"),
1583 Glib::Markup::escape_text(rg->name()),
1584 Glib::Markup::escape_text(take_name))
1591 /* set the playlist button tooltip to the playlist name */
1592 ARDOUR_UI::instance()->set_tip (playlist_button, _("Playlist") + std::string(": ") + Glib::Markup::escape_text(track()->playlist()->name()));
1597 RouteTimeAxisView::show_playlist_selector ()
1599 _editor.playlist_selector().show_for (this);
1603 RouteTimeAxisView::map_frozen ()
1609 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1611 switch (track()->freeze_state()) {
1613 playlist_button.set_sensitive (false);
1614 rec_enable_button->set_sensitive (false);
1617 playlist_button.set_sensitive (true);
1618 rec_enable_button->set_sensitive (true);
1624 RouteTimeAxisView::color_handler ()
1626 //case cTimeStretchOutline:
1627 if (timestretch_rect) {
1628 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
1630 //case cTimeStretchFill:
1631 if (timestretch_rect) {
1632 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
1638 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1639 * Will add track if necessary.
1642 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1644 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1645 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1648 /* it doesn't exist yet, so we don't care about the button state: just add it */
1649 create_automation_child (param, true);
1652 bool yn = menu->get_active();
1653 bool changed = false;
1655 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1657 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1658 will have done that for us.
1661 if (changed && !no_redraw) {
1669 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1671 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1677 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1679 if (menu && !_hidden) {
1680 ignore_toggle = true;
1681 menu->set_active (false);
1682 ignore_toggle = false;
1685 if (_route && !no_redraw) {
1692 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1694 if (apply_to_selection) {
1695 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1699 /* Show our automation */
1701 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1702 i->second->set_marked_for_display (true);
1704 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1707 menu->set_active(true);
1712 /* Show processor automation */
1714 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1715 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1716 if ((*ii)->view == 0) {
1717 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1720 (*ii)->menu_item->set_active (true);
1733 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1735 if (apply_to_selection) {
1736 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1740 /* Show our automation */
1742 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1743 if (i->second->has_automation()) {
1744 i->second->set_marked_for_display (true);
1746 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1748 menu->set_active(true);
1753 /* Show processor automation */
1755 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1756 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1757 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1758 (*ii)->menu_item->set_active (true);
1770 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1772 if (apply_to_selection) {
1773 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1777 /* Hide our automation */
1779 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1780 i->second->set_marked_for_display (false);
1782 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1785 menu->set_active (false);
1789 /* Hide processor automation */
1791 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1792 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1793 (*ii)->menu_item->set_active (false);
1804 RouteTimeAxisView::region_view_added (RegionView* rv)
1806 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1807 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1808 boost::shared_ptr<AutomationTimeAxisView> atv;
1810 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1815 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1816 (*i)->add_ghost(rv);
1820 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1822 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1828 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1830 parent.remove_processor_automation_node (this);
1834 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1837 remove_child (pan->view);
1841 RouteTimeAxisView::ProcessorAutomationNode*
1842 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1844 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1846 if ((*i)->processor == processor) {
1848 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1849 if ((*ii)->what == what) {
1859 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
1861 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1864 ProcessorAutomationNode* pan;
1866 if ((pan = find_processor_automation_node (processor, what)) == 0) {
1867 /* session state may never have been saved with new plugin */
1868 error << _("programming error: ")
1869 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
1870 processor->name(), what.type(), (int) what.channel(), what.id() )
1880 boost::shared_ptr<AutomationControl> control
1881 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
1883 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
1884 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
1885 _editor, *this, false, parent_canvas,
1886 processor->describe_parameter (what), processor->name()));
1888 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
1890 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
1893 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
1898 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
1901 pan->menu_item->set_active (false);
1910 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
1912 boost::shared_ptr<Processor> processor (p.lock ());
1914 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
1915 /* The Amp processor is a special case and is dealt with separately */
1919 set<Evoral::Parameter> existing;
1921 processor->what_has_data (existing);
1923 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
1925 Evoral::Parameter param (*i);
1926 boost::shared_ptr<AutomationLine> al;
1928 if ((al = find_processor_automation_curve (processor, param)) != 0) {
1931 add_processor_automation_curve (processor, param);
1937 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
1939 using namespace Menu_Helpers;
1943 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
1945 _automation_tracks[param] = track;
1947 /* existing state overrides "show" argument */
1948 string s = track->gui_property ("visible");
1950 show = string_is_affirmative (s);
1953 /* this might or might not change the visibility status, so don't rely on it */
1954 track->set_marked_for_display (show);
1956 if (show && !no_redraw) {
1960 if (!EventTypeMap::instance().is_midi_parameter(param)) {
1961 /* MIDI-related parameters are always in the menu, there's no
1962 reason to rebuild the menu just because we added a automation
1963 lane for one of them. But if we add a non-MIDI automation
1964 lane, then we need to invalidate the display menu.
1966 delete display_menu;
1972 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
1974 boost::shared_ptr<Processor> processor (p.lock ());
1976 if (!processor || !processor->display_to_user ()) {
1980 /* we use this override to veto the Amp processor from the plugin menu,
1981 as its automation lane can be accessed using the special "Fader" menu
1985 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
1989 using namespace Menu_Helpers;
1990 ProcessorAutomationInfo *rai;
1991 list<ProcessorAutomationInfo*>::iterator x;
1993 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
1995 if (automatable.empty()) {
1999 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2000 if ((*x)->processor == processor) {
2005 if (x == processor_automation.end()) {
2007 rai = new ProcessorAutomationInfo (processor);
2008 processor_automation.push_back (rai);
2016 /* any older menu was deleted at the top of processors_changed()
2017 when we cleared the subplugin menu.
2020 rai->menu = manage (new Menu);
2021 MenuList& items = rai->menu->items();
2022 rai->menu->set_name ("ArdourContextMenu");
2026 std::set<Evoral::Parameter> has_visible_automation;
2027 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2029 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2031 ProcessorAutomationNode* pan;
2032 CheckMenuItem* mitem;
2034 string name = processor->describe_parameter (*i);
2036 items.push_back (CheckMenuElem (name));
2037 mitem = dynamic_cast<CheckMenuItem*> (&items.back());
2039 _subplugin_menu_map[*i] = mitem;
2041 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2042 mitem->set_active(true);
2045 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2049 pan = new ProcessorAutomationNode (*i, mitem, *this);
2051 rai->lines.push_back (pan);
2055 pan->menu_item = mitem;
2059 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2062 /* add the menu for this processor, because the subplugin
2063 menu is always cleared at the top of processors_changed().
2064 this is the result of some poor design in gtkmm and/or
2068 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2073 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2074 RouteTimeAxisView::ProcessorAutomationNode* pan)
2076 bool showit = pan->menu_item->get_active();
2077 bool redraw = false;
2079 if (pan->view == 0 && showit) {
2080 add_processor_automation_curve (rai->processor, pan->what);
2084 if (pan->view && pan->view->set_marked_for_display (showit)) {
2088 if (redraw && !no_redraw) {
2094 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2096 if (c.type == RouteProcessorChange::MeterPointChange) {
2097 /* nothing to do if only the meter point has changed */
2101 using namespace Menu_Helpers;
2103 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2104 (*i)->valid = false;
2107 setup_processor_menu_and_curves ();
2109 bool deleted_processor_automation = false;
2111 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2113 list<ProcessorAutomationInfo*>::iterator tmp;
2121 processor_automation.erase (i);
2122 deleted_processor_automation = true;
2129 if (deleted_processor_automation && !no_redraw) {
2134 boost::shared_ptr<AutomationLine>
2135 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2137 ProcessorAutomationNode* pan;
2139 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2145 return boost::shared_ptr<AutomationLine>();
2149 RouteTimeAxisView::reset_processor_automation_curves ()
2151 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2157 RouteTimeAxisView::can_edit_name () const
2159 /* we do not allow track name changes if it is record enabled
2161 return !_route->record_enabled();
2165 RouteTimeAxisView::update_rec_display ()
2167 RouteUI::update_rec_display ();
2171 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2173 if (_ignore_set_layer_display) {
2177 if (apply_to_selection) {
2178 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2182 _view->set_layer_display (d);
2185 set_gui_property (X_("layer-display"), enum_2_string (d));
2190 RouteTimeAxisView::layer_display () const
2193 return _view->layer_display ();
2196 /* we don't know, since we don't have a _view, so just return something */
2202 boost::shared_ptr<AutomationTimeAxisView>
2203 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2205 AutomationTracks::iterator i = _automation_tracks.find(param);
2206 if (i != _automation_tracks.end()) {
2209 return boost::shared_ptr<AutomationTimeAxisView>();
2214 RouteTimeAxisView::fast_update ()
2216 gm.get_level_meter().update_meters ();
2220 RouteTimeAxisView::hide_meter ()
2223 gm.get_level_meter().hide_meters ();
2227 RouteTimeAxisView::show_meter ()
2233 RouteTimeAxisView::reset_meter ()
2235 if (Config->get_show_track_meters()) {
2236 gm.get_level_meter().setup_meters (height-5);
2243 RouteTimeAxisView::clear_meter ()
2245 gm.get_level_meter().clear_meters ();
2249 RouteTimeAxisView::meter_changed ()
2251 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2256 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2262 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2264 using namespace Menu_Helpers;
2266 if (!_underlay_streams.empty()) {
2267 MenuList& parent_items = parent_menu->items();
2268 Menu* gs_menu = manage (new Menu);
2269 gs_menu->set_name ("ArdourContextMenu");
2270 MenuList& gs_items = gs_menu->items();
2272 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2274 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2275 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2276 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2282 RouteTimeAxisView::set_underlay_state()
2284 if (!underlay_xml_node) {
2288 XMLNodeList nlist = underlay_xml_node->children();
2289 XMLNodeConstIterator niter;
2290 XMLNode *child_node;
2292 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2293 child_node = *niter;
2295 if (child_node->name() != "Underlay") {
2299 XMLProperty* prop = child_node->property ("id");
2301 PBD::ID id (prop->value());
2303 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2306 add_underlay(v->view(), false);
2315 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2321 RouteTimeAxisView& other = v->trackview();
2323 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2324 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2325 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2329 _underlay_streams.push_back(v);
2330 other._underlay_mirrors.push_back(this);
2332 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2334 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2336 if (!underlay_xml_node) {
2337 underlay_xml_node = xml_node->add_child("Underlays");
2340 XMLNode* node = underlay_xml_node->add_child("Underlay");
2341 XMLProperty* prop = node->add_property("id");
2342 prop->set_value(v->trackview().route()->id().to_s());
2349 RouteTimeAxisView::remove_underlay (StreamView* v)
2355 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2356 RouteTimeAxisView& other = v->trackview();
2358 if (it != _underlay_streams.end()) {
2359 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2361 if (gm == other._underlay_mirrors.end()) {
2362 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2366 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2368 _underlay_streams.erase(it);
2369 other._underlay_mirrors.erase(gm);
2371 if (underlay_xml_node) {
2372 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2378 RouteTimeAxisView::set_button_names ()
2380 if (_route && _route->solo_safe()) {
2381 solo_button->remove ();
2382 if (solo_safe_pixbuf == 0) {
2383 solo_safe_pixbuf = ::get_icon("solo-safe-icon");
2385 solo_button->set_image (solo_safe_pixbuf);
2386 solo_button->set_text (string());
2388 solo_button->set_image (Glib::RefPtr<Gdk::Pixbuf>());
2389 if (Config->get_solo_control_is_listen_control()) {
2390 switch (Config->get_listen_position()) {
2391 case AfterFaderListen:
2392 solo_button->set_text (_("A"));
2393 ARDOUR_UI::instance()->set_tip (*solo_button, _("After-fade listen (AFL)"));
2395 case PreFaderListen:
2396 solo_button->set_text (_("P"));
2397 ARDOUR_UI::instance()->set_tip (*solo_button, _("Pre-fade listen (PFL)"));
2401 solo_button->set_text (_("s"));
2402 ARDOUR_UI::instance()->set_tip (*solo_button, _("Solo"));
2405 mute_button->set_text (_("m"));
2409 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2411 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2412 if (i != _main_automation_menu_map.end()) {
2416 i = _subplugin_menu_map.find (param);
2417 if (i != _subplugin_menu_map.end()) {
2425 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2427 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2429 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2433 gain_track.reset (new AutomationTimeAxisView (_session,
2434 _route, _route->amp(), c, param,
2439 _route->amp()->describe_parameter(param)));
2442 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2445 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2449 void add_region_to_list (RegionView* rv, RegionList* l)
2451 l->push_back (rv->region());
2455 RouteTimeAxisView::combine_regions ()
2457 /* as of may 2011, we do not offer uncombine for MIDI tracks
2460 if (!is_audio_track()) {
2468 RegionList selected_regions;
2469 boost::shared_ptr<Playlist> playlist = track()->playlist();
2471 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2473 if (selected_regions.size() < 2) {
2477 playlist->clear_changes ();
2478 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2480 _session->add_command (new StatefulDiffCommand (playlist));
2481 /* make the new region be selected */
2483 return _view->find_view (compound_region);
2487 RouteTimeAxisView::uncombine_regions ()
2489 /* as of may 2011, we do not offer uncombine for MIDI tracks
2491 if (!is_audio_track()) {
2499 RegionList selected_regions;
2500 boost::shared_ptr<Playlist> playlist = track()->playlist();
2502 /* have to grab selected regions first because the uncombine is going
2503 * to change that in the middle of the list traverse
2506 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2508 playlist->clear_changes ();
2510 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2511 playlist->uncombine (*i);
2514 _session->add_command (new StatefulDiffCommand (playlist));
2518 RouteTimeAxisView::state_id() const
2520 return string_compose ("rtav %1", _route->id().to_s());
2525 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2527 TimeAxisView::remove_child (c);
2529 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2531 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2532 if (i->second == a) {
2533 _automation_tracks.erase (i);