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/audioplaylist.h"
47 #include "ardour/diskstream.h"
48 #include "ardour/event_type_map.h"
49 #include "ardour/ladspa_plugin.h"
50 #include "ardour/location.h"
51 #include "ardour/panner.h"
52 #include "ardour/playlist.h"
53 #include "ardour/playlist.h"
54 #include "ardour/processor.h"
55 #include "ardour/profile.h"
56 #include "ardour/region_factory.h"
57 #include "ardour/route_group.h"
58 #include "ardour/session.h"
59 #include "ardour/session_playlist.h"
60 #include "ardour/debug.h"
61 #include "ardour/utils.h"
62 #include "evoral/Parameter.hpp"
64 #include "ardour_ui.h"
65 #include "ardour_button.h"
67 #include "global_signals.h"
68 #include "route_time_axis.h"
69 #include "automation_time_axis.h"
70 #include "canvas_impl.h"
71 #include "crossfade_view.h"
73 #include "gui_thread.h"
75 #include "playlist_selector.h"
76 #include "point_selection.h"
78 #include "public_editor.h"
79 #include "region_view.h"
80 #include "rgb_macros.h"
81 #include "selection.h"
82 #include "simplerect.h"
83 #include "streamview.h"
85 #include "route_group_menu.h"
87 #include "ardour/track.h"
91 using namespace ARDOUR;
93 using namespace Gtkmm2ext;
95 using namespace Editing;
99 Glib::RefPtr<Gdk::Pixbuf> RouteTimeAxisView::slider;
102 RouteTimeAxisView::setup_slider_pix ()
104 if ((slider = ::get_icon ("fader_belt_h")) == 0) {
105 throw failed_constructor ();
109 RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, Canvas& canvas)
112 , TimeAxisView(sess,ed,(TimeAxisView*) 0, canvas)
114 , parent_canvas (canvas)
116 , button_table (3, 3)
117 , route_group_button (_("g"))
118 , playlist_button (_("p"))
119 , automation_button (_("a"))
120 , automation_action_menu (0)
121 , plugins_submenu_item (0)
122 , route_group_menu (0)
123 , playlist_action_menu (0)
125 , color_mode_menu (0)
126 , gm (sess, slider, true, 115)
131 RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
133 RouteUI::set_route (rt);
135 gm.set_controls (_route, _route->shared_peak_meter(), _route->amp());
136 gm.get_level_meter().set_no_show_all();
137 gm.get_level_meter().setup_meters(50);
139 string str = gui_property ("height");
141 set_height (atoi (str));
143 set_height (preset_height (HeightNormal));
146 if (!_route->is_hidden()) {
147 if (gui_property ("visible").empty()) {
148 set_gui_property ("visible", true);
151 set_gui_property ("visible", false);
155 update_solo_display ();
157 timestretch_rect = 0;
160 ignore_toggle = false;
162 route_group_button.set_name ("route button");
163 playlist_button.set_name ("route button");
164 automation_button.set_name ("route button");
166 route_group_button.signal_button_release_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
167 playlist_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click));
168 automation_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click));
174 switch (track()->mode()) {
176 case ARDOUR::NonLayered:
177 rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
179 case ARDOUR::Destructive:
180 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
184 controls_table.attach (*rec_enable_button, 5, 6, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
186 if (is_midi_track()) {
187 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
189 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record"));
192 rec_enable_button->set_sensitive (_session->writable());
195 controls_hbox.pack_start(gm.get_level_meter(), false, false);
196 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
197 _route->input()->changed.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
198 _route->output()->changed.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
200 controls_table.attach (*mute_button, 6, 7, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
202 if (!_route->is_master()) {
203 controls_table.attach (*solo_button, 7, 8, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
206 controls_table.attach (route_group_button, 7, 8, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
207 controls_table.attach (gm.get_gain_slider(), 0, 5, 1, 2, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
209 ARDOUR_UI::instance()->set_tip(*solo_button,_("Solo"));
210 ARDOUR_UI::instance()->set_tip(*mute_button,_("Mute"));
211 ARDOUR_UI::instance()->set_tip(route_group_button, _("Route Group"));
212 ARDOUR_UI::instance()->set_tip(playlist_button,_("Playlist"));
213 ARDOUR_UI::instance()->set_tip(automation_button, _("Automation"));
217 controls_table.attach (automation_button, 6, 7, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
219 if (is_track() && track()->mode() == ARDOUR::Normal) {
220 controls_table.attach (playlist_button, 5, 6, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
225 _route->processors_changed.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
226 _route->PropertyChanged.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::route_property_changed, this, _1), gui_context());
230 str = gui_property ("layer-display");
232 set_layer_display (LayerDisplay (string_2_enum (str, _view->layer_display ())));
235 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
236 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
238 /* pick up the correct freeze state */
242 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_unit));
243 _editor.HorizontalPositionChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::horizontal_position_changed));
244 ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
246 PropertyList* plist = new PropertyList();
248 plist->add (ARDOUR::Properties::edit, true);
249 plist->add (ARDOUR::Properties::mute, true);
250 plist->add (ARDOUR::Properties::solo, true);
252 route_group_menu = new RouteGroupMenu (_session, plist);
254 gm.get_gain_slider().signal_scroll_event().connect(sigc::mem_fun(*this, &RouteTimeAxisView::controls_ebox_scroll), false);
255 gm.get_gain_slider().set_name ("TrackGainFader");
257 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
263 RouteTimeAxisView::~RouteTimeAxisView ()
265 CatchDeletion (this);
267 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
271 delete playlist_action_menu;
272 playlist_action_menu = 0;
277 _automation_tracks.clear ();
279 delete route_group_menu;
283 RouteTimeAxisView::post_construct ()
285 /* map current state of the route */
287 update_diskstream_display ();
288 setup_processor_menu_and_curves ();
289 reset_processor_automation_curves ();
292 /** Set up the processor menu for the current set of processors, and
293 * display automation curves for any parameters which have data.
296 RouteTimeAxisView::setup_processor_menu_and_curves ()
298 _subplugin_menu_map.clear ();
299 subplugin_menu.items().clear ();
300 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
301 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
305 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
307 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
308 if (_route->route_group()) {
309 _route->route_group()->remove (_route);
315 r.push_back (route ());
317 route_group_menu->build (r);
318 route_group_menu->menu()->popup (ev->button, ev->time);
324 RouteTimeAxisView::playlist_changed ()
330 RouteTimeAxisView::label_view ()
332 string x = _route->name();
334 if (x != name_entry.get_text()) {
335 name_entry.set_text (x);
338 if (x != name_label.get_text()) {
339 name_label.set_text (x);
342 ARDOUR_UI::instance()->set_tip (name_entry, x);
346 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
348 if (what_changed.contains (ARDOUR::Properties::name)) {
354 RouteTimeAxisView::take_name_changed (void *src)
362 RouteTimeAxisView::playlist_click ()
364 build_playlist_menu ();
365 conditionally_add_to_selection ();
366 playlist_action_menu->popup (1, gtk_get_current_event_time());
370 RouteTimeAxisView::automation_click ()
372 conditionally_add_to_selection ();
373 build_automation_action_menu (false);
374 automation_action_menu->popup (1, gtk_get_current_event_time());
378 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
380 using namespace Menu_Helpers;
382 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
383 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
386 detach_menu (subplugin_menu);
388 _main_automation_menu_map.clear ();
389 delete automation_action_menu;
390 automation_action_menu = new Menu;
392 MenuList& items = automation_action_menu->items();
394 automation_action_menu->set_name ("ArdourContextMenu");
396 items.push_back (MenuElem (_("Show All Automation"),
397 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
399 items.push_back (MenuElem (_("Show Existing Automation"),
400 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
402 items.push_back (MenuElem (_("Hide All Automation"),
403 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
405 items.push_back (SeparatorElem ());
407 /* Attach the plugin submenu. It may have previously been used elsewhere,
408 so it was detached above */
410 items.push_back (MenuElem (_("Plugins"), subplugin_menu));
411 items.back().set_sensitive (!subplugin_menu.items().empty() && (!for_selection || _editor.get_selection().tracks.size() == 1));;
415 RouteTimeAxisView::build_display_menu ()
417 using namespace Menu_Helpers;
421 TimeAxisView::build_display_menu ();
423 /* now fill it with our stuff */
425 MenuList& items = display_menu->items();
426 display_menu->set_name ("ArdourContextMenu");
428 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
431 detach_menu (*_size_menu);
434 items.push_back (MenuElem (_("Height"), *_size_menu));
436 items.push_back (SeparatorElem());
438 if (!Profile->get_sae()) {
439 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
440 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
441 items.push_back (SeparatorElem());
444 // Hook for derived classes to add type specific stuff
445 append_extra_display_menu_items ();
449 Menu* layers_menu = manage (new Menu);
450 MenuList &layers_items = layers_menu->items();
451 layers_menu->set_name("ArdourContextMenu");
453 RadioMenuItem::Group layers_group;
455 /* Find out how many overlaid/stacked tracks we have in the selection */
459 TrackSelection const & s = _editor.get_selection().tracks;
460 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
461 StreamView* v = (*i)->view ();
466 switch (v->layer_display ()) {
476 /* We're not connecting to signal_toggled() here; in the case where these two items are
477 set to be in the `inconsistent' state, it seems that one or other will end up active
478 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
479 select the active one, no toggled signal is emitted so nothing happens.
482 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
483 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
484 i->set_active (overlaid != 0 && stacked == 0);
485 i->set_inconsistent (overlaid != 0 && stacked != 0);
486 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
488 layers_items.push_back (
489 RadioMenuElem (layers_group, _("Stacked"),
490 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true))
493 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
494 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
495 i->set_active (overlaid == 0 && stacked != 0);
496 i->set_inconsistent (overlaid != 0 && stacked != 0);
498 items.push_back (MenuElem (_("Layers"), *layers_menu));
500 if (!Profile->get_sae()) {
502 Menu* alignment_menu = manage (new Menu);
503 MenuList& alignment_items = alignment_menu->items();
504 alignment_menu->set_name ("ArdourContextMenu");
506 RadioMenuItem::Group align_group;
508 /* Same verbose hacks as for the layering options above */
514 boost::shared_ptr<Track> first_track;
516 TrackSelection const & s = _editor.get_selection().tracks;
517 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
518 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
519 if (!r || !r->is_track ()) {
524 first_track = r->track();
527 switch (r->track()->alignment_choice()) {
531 switch (r->track()->alignment_style()) {
532 case ExistingMaterial:
540 case UseExistingMaterial:
556 inconsistent = false;
565 if (!inconsistent && first_track) {
567 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
568 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
569 i->set_active (automatic != 0 && existing == 0 && capture == 0);
570 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
572 switch (first_track->alignment_choice()) {
574 switch (first_track->alignment_style()) {
575 case ExistingMaterial:
576 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
579 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
587 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
588 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
589 i->set_active (existing != 0 && capture == 0 && automatic == 0);
590 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
592 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
593 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
594 i->set_active (existing == 0 && capture != 0 && automatic == 0);
595 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
597 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
603 Menu* mode_menu = manage (new Menu);
604 MenuList& mode_items = mode_menu->items ();
605 mode_menu->set_name ("ArdourContextMenu");
607 RadioMenuItem::Group mode_group;
613 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
614 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
615 if (!r || !r->is_track ()) {
619 switch (r->track()->mode()) {
632 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
633 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
634 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
635 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
636 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
638 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
639 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
640 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
641 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
642 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
644 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
645 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
646 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
647 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
648 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
650 items.push_back (MenuElem (_("Mode"), *mode_menu));
653 color_mode_menu = build_color_mode_menu();
654 if (color_mode_menu) {
655 items.push_back (MenuElem (_("Color Mode"), *color_mode_menu));
658 items.push_back (SeparatorElem());
660 build_playlist_menu ();
661 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
662 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
664 route_group_menu->detach ();
667 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
668 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
670 r.push_back (rtv->route ());
675 r.push_back (route ());
678 route_group_menu->build (r);
679 items.push_back (MenuElem (_("Route Group"), *route_group_menu->menu ()));
681 build_automation_action_menu (true);
682 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
684 items.push_back (SeparatorElem());
689 TrackSelection const & s = _editor.get_selection().tracks;
690 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
691 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
696 if (r->route()->active()) {
703 items.push_back (CheckMenuElem (_("Active")));
704 CheckMenuItem* i = dynamic_cast<CheckMenuItem *> (&items.back());
705 bool click_sets_active = true;
706 if (active > 0 && inactive == 0) {
707 i->set_active (true);
708 click_sets_active = false;
709 } else if (active > 0 && inactive > 0) {
710 i->set_inconsistent (true);
712 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
714 items.push_back (SeparatorElem());
715 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
716 if (!Profile->get_sae()) {
717 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
719 items.push_front (SeparatorElem());
720 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
725 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
727 if (apply_to_selection) {
728 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
733 if (!track()->can_use_mode (mode, needs_bounce)) {
739 cerr << "would bounce this one\n";
744 track()->set_mode (mode);
746 rec_enable_button->remove ();
749 case ARDOUR::NonLayered:
751 rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
752 rec_enable_button->set_text (string());
754 case ARDOUR::Destructive:
755 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
756 rec_enable_button->set_text (string());
760 rec_enable_button->show_all ();
765 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end)
771 TimeAxisView::show_timestretch (start, end);
781 /* check that the time selection was made in our route, or our route group.
782 remember that route_group() == 0 implies the route is *not* in a edit group.
785 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
786 /* this doesn't apply to us */
790 /* ignore it if our edit group is not active */
792 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
797 if (timestretch_rect == 0) {
798 timestretch_rect = new SimpleRect (*canvas_display ());
799 timestretch_rect->property_x1() = 0.0;
800 timestretch_rect->property_y1() = 0.0;
801 timestretch_rect->property_x2() = 0.0;
802 timestretch_rect->property_y2() = 0.0;
803 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
804 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
807 timestretch_rect->show ();
808 timestretch_rect->raise_to_top ();
810 x1 = start / _editor.get_current_zoom();
811 x2 = (end - 1) / _editor.get_current_zoom();
812 y2 = current_height() - 2;
814 timestretch_rect->property_x1() = x1;
815 timestretch_rect->property_y1() = 1.0;
816 timestretch_rect->property_x2() = x2;
817 timestretch_rect->property_y2() = y2;
821 RouteTimeAxisView::hide_timestretch ()
823 TimeAxisView::hide_timestretch ();
825 if (timestretch_rect) {
826 timestretch_rect->hide ();
831 RouteTimeAxisView::show_selection (TimeSelection& ts)
835 /* ignore it if our edit group is not active or if the selection was started
836 in some other track or route group (remember that route_group() == 0 means
837 that the track is not in an route group).
840 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
841 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
847 TimeAxisView::show_selection (ts);
851 RouteTimeAxisView::set_height (uint32_t h)
854 bool height_changed = (height == 0) || (h != height);
855 gm.get_level_meter().setup_meters (gmlen);
857 TimeAxisView::set_height (h);
860 _view->set_height ((double) current_height());
863 if (height >= preset_height (HeightNormal)) {
867 gm.get_gain_slider().show();
869 if (!_route || _route->is_monitor()) {
874 if (rec_enable_button)
875 rec_enable_button->show();
877 route_group_button.show();
878 automation_button.show();
880 if (is_track() && track()->mode() == ARDOUR::Normal) {
881 playlist_button.show();
888 gm.get_gain_slider().hide();
890 if (!_route || _route->is_monitor()) {
895 if (rec_enable_button)
896 rec_enable_button->show();
898 route_group_button.hide ();
899 automation_button.hide ();
901 if (is_track() && track()->mode() == ARDOUR::Normal) {
902 playlist_button.hide ();
907 if (height_changed && !no_redraw) {
908 /* only emit the signal if the height really changed */
914 RouteTimeAxisView::route_color_changed ()
917 _view->apply_color (color(), StreamView::RegionColor);
922 RouteTimeAxisView::reset_samples_per_unit ()
924 set_samples_per_unit (_editor.get_current_zoom());
928 RouteTimeAxisView::horizontal_position_changed ()
931 _view->horizontal_position_changed ();
936 RouteTimeAxisView::set_samples_per_unit (double spu)
941 speed = track()->speed();
945 _view->set_samples_per_unit (spu * speed);
948 TimeAxisView::set_samples_per_unit (spu * speed);
952 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
954 if (!mitem->get_active()) {
955 /* this is one of the two calls made when these radio menu items change status. this one
956 is for the item that became inactive, and we want to ignore it.
961 if (apply_to_selection) {
962 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
965 track()->set_align_choice (choice);
971 RouteTimeAxisView::rename_current_playlist ()
973 ArdourPrompter prompter (true);
976 boost::shared_ptr<Track> tr = track();
977 if (!tr || tr->destructive()) {
981 boost::shared_ptr<Playlist> pl = tr->playlist();
986 prompter.set_title (_("Rename Playlist"));
987 prompter.set_prompt (_("New name for playlist:"));
988 prompter.set_initial_text (pl->name());
989 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
990 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
992 switch (prompter.run ()) {
993 case Gtk::RESPONSE_ACCEPT:
994 prompter.get_result (name);
1006 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1008 std::string ret (basename);
1010 std::string const group_string = "." + route_group()->name() + ".";
1012 // iterate through all playlists
1014 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1015 std::string tmp = (*i)->name();
1017 std::string::size_type idx = tmp.find(group_string);
1018 // find those which belong to this group
1019 if (idx != string::npos) {
1020 tmp = tmp.substr(idx + group_string.length());
1022 // and find the largest current number
1023 int x = atoi(tmp.c_str());
1024 if (x > maxnumber) {
1033 snprintf (buf, sizeof(buf), "%d", maxnumber);
1035 ret = this->name() + "." + route_group()->name () + "." + buf;
1041 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1045 boost::shared_ptr<Track> tr = track ();
1046 if (!tr || tr->destructive()) {
1050 boost::shared_ptr<const Playlist> pl = tr->playlist();
1057 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::edit.property_id)) {
1058 name = resolve_new_group_playlist_name(name, playlists_before_op);
1061 while (_session->playlists->by_name(name)) {
1062 name = Playlist::bump_name (name, *_session);
1065 // TODO: The prompter "new" button should be de-activated if the user
1066 // specifies a playlist name which already exists in the session.
1070 ArdourPrompter prompter (true);
1072 prompter.set_title (_("New Copy Playlist"));
1073 prompter.set_prompt (_("Name for new playlist:"));
1074 prompter.set_initial_text (name);
1075 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1076 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1077 prompter.show_all ();
1079 switch (prompter.run ()) {
1080 case Gtk::RESPONSE_ACCEPT:
1081 prompter.get_result (name);
1089 if (name.length()) {
1090 tr->use_copy_playlist ();
1091 tr->playlist()->set_name (name);
1096 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1100 boost::shared_ptr<Track> tr = track ();
1101 if (!tr || tr->destructive()) {
1105 boost::shared_ptr<const Playlist> pl = tr->playlist();
1112 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::edit.property_id)) {
1113 name = resolve_new_group_playlist_name(name,playlists_before_op);
1116 while (_session->playlists->by_name(name)) {
1117 name = Playlist::bump_name (name, *_session);
1123 ArdourPrompter prompter (true);
1125 prompter.set_title (_("New Playlist"));
1126 prompter.set_prompt (_("Name for new playlist:"));
1127 prompter.set_initial_text (name);
1128 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1129 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1131 switch (prompter.run ()) {
1132 case Gtk::RESPONSE_ACCEPT:
1133 prompter.get_result (name);
1141 if (name.length()) {
1142 tr->use_new_playlist ();
1143 tr->playlist()->set_name (name);
1148 RouteTimeAxisView::clear_playlist ()
1150 boost::shared_ptr<Track> tr = track ();
1151 if (!tr || tr->destructive()) {
1155 boost::shared_ptr<Playlist> pl = tr->playlist();
1160 _editor.clear_playlist (pl);
1164 RouteTimeAxisView::speed_changed ()
1166 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_unit, this));
1170 RouteTimeAxisView::update_diskstream_display ()
1180 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1182 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1184 /* special case: select/deselect all tracks */
1185 if (_editor.get_selection().selected (this)) {
1186 _editor.get_selection().clear_tracks ();
1188 _editor.select_all_tracks ();
1194 switch (ArdourKeyboard::selection_type (ev->state)) {
1195 case Selection::Toggle:
1196 _editor.get_selection().toggle (this);
1199 case Selection::Set:
1200 _editor.get_selection().set (this);
1203 case Selection::Extend:
1204 _editor.extend_selection_to_track (*this);
1207 case Selection::Add:
1208 _editor.get_selection().add (this);
1214 RouteTimeAxisView::set_selected_points (PointSelection& points)
1216 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1217 (*i)->set_selected_points (points);
1222 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1225 _view->set_selected_regionviews (regions);
1229 /** Add the selectable things that we have to a list.
1230 * @param results List to add things to.
1233 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1238 speed = track()->speed();
1241 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1242 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1244 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1245 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1248 /* pick up visible automation tracks */
1250 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1251 if (!(*i)->hidden()) {
1252 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1258 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1261 _view->get_inverted_selectables (sel, results);
1264 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1265 if (!(*i)->hidden()) {
1266 (*i)->get_inverted_selectables (sel, results);
1274 RouteTimeAxisView::route_group () const
1276 return _route->route_group();
1280 RouteTimeAxisView::name() const
1282 return _route->name();
1285 boost::shared_ptr<Playlist>
1286 RouteTimeAxisView::playlist () const
1288 boost::shared_ptr<Track> tr;
1290 if ((tr = track()) != 0) {
1291 return tr->playlist();
1293 return boost::shared_ptr<Playlist> ();
1298 RouteTimeAxisView::name_entry_changed ()
1300 string x = name_entry.get_text ();
1302 if (x == _route->name()) {
1306 strip_whitespace_edges (x);
1308 if (x.length() == 0) {
1309 name_entry.set_text (_route->name());
1313 if (_session->route_name_internal (x)) {
1314 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1316 name_entry.grab_focus ();
1317 } else if (RouteUI::verify_new_route_name (x)) {
1318 _route->set_name (x);
1320 name_entry.grab_focus ();
1324 boost::shared_ptr<Region>
1325 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1327 boost::shared_ptr<Playlist> pl = playlist ();
1330 return pl->find_next_region (pos, point, dir);
1333 return boost::shared_ptr<Region> ();
1337 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1339 boost::shared_ptr<Playlist> pl = playlist ();
1342 return pl->find_next_region_boundary (pos, dir);
1349 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1351 boost::shared_ptr<Playlist> what_we_got;
1352 boost::shared_ptr<Track> tr = track ();
1353 boost::shared_ptr<Playlist> playlist;
1356 /* route is a bus, not a track */
1360 playlist = tr->playlist();
1362 TimeSelection time (selection.time);
1363 float const speed = tr->speed();
1364 if (speed != 1.0f) {
1365 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1366 (*i).start = session_frame_to_track_frame((*i).start, speed);
1367 (*i).end = session_frame_to_track_frame((*i).end, speed);
1371 playlist->clear_changes ();
1372 playlist->clear_owned_changes ();
1376 if (playlist->cut (time) != 0) {
1377 vector<Command*> cmds;
1378 playlist->rdiff (cmds);
1379 _session->add_commands (cmds);
1381 _session->add_command (new StatefulDiffCommand (playlist));
1386 if ((what_we_got = playlist->cut (time)) != 0) {
1387 _editor.get_cut_buffer().add (what_we_got);
1388 vector<Command*> cmds;
1389 playlist->rdiff (cmds);
1390 _session->add_commands (cmds);
1392 _session->add_command (new StatefulDiffCommand (playlist));
1396 if ((what_we_got = playlist->copy (time)) != 0) {
1397 _editor.get_cut_buffer().add (what_we_got);
1402 if ((what_we_got = playlist->cut (time)) != 0) {
1404 vector<Command*> cmds;
1405 playlist->rdiff (cmds);
1406 _session->add_commands (cmds);
1407 _session->add_command (new StatefulDiffCommand (playlist));
1408 what_we_got->release ();
1415 RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
1421 boost::shared_ptr<Playlist> pl = playlist ();
1422 PlaylistSelection::iterator p;
1424 for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
1426 if (p == selection.playlists.end()) {
1430 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1432 if (track()->speed() != 1.0f) {
1433 pos = session_frame_to_track_frame (pos, track()->speed());
1434 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1437 pl->clear_changes ();
1438 pl->paste (*p, pos, times);
1439 _session->add_command (new StatefulDiffCommand (pl));
1445 struct PlaylistSorter {
1446 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1447 return a->sort_id() < b->sort_id();
1452 RouteTimeAxisView::build_playlist_menu ()
1454 using namespace Menu_Helpers;
1460 delete playlist_action_menu;
1461 playlist_action_menu = new Menu;
1462 playlist_action_menu->set_name ("ArdourContextMenu");
1464 MenuList& playlist_items = playlist_action_menu->items();
1465 playlist_action_menu->set_name ("ArdourContextMenu");
1466 playlist_items.clear();
1468 RadioMenuItem::Group playlist_group;
1469 boost::shared_ptr<Track> tr = track ();
1471 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1473 /* sort the playlists */
1475 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1477 /* add the playlists to the menu */
1478 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1479 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1480 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1481 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1483 if (tr->playlist()->id() == (*i)->id()) {
1489 playlist_items.push_back (SeparatorElem());
1490 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1491 playlist_items.push_back (SeparatorElem());
1493 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::edit.property_id)) {
1494 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1495 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1498 // Use a label which tells the user what is happening
1499 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1500 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1504 playlist_items.push_back (SeparatorElem());
1505 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1506 playlist_items.push_back (SeparatorElem());
1508 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1512 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1514 assert (is_track());
1516 // exit if we were triggered by deactivating the old playlist
1517 if (!item->get_active()) {
1521 boost::shared_ptr<Playlist> pl (wpl.lock());
1527 if (track()->playlist() == pl) {
1528 // exit when use_playlist is called by the creation of the playlist menu
1529 // or the playlist choice is unchanged
1533 track()->use_playlist (pl);
1535 RouteGroup* rg = route_group();
1537 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::edit.property_id)) {
1538 std::string group_string = "." + rg->name() + ".";
1540 std::string take_name = pl->name();
1541 std::string::size_type idx = take_name.find(group_string);
1543 if (idx == std::string::npos)
1546 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1548 boost::shared_ptr<RouteList> rl (rg->route_list());
1550 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1551 if ( (*i) == this->route()) {
1555 std::string playlist_name = (*i)->name()+group_string+take_name;
1557 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1562 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1564 // No playlist for this track for this take yet, make it
1565 track->use_new_playlist();
1566 track->playlist()->set_name(playlist_name);
1568 track->use_playlist(ipl);
1575 RouteTimeAxisView::show_playlist_selector ()
1577 _editor.playlist_selector().show_for (this);
1581 RouteTimeAxisView::map_frozen ()
1587 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1589 switch (track()->freeze_state()) {
1591 playlist_button.set_sensitive (false);
1592 rec_enable_button->set_sensitive (false);
1595 playlist_button.set_sensitive (true);
1596 rec_enable_button->set_sensitive (true);
1602 RouteTimeAxisView::color_handler ()
1604 //case cTimeStretchOutline:
1605 if (timestretch_rect) {
1606 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
1608 //case cTimeStretchFill:
1609 if (timestretch_rect) {
1610 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
1616 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1617 * Will add track if necessary.
1620 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1622 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1623 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1626 /* it doesn't exist yet, so we don't care about the button state: just add it */
1627 create_automation_child (param, true);
1630 bool yn = menu->get_active();
1631 bool changed = false;
1633 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1635 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1636 will have done that for us.
1639 if (changed && !no_redraw) {
1647 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1649 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1655 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1657 if (menu && !_hidden) {
1658 ignore_toggle = true;
1659 menu->set_active (false);
1660 ignore_toggle = false;
1663 if (_route && !no_redraw) {
1670 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1672 if (apply_to_selection) {
1673 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1677 /* Show our automation */
1679 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1680 i->second->set_marked_for_display (true);
1682 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1685 menu->set_active(true);
1690 /* Show processor automation */
1692 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1693 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1694 if ((*ii)->view == 0) {
1695 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1698 (*ii)->menu_item->set_active (true);
1711 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1713 if (apply_to_selection) {
1714 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1718 /* Show our automation */
1720 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1721 if (i->second->has_automation()) {
1722 i->second->set_marked_for_display (true);
1724 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1726 menu->set_active(true);
1731 /* Show processor automation */
1733 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1734 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1735 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1736 (*ii)->menu_item->set_active (true);
1748 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1750 if (apply_to_selection) {
1751 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1755 /* Hide our automation */
1757 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1758 i->second->set_marked_for_display (false);
1760 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1763 menu->set_active (false);
1767 /* Hide processor automation */
1769 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1770 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1771 (*ii)->menu_item->set_active (false);
1782 RouteTimeAxisView::region_view_added (RegionView* rv)
1784 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1785 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1786 boost::shared_ptr<AutomationTimeAxisView> atv;
1788 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1793 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1794 (*i)->add_ghost(rv);
1798 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1800 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1806 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1808 parent.remove_processor_automation_node (this);
1812 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1815 remove_child (pan->view);
1819 RouteTimeAxisView::ProcessorAutomationNode*
1820 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1822 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1824 if ((*i)->processor == processor) {
1826 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1827 if ((*ii)->what == what) {
1837 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
1839 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1842 ProcessorAutomationNode* pan;
1844 if ((pan = find_processor_automation_node (processor, what)) == 0) {
1845 /* session state may never have been saved with new plugin */
1846 error << _("programming error: ")
1847 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
1848 processor->name(), what.type(), (int) what.channel(), what.id() )
1858 boost::shared_ptr<AutomationControl> control
1859 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
1861 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
1862 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
1863 _editor, *this, false, parent_canvas,
1864 processor->describe_parameter (what), processor->name()));
1866 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
1868 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
1871 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
1876 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
1879 pan->menu_item->set_active (false);
1888 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
1890 boost::shared_ptr<Processor> processor (p.lock ());
1892 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
1893 /* The Amp processor is a special case and is dealt with separately */
1897 set<Evoral::Parameter> existing;
1899 processor->what_has_data (existing);
1901 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
1903 Evoral::Parameter param (*i);
1904 boost::shared_ptr<AutomationLine> al;
1906 if ((al = find_processor_automation_curve (processor, param)) != 0) {
1909 add_processor_automation_curve (processor, param);
1915 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
1917 using namespace Menu_Helpers;
1921 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
1923 _automation_tracks[param] = track;
1925 /* existing state overrides "show" argument */
1926 string s = track->gui_property ("visible");
1928 show = string_is_affirmative (s);
1931 /* this might or might not change the visibility status, so don't rely on it */
1932 track->set_marked_for_display (show);
1934 if (show && !no_redraw) {
1938 if (!EventTypeMap::instance().is_midi_parameter(param)) {
1939 /* MIDI-related parameters are always in the menu, there's no
1940 reason to rebuild the menu just because we added a automation
1941 lane for one of them. But if we add a non-MIDI automation
1942 lane, then we need to invalidate the display menu.
1944 delete display_menu;
1950 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
1952 boost::shared_ptr<Processor> processor (p.lock ());
1954 if (!processor || !processor->display_to_user ()) {
1958 /* we use this override to veto the Amp processor from the plugin menu,
1959 as its automation lane can be accessed using the special "Fader" menu
1963 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
1967 using namespace Menu_Helpers;
1968 ProcessorAutomationInfo *rai;
1969 list<ProcessorAutomationInfo*>::iterator x;
1971 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
1973 if (automatable.empty()) {
1977 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
1978 if ((*x)->processor == processor) {
1983 if (x == processor_automation.end()) {
1985 rai = new ProcessorAutomationInfo (processor);
1986 processor_automation.push_back (rai);
1994 /* any older menu was deleted at the top of processors_changed()
1995 when we cleared the subplugin menu.
1998 rai->menu = manage (new Menu);
1999 MenuList& items = rai->menu->items();
2000 rai->menu->set_name ("ArdourContextMenu");
2004 std::set<Evoral::Parameter> has_visible_automation;
2005 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2007 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2009 ProcessorAutomationNode* pan;
2010 CheckMenuItem* mitem;
2012 string name = processor->describe_parameter (*i);
2014 items.push_back (CheckMenuElem (name));
2015 mitem = dynamic_cast<CheckMenuItem*> (&items.back());
2017 _subplugin_menu_map[*i] = mitem;
2019 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2020 mitem->set_active(true);
2023 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2027 pan = new ProcessorAutomationNode (*i, mitem, *this);
2029 rai->lines.push_back (pan);
2033 pan->menu_item = mitem;
2037 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2040 /* add the menu for this processor, because the subplugin
2041 menu is always cleared at the top of processors_changed().
2042 this is the result of some poor design in gtkmm and/or
2046 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2051 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2052 RouteTimeAxisView::ProcessorAutomationNode* pan)
2054 bool showit = pan->menu_item->get_active();
2055 bool redraw = false;
2057 if (pan->view == 0 && showit) {
2058 add_processor_automation_curve (rai->processor, pan->what);
2062 if (pan->view && pan->view->set_marked_for_display (showit)) {
2066 if (redraw && !no_redraw) {
2072 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2074 if (c.type == RouteProcessorChange::MeterPointChange) {
2075 /* nothing to do if only the meter point has changed */
2079 using namespace Menu_Helpers;
2081 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2082 (*i)->valid = false;
2085 setup_processor_menu_and_curves ();
2087 bool deleted_processor_automation = false;
2089 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2091 list<ProcessorAutomationInfo*>::iterator tmp;
2099 processor_automation.erase (i);
2100 deleted_processor_automation = true;
2107 if (deleted_processor_automation && !no_redraw) {
2112 boost::shared_ptr<AutomationLine>
2113 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2115 ProcessorAutomationNode* pan;
2117 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2123 return boost::shared_ptr<AutomationLine>();
2127 RouteTimeAxisView::reset_processor_automation_curves ()
2129 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2135 RouteTimeAxisView::update_rec_display ()
2137 RouteUI::update_rec_display ();
2138 name_entry.set_sensitive (!_route->record_enabled());
2142 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2144 if (apply_to_selection) {
2145 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2149 _view->set_layer_display (d);
2152 set_gui_property (X_("layer-display"), enum_2_string (d));
2157 RouteTimeAxisView::layer_display () const
2160 return _view->layer_display ();
2163 /* we don't know, since we don't have a _view, so just return something */
2169 boost::shared_ptr<AutomationTimeAxisView>
2170 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2172 AutomationTracks::iterator i = _automation_tracks.find(param);
2173 if (i != _automation_tracks.end()) {
2176 return boost::shared_ptr<AutomationTimeAxisView>();
2181 RouteTimeAxisView::fast_update ()
2183 gm.get_level_meter().update_meters ();
2187 RouteTimeAxisView::hide_meter ()
2190 gm.get_level_meter().hide_meters ();
2194 RouteTimeAxisView::show_meter ()
2200 RouteTimeAxisView::reset_meter ()
2202 if (Config->get_show_track_meters()) {
2203 gm.get_level_meter().setup_meters (height-5);
2210 RouteTimeAxisView::clear_meter ()
2212 gm.get_level_meter().clear_meters ();
2216 RouteTimeAxisView::meter_changed ()
2218 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2223 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2229 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2231 using namespace Menu_Helpers;
2233 if (!_underlay_streams.empty()) {
2234 MenuList& parent_items = parent_menu->items();
2235 Menu* gs_menu = manage (new Menu);
2236 gs_menu->set_name ("ArdourContextMenu");
2237 MenuList& gs_items = gs_menu->items();
2239 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2241 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2242 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2243 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2249 RouteTimeAxisView::set_underlay_state()
2251 if (!underlay_xml_node) {
2255 XMLNodeList nlist = underlay_xml_node->children();
2256 XMLNodeConstIterator niter;
2257 XMLNode *child_node;
2259 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2260 child_node = *niter;
2262 if (child_node->name() != "Underlay") {
2266 XMLProperty* prop = child_node->property ("id");
2268 PBD::ID id (prop->value());
2270 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2273 add_underlay(v->view(), false);
2282 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2288 RouteTimeAxisView& other = v->trackview();
2290 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2291 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2292 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2296 _underlay_streams.push_back(v);
2297 other._underlay_mirrors.push_back(this);
2299 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2301 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2303 if (!underlay_xml_node) {
2304 underlay_xml_node = xml_node->add_child("Underlays");
2307 XMLNode* node = underlay_xml_node->add_child("Underlay");
2308 XMLProperty* prop = node->add_property("id");
2309 prop->set_value(v->trackview().route()->id().to_s());
2316 RouteTimeAxisView::remove_underlay (StreamView* v)
2322 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2323 RouteTimeAxisView& other = v->trackview();
2325 if (it != _underlay_streams.end()) {
2326 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2328 if (gm == other._underlay_mirrors.end()) {
2329 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2333 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2335 _underlay_streams.erase(it);
2336 other._underlay_mirrors.erase(gm);
2338 if (underlay_xml_node) {
2339 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2345 RouteTimeAxisView::set_button_names ()
2347 if (_route && _route->solo_safe()) {
2348 solo_button->remove ();
2349 if (solo_safe_pixbuf == 0) {
2350 solo_safe_pixbuf = ::get_icon("solo-safe-icon");
2352 solo_button->set_image (solo_safe_pixbuf);
2353 solo_button->set_text (string());
2355 solo_button->set_image (Glib::RefPtr<Gdk::Pixbuf>());
2356 if (Config->get_solo_control_is_listen_control()) {
2357 switch (Config->get_listen_position()) {
2358 case AfterFaderListen:
2359 solo_button->set_text (_("A"));
2361 case PreFaderListen:
2362 solo_button->set_text (_("P"));
2366 solo_button->set_text (_("s"));
2369 mute_button->set_text (_("m"));
2373 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2375 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2376 if (i != _main_automation_menu_map.end()) {
2380 i = _subplugin_menu_map.find (param);
2381 if (i != _subplugin_menu_map.end()) {
2389 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2391 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2393 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2397 gain_track.reset (new AutomationTimeAxisView (_session,
2398 _route, _route->amp(), c, param,
2403 _route->amp()->describe_parameter(param)));
2406 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2409 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2413 void add_region_to_list (RegionView* rv, Playlist::RegionList* l)
2415 l->push_back (rv->region());
2419 RouteTimeAxisView::combine_regions ()
2421 /* as of may 2011, we do not offer uncombine for MIDI tracks
2424 if (!is_audio_track()) {
2432 Playlist::RegionList selected_regions;
2433 boost::shared_ptr<Playlist> playlist = track()->playlist();
2435 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2437 if (selected_regions.size() < 2) {
2441 playlist->clear_changes ();
2442 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2444 _session->add_command (new StatefulDiffCommand (playlist));
2445 /* make the new region be selected */
2447 return _view->find_view (compound_region);
2451 RouteTimeAxisView::uncombine_regions ()
2453 /* as of may 2011, we do not offer uncombine for MIDI tracks
2455 if (!is_audio_track()) {
2463 Playlist::RegionList selected_regions;
2464 boost::shared_ptr<Playlist> playlist = track()->playlist();
2466 /* have to grab selected regions first because the uncombine is going
2467 * to change that in the middle of the list traverse
2470 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2472 playlist->clear_changes ();
2474 for (Playlist::RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2475 playlist->uncombine (*i);
2478 _session->add_command (new StatefulDiffCommand (playlist));
2482 RouteTimeAxisView::state_id() const
2484 return string_compose ("rtav %1", _route->id().to_s());