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"
66 #include "global_signals.h"
67 #include "route_time_axis.h"
68 #include "automation_time_axis.h"
69 #include "canvas_impl.h"
70 #include "crossfade_view.h"
72 #include "gui_thread.h"
74 #include "playlist_selector.h"
75 #include "point_selection.h"
77 #include "public_editor.h"
78 #include "region_view.h"
79 #include "rgb_macros.h"
80 #include "selection.h"
81 #include "simplerect.h"
82 #include "streamview.h"
84 #include "route_group_menu.h"
86 #include "ardour/track.h"
90 using namespace ARDOUR;
92 using namespace Gtkmm2ext;
94 using namespace Editing;
97 Glib::RefPtr<Gdk::Pixbuf> RouteTimeAxisView::slider;
100 RouteTimeAxisView::setup_slider_pix ()
102 if ((slider = ::get_icon ("fader_belt_h")) == 0) {
103 throw failed_constructor ();
107 RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, boost::shared_ptr<Route> rt, Canvas& canvas)
110 , TimeAxisView(sess,ed,(TimeAxisView*) 0, canvas)
111 , parent_canvas (canvas)
112 , button_table (3, 3)
113 , route_group_button (_("g"))
114 , playlist_button (_("p"))
115 , automation_button (_("a"))
116 , gm (sess, slider, true, 115)
118 gm.set_controls (_route, _route->shared_peak_meter(), _route->amp());
119 gm.get_level_meter().set_no_show_all();
120 gm.get_level_meter().setup_meters(50);
123 playlist_action_menu = 0;
124 automation_action_menu = 0;
125 plugins_submenu_item = 0;
129 if (!_route->is_hidden()) {
130 _marked_for_display = true;
134 update_solo_display ();
136 timestretch_rect = 0;
139 ignore_toggle = false;
141 route_group_button.set_name ("TrackGroupButton");
142 playlist_button.set_name ("TrackPlaylistButton");
143 automation_button.set_name ("TrackAutomationButton");
145 route_group_button.unset_flags (Gtk::CAN_FOCUS);
146 playlist_button.unset_flags (Gtk::CAN_FOCUS);
147 automation_button.unset_flags (Gtk::CAN_FOCUS);
149 route_group_button.signal_button_release_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
150 playlist_button.signal_clicked().connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click));
151 automation_button.signal_clicked().connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click));
157 rec_enable_button->remove ();
159 switch (track()->mode()) {
161 case ARDOUR::NonLayered:
162 rec_enable_button->add (*(manage (new Image (::get_icon (X_("record_normal_red"))))));
164 case ARDOUR::Destructive:
165 rec_enable_button->add (*(manage (new Image (::get_icon (X_("record_tape_red"))))));
168 rec_enable_button->show_all ();
170 controls_table.attach (*rec_enable_button, 5, 6, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
172 if (is_midi_track()) {
173 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
175 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record"));
178 rec_enable_button->set_sensitive (_session->writable());
181 controls_hbox.pack_start(gm.get_level_meter(), false, false);
182 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
183 _route->input()->changed.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
184 _route->output()->changed.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
186 controls_table.attach (*mute_button, 6, 7, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
188 if (!_route->is_master()) {
189 controls_table.attach (*solo_button, 7, 8, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
192 controls_table.attach (route_group_button, 7, 8, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
193 controls_table.attach (gm.get_gain_slider(), 0, 5, 1, 2, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
195 ARDOUR_UI::instance()->set_tip(*solo_button,_("Solo"));
196 ARDOUR_UI::instance()->set_tip(*mute_button,_("Mute"));
197 ARDOUR_UI::instance()->set_tip(route_group_button, _("Route Group"));
198 ARDOUR_UI::instance()->set_tip(playlist_button,_("Playlist"));
199 ARDOUR_UI::instance()->set_tip(automation_button, _("Automation"));
203 controls_table.attach (automation_button, 6, 7, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
205 if (is_track() && track()->mode() == ARDOUR::Normal) {
206 controls_table.attach (playlist_button, 5, 6, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
211 _route->processors_changed.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
212 _route->PropertyChanged.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::route_property_changed, this, _1), gui_context());
216 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
217 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
219 /* pick up the correct freeze state */
224 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_unit));
225 _editor.HorizontalPositionChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::horizontal_position_changed));
226 ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
228 PropertyList* plist = new PropertyList();
230 plist->add (ARDOUR::Properties::edit, true);
231 plist->add (ARDOUR::Properties::mute, true);
232 plist->add (ARDOUR::Properties::solo, true);
234 route_group_menu = new RouteGroupMenu (_session, plist);
236 gm.get_gain_slider().signal_scroll_event().connect(sigc::mem_fun(*this, &RouteTimeAxisView::controls_ebox_scroll), false);
237 gm.get_gain_slider().set_name ("TrackGainFader");
243 RouteTimeAxisView::~RouteTimeAxisView ()
245 CatchDeletion (this);
247 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
251 delete playlist_action_menu;
252 playlist_action_menu = 0;
257 _automation_tracks.clear ();
259 delete route_group_menu;
263 RouteTimeAxisView::post_construct ()
265 /* map current state of the route */
267 update_diskstream_display ();
269 _subplugin_menu_map.clear ();
270 subplugin_menu.items().clear ();
271 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
272 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
273 reset_processor_automation_curves ();
277 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
279 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
280 if (_route->route_group()) {
281 _route->route_group()->remove (_route);
287 r.push_back (route ());
289 route_group_menu->build (r);
290 route_group_menu->menu()->popup (ev->button, ev->time);
296 RouteTimeAxisView::playlist_changed ()
302 RouteTimeAxisView::label_view ()
304 string x = _route->name();
306 if (x != name_entry.get_text()) {
307 name_entry.set_text (x);
310 if (x != name_label.get_text()) {
311 name_label.set_text (x);
314 ARDOUR_UI::instance()->set_tip (name_entry, x);
318 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
320 if (what_changed.contains (ARDOUR::Properties::name)) {
326 RouteTimeAxisView::take_name_changed (void *src)
334 RouteTimeAxisView::playlist_click ()
336 build_playlist_menu ();
337 conditionally_add_to_selection ();
338 playlist_action_menu->popup (1, gtk_get_current_event_time());
342 RouteTimeAxisView::automation_click ()
344 conditionally_add_to_selection ();
345 build_automation_action_menu (false);
346 automation_action_menu->popup (1, gtk_get_current_event_time());
350 RouteTimeAxisView::set_state (const XMLNode& node, int version)
352 TimeAxisView::set_state (node, version);
354 XMLNodeList kids = node.children();
355 XMLNodeConstIterator iter;
356 const XMLProperty* prop;
358 if (_view && (prop = node.property ("layer-display"))) {
359 set_layer_display (LayerDisplay (string_2_enum (prop->value(), _view->layer_display ())));
366 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
368 using namespace Menu_Helpers;
370 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
371 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
374 detach_menu (subplugin_menu);
376 _main_automation_menu_map.clear ();
377 delete automation_action_menu;
378 automation_action_menu = new Menu;
380 MenuList& items = automation_action_menu->items();
382 automation_action_menu->set_name ("ArdourContextMenu");
384 items.push_back (MenuElem (_("Show All Automation"),
385 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
387 items.push_back (MenuElem (_("Show Existing Automation"),
388 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
390 items.push_back (MenuElem (_("Hide All Automation"),
391 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
393 items.push_back (SeparatorElem ());
395 /* Attach the plugin submenu. It may have previously been used elsewhere,
396 so it was detached above */
398 items.push_back (MenuElem (_("Plugins"), subplugin_menu));
399 items.back().set_sensitive (!subplugin_menu.items().empty() && (!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 ()) {
464 /* We're not connecting to signal_toggled() here; in the case where these two items are
465 set to be in the `inconsistent' state, it seems that one or other will end up active
466 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
467 select the active one, no toggled signal is emitted so nothing happens.
470 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
471 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
472 i->set_active (overlaid != 0 && stacked == 0);
473 i->set_inconsistent (overlaid != 0 && stacked != 0);
474 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
476 layers_items.push_back (
477 RadioMenuElem (layers_group, _("Stacked"),
478 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true))
481 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
482 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
483 i->set_active (overlaid == 0 && stacked != 0);
484 i->set_inconsistent (overlaid != 0 && stacked != 0);
486 items.push_back (MenuElem (_("Layers"), *layers_menu));
488 if (!Profile->get_sae()) {
490 Menu* alignment_menu = manage (new Menu);
491 MenuList& alignment_items = alignment_menu->items();
492 alignment_menu->set_name ("ArdourContextMenu");
494 RadioMenuItem::Group align_group;
496 /* Same verbose hacks as for the layering options above */
502 boost::shared_ptr<Track> first_track;
504 TrackSelection const & s = _editor.get_selection().tracks;
505 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
506 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
507 if (!r || !r->is_track ()) {
512 first_track = r->track();
515 switch (r->track()->alignment_choice()) {
519 switch (r->track()->alignment_style()) {
520 case ExistingMaterial:
528 case UseExistingMaterial:
544 inconsistent = false;
553 if (!inconsistent && first_track) {
555 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
556 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
557 i->set_active (automatic != 0 && existing == 0 && capture == 0);
558 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
560 switch (first_track->alignment_choice()) {
562 switch (first_track->alignment_style()) {
563 case ExistingMaterial:
564 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
567 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
575 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
576 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
577 i->set_active (existing != 0 && capture == 0 && automatic == 0);
578 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
580 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
581 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
582 i->set_active (existing == 0 && capture != 0 && automatic == 0);
583 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
585 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
591 Menu* mode_menu = manage (new Menu);
592 MenuList& mode_items = mode_menu->items ();
593 mode_menu->set_name ("ArdourContextMenu");
595 RadioMenuItem::Group mode_group;
601 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
602 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
603 if (!r || !r->is_track ()) {
607 switch (r->track()->mode()) {
620 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
621 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
622 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
623 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
624 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
626 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
627 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
628 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
629 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
630 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
632 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered 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::NonLayered, true));
635 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
636 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
638 items.push_back (MenuElem (_("Mode"), *mode_menu));
641 color_mode_menu = build_color_mode_menu();
642 if (color_mode_menu) {
643 items.push_back (MenuElem (_("Color Mode"), *color_mode_menu));
646 items.push_back (SeparatorElem());
648 build_playlist_menu ();
649 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
650 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
652 route_group_menu->detach ();
655 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
656 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
658 r.push_back (rtv->route ());
663 r.push_back (route ());
666 route_group_menu->build (r);
667 items.push_back (MenuElem (_("Route Group"), *route_group_menu->menu ()));
669 build_automation_action_menu (true);
670 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
672 items.push_back (SeparatorElem());
677 TrackSelection const & s = _editor.get_selection().tracks;
678 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
679 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
684 if (r->route()->active()) {
691 items.push_back (CheckMenuElem (_("Active")));
692 CheckMenuItem* i = dynamic_cast<CheckMenuItem *> (&items.back());
693 bool click_sets_active = true;
694 if (active > 0 && inactive == 0) {
695 i->set_active (true);
696 click_sets_active = false;
697 } else if (active > 0 && inactive > 0) {
698 i->set_inconsistent (true);
700 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
702 items.push_back (SeparatorElem());
703 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
704 if (!Profile->get_sae()) {
705 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
707 items.push_front (SeparatorElem());
708 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
713 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
715 if (apply_to_selection) {
716 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
721 if (!track()->can_use_mode (mode, needs_bounce)) {
727 cerr << "would bounce this one\n";
732 track()->set_mode (mode);
734 rec_enable_button->remove ();
737 case ARDOUR::NonLayered:
739 rec_enable_button->add (*(manage (new Image (::get_icon (X_("record_normal_red"))))));
741 case ARDOUR::Destructive:
742 rec_enable_button->add (*(manage (new Image (::get_icon (X_("record_tape_red"))))));
746 rec_enable_button->show_all ();
751 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end)
757 TimeAxisView::show_timestretch (start, end);
767 /* check that the time selection was made in our route, or our route group.
768 remember that route_group() == 0 implies the route is *not* in a edit group.
771 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
772 /* this doesn't apply to us */
776 /* ignore it if our edit group is not active */
778 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
783 if (timestretch_rect == 0) {
784 timestretch_rect = new SimpleRect (*canvas_display ());
785 timestretch_rect->property_x1() = 0.0;
786 timestretch_rect->property_y1() = 0.0;
787 timestretch_rect->property_x2() = 0.0;
788 timestretch_rect->property_y2() = 0.0;
789 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
790 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
793 timestretch_rect->show ();
794 timestretch_rect->raise_to_top ();
796 x1 = start / _editor.get_current_zoom();
797 x2 = (end - 1) / _editor.get_current_zoom();
798 y2 = current_height() - 2;
800 timestretch_rect->property_x1() = x1;
801 timestretch_rect->property_y1() = 1.0;
802 timestretch_rect->property_x2() = x2;
803 timestretch_rect->property_y2() = y2;
807 RouteTimeAxisView::hide_timestretch ()
809 TimeAxisView::hide_timestretch ();
811 if (timestretch_rect) {
812 timestretch_rect->hide ();
817 RouteTimeAxisView::show_selection (TimeSelection& ts)
821 /* ignore it if our edit group is not active or if the selection was started
822 in some other track or route group (remember that route_group() == 0 means
823 that the track is not in an route group).
826 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
827 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
833 TimeAxisView::show_selection (ts);
837 RouteTimeAxisView::set_height (uint32_t h)
840 bool height_changed = (height == 0) || (h != height);
841 gm.get_level_meter().setup_meters (gmlen);
843 TimeAxisView::set_height (h);
848 _view->set_height ((double) current_height());
852 snprintf (buf, sizeof (buf), "%u", height);
853 xml_node->add_property ("height", buf);
855 if (height >= preset_height (HeightNormal)) {
859 gm.get_gain_slider().show();
861 if (!_route || _route->is_monitor()) {
866 if (rec_enable_button)
867 rec_enable_button->show();
869 route_group_button.show();
870 automation_button.show();
872 if (is_track() && track()->mode() == ARDOUR::Normal) {
873 playlist_button.show();
880 gm.get_gain_slider().hide();
882 if (!_route || _route->is_monitor()) {
887 if (rec_enable_button)
888 rec_enable_button->show();
890 route_group_button.hide ();
891 automation_button.hide ();
893 if (is_track() && track()->mode() == ARDOUR::Normal) {
894 playlist_button.hide ();
899 if (height_changed && !no_redraw) {
900 /* only emit the signal if the height really changed */
901 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
906 RouteTimeAxisView::set_color (Gdk::Color const & c)
908 RouteUI::set_color (c);
911 _view->apply_color (_color, StreamView::RegionColor);
916 RouteTimeAxisView::reset_samples_per_unit ()
918 set_samples_per_unit (_editor.get_current_zoom());
922 RouteTimeAxisView::horizontal_position_changed ()
925 _view->horizontal_position_changed ();
930 RouteTimeAxisView::set_samples_per_unit (double spu)
935 speed = track()->speed();
939 _view->set_samples_per_unit (spu * speed);
942 TimeAxisView::set_samples_per_unit (spu * speed);
946 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
948 if (!mitem->get_active()) {
949 /* this is one of the two calls made when these radio menu items change status. this one
950 is for the item that became inactive, and we want to ignore it.
955 if (apply_to_selection) {
956 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
959 track()->set_align_choice (choice);
965 RouteTimeAxisView::rename_current_playlist ()
967 ArdourPrompter prompter (true);
970 boost::shared_ptr<Track> tr = track();
971 if (!tr || tr->destructive()) {
975 boost::shared_ptr<Playlist> pl = tr->playlist();
980 prompter.set_title (_("Rename Playlist"));
981 prompter.set_prompt (_("New name for playlist:"));
982 prompter.set_initial_text (pl->name());
983 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
984 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
986 switch (prompter.run ()) {
987 case Gtk::RESPONSE_ACCEPT:
988 prompter.get_result (name);
1000 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1002 std::string ret (basename);
1004 std::string const group_string = "." + route_group()->name() + ".";
1006 // iterate through all playlists
1008 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1009 std::string tmp = (*i)->name();
1011 std::string::size_type idx = tmp.find(group_string);
1012 // find those which belong to this group
1013 if (idx != string::npos) {
1014 tmp = tmp.substr(idx + group_string.length());
1016 // and find the largest current number
1017 int x = atoi(tmp.c_str());
1018 if (x > maxnumber) {
1027 snprintf (buf, sizeof(buf), "%d", maxnumber);
1029 ret = this->name() + "." + route_group()->name () + "." + buf;
1035 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1039 boost::shared_ptr<Track> tr = track ();
1040 if (!tr || tr->destructive()) {
1044 boost::shared_ptr<const Playlist> pl = tr->playlist();
1051 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::edit.property_id)) {
1052 name = resolve_new_group_playlist_name(name, playlists_before_op);
1055 while (_session->playlists->by_name(name)) {
1056 name = Playlist::bump_name (name, *_session);
1059 // TODO: The prompter "new" button should be de-activated if the user
1060 // specifies a playlist name which already exists in the session.
1064 ArdourPrompter prompter (true);
1066 prompter.set_title (_("New Copy Playlist"));
1067 prompter.set_prompt (_("Name for new playlist:"));
1068 prompter.set_initial_text (name);
1069 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1070 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1071 prompter.show_all ();
1073 switch (prompter.run ()) {
1074 case Gtk::RESPONSE_ACCEPT:
1075 prompter.get_result (name);
1083 if (name.length()) {
1084 tr->use_copy_playlist ();
1085 tr->playlist()->set_name (name);
1090 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1094 boost::shared_ptr<Track> tr = track ();
1095 if (!tr || tr->destructive()) {
1099 boost::shared_ptr<const Playlist> pl = tr->playlist();
1106 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::edit.property_id)) {
1107 name = resolve_new_group_playlist_name(name,playlists_before_op);
1110 while (_session->playlists->by_name(name)) {
1111 name = Playlist::bump_name (name, *_session);
1117 ArdourPrompter prompter (true);
1119 prompter.set_title (_("New Playlist"));
1120 prompter.set_prompt (_("Name for new playlist:"));
1121 prompter.set_initial_text (name);
1122 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1123 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1125 switch (prompter.run ()) {
1126 case Gtk::RESPONSE_ACCEPT:
1127 prompter.get_result (name);
1135 if (name.length()) {
1136 tr->use_new_playlist ();
1137 tr->playlist()->set_name (name);
1142 RouteTimeAxisView::clear_playlist ()
1144 boost::shared_ptr<Track> tr = track ();
1145 if (!tr || tr->destructive()) {
1149 boost::shared_ptr<Playlist> pl = tr->playlist();
1154 _editor.clear_playlist (pl);
1158 RouteTimeAxisView::speed_changed ()
1160 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_unit, this));
1164 RouteTimeAxisView::update_diskstream_display ()
1174 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1176 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1178 /* special case: select/deselect all tracks */
1179 if (_editor.get_selection().selected (this)) {
1180 _editor.get_selection().clear_tracks ();
1182 _editor.select_all_tracks ();
1188 switch (ArdourKeyboard::selection_type (ev->state)) {
1189 case Selection::Toggle:
1190 _editor.get_selection().toggle (this);
1193 case Selection::Set:
1194 _editor.get_selection().set (this);
1197 case Selection::Extend:
1198 _editor.extend_selection_to_track (*this);
1201 case Selection::Add:
1202 _editor.get_selection().add (this);
1208 RouteTimeAxisView::set_selected_points (PointSelection& points)
1210 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1211 (*i)->set_selected_points (points);
1216 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1219 _view->set_selected_regionviews (regions);
1223 /** Add the selectable things that we have to a list.
1224 * @param results List to add things to.
1227 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1232 speed = track()->speed();
1235 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1236 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1238 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1239 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1242 /* pick up visible automation tracks */
1244 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1245 if (!(*i)->hidden()) {
1246 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1252 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1255 _view->get_inverted_selectables (sel, results);
1258 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1259 if (!(*i)->hidden()) {
1260 (*i)->get_inverted_selectables (sel, results);
1268 RouteTimeAxisView::route_group () const
1270 return _route->route_group();
1274 RouteTimeAxisView::name() const
1276 return _route->name();
1279 boost::shared_ptr<Playlist>
1280 RouteTimeAxisView::playlist () const
1282 boost::shared_ptr<Track> tr;
1284 if ((tr = track()) != 0) {
1285 return tr->playlist();
1287 return boost::shared_ptr<Playlist> ();
1292 RouteTimeAxisView::name_entry_changed ()
1296 x = name_entry.get_text ();
1298 if (x == _route->name()) {
1302 strip_whitespace_edges(x);
1304 if (x.length() == 0) {
1305 name_entry.set_text (_route->name());
1309 if (!_session->route_name_unique (x)) {
1310 ARDOUR_UI::instance()->popup_error (_("A track already exists with that name"));
1311 name_entry.set_text (_route->name());
1312 } else if (_session->route_name_internal (x)) {
1313 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1315 name_entry.set_text (_route->name());
1317 _route->set_name (x);
1321 boost::shared_ptr<Region>
1322 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1324 boost::shared_ptr<Playlist> pl = playlist ();
1327 return pl->find_next_region (pos, point, dir);
1330 return boost::shared_ptr<Region> ();
1334 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1336 boost::shared_ptr<Playlist> pl = playlist ();
1339 return pl->find_next_region_boundary (pos, dir);
1346 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1348 boost::shared_ptr<Playlist> what_we_got;
1349 boost::shared_ptr<Track> tr = track ();
1350 boost::shared_ptr<Playlist> playlist;
1353 /* route is a bus, not a track */
1357 playlist = tr->playlist();
1359 TimeSelection time (selection.time);
1360 float const speed = tr->speed();
1361 if (speed != 1.0f) {
1362 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1363 (*i).start = session_frame_to_track_frame((*i).start, speed);
1364 (*i).end = session_frame_to_track_frame((*i).end, speed);
1368 playlist->clear_changes ();
1369 playlist->clear_owned_changes ();
1373 if ((what_we_got = playlist->cut (time)) != 0) {
1374 _editor.get_cut_buffer().add (what_we_got);
1376 vector<Command*> cmds;
1377 playlist->rdiff (cmds);
1378 _session->add_commands (cmds);
1380 _session->add_command (new StatefulDiffCommand (playlist));
1384 if ((what_we_got = playlist->copy (time)) != 0) {
1385 _editor.get_cut_buffer().add (what_we_got);
1390 if ((what_we_got = playlist->cut (time)) != 0) {
1392 vector<Command*> cmds;
1393 playlist->rdiff (cmds);
1394 _session->add_commands (cmds);
1395 _session->add_command (new StatefulDiffCommand (playlist));
1396 what_we_got->release ();
1403 RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
1409 boost::shared_ptr<Playlist> pl = playlist ();
1410 PlaylistSelection::iterator p;
1412 for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
1414 if (p == selection.playlists.end()) {
1418 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1420 if (track()->speed() != 1.0f) {
1421 pos = session_frame_to_track_frame (pos, track()->speed());
1422 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1425 pl->clear_changes ();
1426 pl->paste (*p, pos, times);
1427 _session->add_command (new StatefulDiffCommand (pl));
1433 struct PlaylistSorter {
1434 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1435 return a->sort_id() < b->sort_id();
1440 RouteTimeAxisView::build_playlist_menu ()
1442 using namespace Menu_Helpers;
1448 delete playlist_action_menu;
1449 playlist_action_menu = new Menu;
1450 playlist_action_menu->set_name ("ArdourContextMenu");
1452 MenuList& playlist_items = playlist_action_menu->items();
1453 playlist_action_menu->set_name ("ArdourContextMenu");
1454 playlist_items.clear();
1456 vector<boost::shared_ptr<Playlist> > playlists, playlists_tr;
1457 boost::shared_ptr<Track> tr = track();
1458 RadioMenuItem::Group playlist_group;
1460 _session->playlists->get (playlists);
1462 /* find the playlists for this diskstream */
1463 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
1464 if (((*i)->get_orig_diskstream_id() == tr->diskstream_id()) || (tr->playlist()->id() == (*i)->id())) {
1465 playlists_tr.push_back(*i);
1469 /* sort the playlists */
1471 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1473 /* add the playlists to the menu */
1474 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1475 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1476 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1477 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1479 if (tr->playlist()->id() == (*i)->id()) {
1485 playlist_items.push_back (SeparatorElem());
1486 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1487 playlist_items.push_back (SeparatorElem());
1489 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::edit.property_id)) {
1490 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1491 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1494 // Use a label which tells the user what is happening
1495 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1496 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1500 playlist_items.push_back (SeparatorElem());
1501 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1502 playlist_items.push_back (SeparatorElem());
1504 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1508 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1510 assert (is_track());
1512 // exit if we were triggered by deactivating the old playlist
1513 if (!item->get_active()) {
1517 boost::shared_ptr<Playlist> pl (wpl.lock());
1523 boost::shared_ptr<AudioPlaylist> apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl);
1526 if (track()->playlist() == apl) {
1527 // exit when use_playlist is called by the creation of the playlist menu
1528 // or the playlist choice is unchanged
1531 track()->use_playlist (apl);
1533 RouteGroup* rg = route_group();
1535 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::edit.property_id)) {
1536 std::string group_string = "." + rg->name() + ".";
1538 std::string take_name = apl->name();
1539 std::string::size_type idx = take_name.find(group_string);
1541 if (idx == std::string::npos)
1544 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1546 boost::shared_ptr<RouteList> rl (rg->route_list());
1548 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1549 if ( (*i) == this->route()) {
1553 std::string playlist_name = (*i)->name()+group_string+take_name;
1555 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1560 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1562 // No playlist for this track for this take yet, make it
1563 track->use_new_playlist();
1564 track->playlist()->set_name(playlist_name);
1566 track->use_playlist(ipl);
1574 RouteTimeAxisView::show_playlist_selector ()
1576 _editor.playlist_selector().show_for (this);
1580 RouteTimeAxisView::map_frozen ()
1586 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1588 switch (track()->freeze_state()) {
1590 playlist_button.set_sensitive (false);
1591 rec_enable_button->set_sensitive (false);
1594 playlist_button.set_sensitive (true);
1595 rec_enable_button->set_sensitive (true);
1601 RouteTimeAxisView::color_handler ()
1603 //case cTimeStretchOutline:
1604 if (timestretch_rect) {
1605 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
1607 //case cTimeStretchFill:
1608 if (timestretch_rect) {
1609 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
1615 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1616 * Will add track if necessary.
1619 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1621 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1622 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1625 /* it doesn't exist yet, so we don't care about the button state: just add it */
1626 create_automation_child (param, true);
1629 bool yn = menu->get_active();
1630 if (track->set_visibility (menu->get_active()) && yn) {
1632 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1633 will have done that for us.
1637 _route->gui_changed (X_("track_height"), (void *) 0); /* EMIT_SIGNAL */
1644 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1646 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1652 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1654 if (menu && !_hidden) {
1655 ignore_toggle = true;
1656 menu->set_active (false);
1657 ignore_toggle = false;
1660 if (_route && !no_redraw) {
1661 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1667 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1669 if (apply_to_selection) {
1670 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1674 /* Show our automation */
1676 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1677 i->second->set_visibility (true);
1679 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1682 menu->set_active(true);
1687 /* Show processor automation */
1689 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1690 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1691 if ((*ii)->view == 0) {
1692 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1695 (*ii)->menu_item->set_active (true);
1703 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1708 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1710 if (apply_to_selection) {
1711 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1715 /* Show our automation */
1717 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1718 if (i->second->has_automation()) {
1719 i->second->set_visibility (true);
1721 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1723 menu->set_active(true);
1728 /* Show processor automation */
1730 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1731 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1732 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1733 (*ii)->menu_item->set_active (true);
1740 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1745 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1747 if (apply_to_selection) {
1748 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1752 /* Hide our automation */
1754 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1755 i->second->set_visibility (false);
1757 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1760 menu->set_active (false);
1764 /* Hide processor automation */
1766 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1767 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1768 (*ii)->menu_item->set_active (false);
1773 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1779 RouteTimeAxisView::region_view_added (RegionView* rv)
1781 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1782 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1783 boost::shared_ptr<AutomationTimeAxisView> atv;
1785 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1790 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1791 (*i)->add_ghost(rv);
1795 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1797 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1803 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1805 parent.remove_processor_automation_node (this);
1809 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1812 remove_child (pan->view);
1816 RouteTimeAxisView::ProcessorAutomationNode*
1817 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1819 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1821 if ((*i)->processor == processor) {
1823 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1824 if ((*ii)->what == what) {
1835 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1838 ProcessorAutomationNode* pan;
1840 if ((pan = find_processor_automation_node (processor, what)) == 0) {
1841 /* session state may never have been saved with new plugin */
1842 error << _("programming error: ")
1843 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
1844 processor->name(), what.type(), (int) what.channel(), what.id() )
1854 boost::shared_ptr<AutomationControl> control
1855 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
1857 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
1858 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
1859 _editor, *this, false, parent_canvas,
1860 processor->describe_parameter (what), processor->name()));
1862 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
1864 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
1867 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
1872 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor> i)
1875 pan->menu_item->set_active (false);
1879 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1884 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
1886 boost::shared_ptr<Processor> processor (p.lock ());
1892 set<Evoral::Parameter> existing;
1894 processor->what_has_data (existing);
1896 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
1898 Evoral::Parameter param (*i);
1899 boost::shared_ptr<AutomationLine> al;
1901 if ((al = find_processor_automation_curve (processor, param)) != 0) {
1904 add_processor_automation_curve (processor, param);
1910 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
1912 using namespace Menu_Helpers;
1919 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
1921 _automation_tracks[param] = track;
1923 if ((node = track->get_state_node()) != 0) {
1924 if ((prop = node->property ("shown")) != 0) {
1925 /* existing state overrides "show" argument */
1926 show = string_is_affirmative (prop->value());
1930 track->set_visibility (show);
1933 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1936 if (!EventTypeMap::instance().is_midi_parameter(param)) {
1937 /* MIDI-related parameters are always in the menu, there's no
1938 reason to rebuild the menu just because we added a automation
1939 lane for one of them. But if we add a non-MIDI automation
1940 lane, then we need to invalidate the display menu.
1942 delete display_menu;
1948 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
1950 boost::shared_ptr<Processor> processor (p.lock ());
1952 if (!processor || !processor->display_to_user ()) {
1956 /* we use this override to veto the Amp processor from the plugin menu,
1957 as its automation lane can be accessed using the special "Fader" menu
1961 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
1965 using namespace Menu_Helpers;
1966 ProcessorAutomationInfo *rai;
1967 list<ProcessorAutomationInfo*>::iterator x;
1969 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
1971 if (automatable.empty()) {
1975 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
1976 if ((*x)->processor == processor) {
1981 if (x == processor_automation.end()) {
1983 rai = new ProcessorAutomationInfo (processor);
1984 processor_automation.push_back (rai);
1992 /* any older menu was deleted at the top of processors_changed()
1993 when we cleared the subplugin menu.
1996 rai->menu = manage (new Menu);
1997 MenuList& items = rai->menu->items();
1998 rai->menu->set_name ("ArdourContextMenu");
2002 std::set<Evoral::Parameter> has_visible_automation;
2003 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2005 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2007 ProcessorAutomationNode* pan;
2008 CheckMenuItem* mitem;
2010 string name = processor->describe_parameter (*i);
2012 items.push_back (CheckMenuElem (name));
2013 mitem = dynamic_cast<CheckMenuItem*> (&items.back());
2015 _subplugin_menu_map[*i] = mitem;
2017 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2018 mitem->set_active(true);
2021 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2025 pan = new ProcessorAutomationNode (*i, mitem, *this);
2027 rai->lines.push_back (pan);
2031 pan->menu_item = mitem;
2035 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2038 /* add the menu for this processor, because the subplugin
2039 menu is always cleared at the top of processors_changed().
2040 this is the result of some poor design in gtkmm and/or
2044 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2049 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2050 RouteTimeAxisView::ProcessorAutomationNode* pan)
2052 bool showit = pan->menu_item->get_active();
2053 bool redraw = false;
2055 if (pan->view == 0 && showit) {
2056 add_processor_automation_curve (rai->processor, pan->what);
2060 if (pan->view && showit != pan->view->marked_for_display()) {
2061 pan->view->set_visibility (showit);
2065 if (redraw && !no_redraw) {
2066 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
2071 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2073 if (c.type == RouteProcessorChange::MeterPointChange) {
2074 /* nothing to do if only the meter point has changed */
2078 using namespace Menu_Helpers;
2080 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2081 (*i)->valid = false;
2084 _subplugin_menu_map.clear ();
2085 subplugin_menu.items().clear ();
2087 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
2088 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
2090 bool deleted_processor_automation = false;
2092 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2094 list<ProcessorAutomationInfo*>::iterator tmp;
2102 processor_automation.erase (i);
2103 deleted_processor_automation = true;
2110 if (deleted_processor_automation && !no_redraw) {
2111 _route->gui_changed ("track_height", this);
2115 boost::shared_ptr<AutomationLine>
2116 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2118 ProcessorAutomationNode* pan;
2120 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2126 return boost::shared_ptr<AutomationLine>();
2130 RouteTimeAxisView::reset_processor_automation_curves ()
2132 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2138 RouteTimeAxisView::update_rec_display ()
2140 RouteUI::update_rec_display ();
2141 name_entry.set_sensitive (!_route->record_enabled());
2145 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2147 if (apply_to_selection) {
2148 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2152 _view->set_layer_display (d);
2156 xml_node->add_property (N_("layer-display"), enum_2_string (d));
2161 RouteTimeAxisView::layer_display () const
2164 return _view->layer_display ();
2167 /* we don't know, since we don't have a _view, so just return something */
2173 boost::shared_ptr<AutomationTimeAxisView>
2174 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2176 AutomationTracks::iterator i = _automation_tracks.find(param);
2177 if (i != _automation_tracks.end()) {
2180 return boost::shared_ptr<AutomationTimeAxisView>();
2185 RouteTimeAxisView::fast_update ()
2187 gm.get_level_meter().update_meters ();
2191 RouteTimeAxisView::hide_meter ()
2194 gm.get_level_meter().hide_meters ();
2198 RouteTimeAxisView::show_meter ()
2204 RouteTimeAxisView::reset_meter ()
2206 if (Config->get_show_track_meters()) {
2207 gm.get_level_meter().setup_meters (height-5);
2214 RouteTimeAxisView::clear_meter ()
2216 gm.get_level_meter().clear_meters ();
2220 RouteTimeAxisView::meter_changed ()
2222 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2227 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2233 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2235 using namespace Menu_Helpers;
2237 if (!_underlay_streams.empty()) {
2238 MenuList& parent_items = parent_menu->items();
2239 Menu* gs_menu = manage (new Menu);
2240 gs_menu->set_name ("ArdourContextMenu");
2241 MenuList& gs_items = gs_menu->items();
2243 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2245 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2246 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2247 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2253 RouteTimeAxisView::set_underlay_state()
2255 if (!underlay_xml_node) {
2259 XMLNodeList nlist = underlay_xml_node->children();
2260 XMLNodeConstIterator niter;
2261 XMLNode *child_node;
2263 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2264 child_node = *niter;
2266 if (child_node->name() != "Underlay") {
2270 XMLProperty* prop = child_node->property ("id");
2272 PBD::ID id (prop->value());
2274 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2277 add_underlay(v->view(), false);
2286 RouteTimeAxisView::add_underlay (StreamView* v, bool update_xml)
2292 RouteTimeAxisView& other = v->trackview();
2294 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2295 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2296 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2300 _underlay_streams.push_back(v);
2301 other._underlay_mirrors.push_back(this);
2303 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2306 if (!underlay_xml_node) {
2308 underlay_xml_node = xml_node->add_child("Underlays");
2311 XMLNode* node = underlay_xml_node->add_child("Underlay");
2312 XMLProperty* prop = node->add_property("id");
2313 prop->set_value(v->trackview().route()->id().to_s());
2319 RouteTimeAxisView::remove_underlay (StreamView* v)
2325 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2326 RouteTimeAxisView& other = v->trackview();
2328 if (it != _underlay_streams.end()) {
2329 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2331 if (gm == other._underlay_mirrors.end()) {
2332 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2336 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2338 _underlay_streams.erase(it);
2339 other._underlay_mirrors.erase(gm);
2341 if (underlay_xml_node) {
2342 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2348 RouteTimeAxisView::set_button_names ()
2350 rec_enable_button_label.set_text (_("r"));
2352 if (_route && _route->solo_safe()) {
2353 solo_button_label.set_text (X_("!"));
2355 if (Config->get_solo_control_is_listen_control()) {
2356 switch (Config->get_listen_position()) {
2357 case AfterFaderListen:
2358 solo_button_label.set_text (_("A"));
2360 case PreFaderListen:
2361 solo_button_label.set_text (_("P"));
2365 solo_button_label.set_text (_("s"));
2368 mute_button_label.set_text (_("m"));
2372 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2374 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2375 if (i != _main_automation_menu_map.end()) {
2379 i = _subplugin_menu_map.find (param);
2380 if (i != _subplugin_menu_map.end()) {
2388 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2390 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2392 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2396 gain_track.reset (new AutomationTimeAxisView (_session,
2397 _route, _route->amp(), c, param,
2402 _route->amp()->describe_parameter(param)));
2405 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2408 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2412 void add_region_to_list (RegionView* rv, Playlist::RegionList* l)
2414 l->push_back (rv->region());
2418 RouteTimeAxisView::combine_regions ()
2420 /* as of may 2011, we do not offer uncombine for MIDI tracks
2423 if (!is_audio_track()) {
2431 Playlist::RegionList selected_regions;
2432 boost::shared_ptr<Playlist> playlist = track()->playlist();
2434 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2436 if (selected_regions.size() < 2) {
2440 playlist->clear_changes ();
2441 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2443 _session->add_command (new StatefulDiffCommand (playlist));
2444 /* make the new region be selected */
2446 return _view->find_view (compound_region);
2450 RouteTimeAxisView::uncombine_regions ()
2452 /* as of may 2011, we do not offer uncombine for MIDI tracks
2454 if (!is_audio_track()) {
2462 Playlist::RegionList selected_regions;
2463 boost::shared_ptr<Playlist> playlist = track()->playlist();
2465 /* have to grab selected regions first because the uncombine is going
2466 * to change that in the middle of the list traverse
2469 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2471 playlist->clear_changes ();
2473 for (Playlist::RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2474 playlist->uncombine (*i);
2477 _session->add_command (new StatefulDiffCommand (playlist));