2 Copyright (C) 2006 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include <sigc++/bind.h>
31 #include "pbd/error.h"
32 #include "pbd/stl_delete.h"
33 #include "pbd/whitespace.h"
34 #include "pbd/memento_command.h"
35 #include "pbd/enumwriter.h"
36 #include "pbd/stateful_diff_command.h"
38 #include <gtkmm/menu.h>
39 #include <gtkmm/menuitem.h>
40 #include <gtkmm2ext/gtk_ui.h>
41 #include <gtkmm2ext/selector.h>
42 #include <gtkmm2ext/bindable_button.h>
43 #include <gtkmm2ext/utils.h>
45 #include "ardour/amp.h"
46 #include "ardour/meter.h"
47 #include "ardour/event_type_map.h"
48 #include "ardour/processor.h"
49 #include "ardour/profile.h"
50 #include "ardour/route_group.h"
51 #include "ardour/session.h"
52 #include "ardour/session_playlists.h"
54 #include "evoral/Parameter.hpp"
56 #include "canvas/debug.h"
58 #include "ardour_ui.h"
59 #include "ardour_button.h"
61 #include "global_signals.h"
62 #include "route_time_axis.h"
63 #include "automation_time_axis.h"
65 #include "gui_thread.h"
67 #include "playlist_selector.h"
68 #include "point_selection.h"
70 #include "public_editor.h"
71 #include "region_view.h"
72 #include "rgb_macros.h"
73 #include "selection.h"
74 #include "streamview.h"
76 #include "route_group_menu.h"
78 #include "ardour/track.h"
82 using namespace ARDOUR;
84 using namespace Gtkmm2ext;
86 using namespace Editing;
90 RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, ArdourCanvas::Canvas& canvas)
93 , TimeAxisView(sess,ed,(TimeAxisView*) 0, canvas)
95 , parent_canvas (canvas)
98 , route_group_button (_("g"))
99 , playlist_button (_("p"))
100 , automation_button (_("a"))
101 , automation_action_menu (0)
102 , plugins_submenu_item (0)
103 , route_group_menu (0)
104 , playlist_action_menu (0)
106 , color_mode_menu (0)
107 , gm (sess, true, 125, 18)
108 , _ignore_set_layer_display (false)
113 RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
115 RouteUI::set_route (rt);
117 CANVAS_DEBUG_NAME (_canvas_display, string_compose ("main for %1", rt->name()));
118 CANVAS_DEBUG_NAME (selection_group, string_compose ("selections for %1", rt->name()));
119 CANVAS_DEBUG_NAME (_ghost_group, string_compose ("ghosts for %1", rt->name()));
122 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
125 gm.set_controls (_route, _route->shared_peak_meter(), _route->amp());
126 gm.get_level_meter().set_no_show_all();
127 gm.get_level_meter().setup_meters(50, meter_width);
128 gm.update_gain_sensitive ();
130 string str = gui_property ("height");
132 set_height (atoi (str));
134 set_height (preset_height (HeightNormal));
137 if (!_route->is_auditioner()) {
138 if (gui_property ("visible").empty()) {
139 set_gui_property ("visible", true);
142 set_gui_property ("visible", false);
146 update_solo_display ();
148 timestretch_rect = 0;
151 ignore_toggle = false;
153 route_group_button.set_name ("route button");
154 playlist_button.set_name ("route button");
155 automation_button.set_name ("route button");
157 route_group_button.signal_button_release_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
158 playlist_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click));
159 automation_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click));
165 switch (track()->mode()) {
167 case ARDOUR::NonLayered:
168 rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
170 case ARDOUR::Destructive:
171 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
175 controls_table.attach (*rec_enable_button, 5, 6, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
177 if (is_midi_track()) {
178 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
179 gm.set_fader_name ("MidiTrackFader");
181 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record"));
182 gm.set_fader_name ("AudioTrackFader");
185 rec_enable_button->set_sensitive (_session->writable());
187 /* set playlist button tip to the current playlist, and make it update when it changes */
188 update_playlist_tip ();
189 track()->PlaylistChanged.connect (*this, invalidator (*this), ui_bind(&RouteTimeAxisView::update_playlist_tip, this), gui_context());
192 gm.set_fader_name ("AudioBusFader");
195 Gtk::VBox *mtrbox = manage(new Gtk::VBox());
196 mtrbox->pack_start(gm.get_level_meter(), false, false, 2);
197 controls_hbox.pack_start(*mtrbox, false, false, 4);
200 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
201 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
202 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
204 controls_table.attach (*mute_button, 6, 7, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
206 if (!_route->is_master()) {
207 controls_table.attach (*solo_button, 7, 8, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
210 controls_table.attach (route_group_button, 7, 8, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
211 controls_table.attach (gm.get_gain_slider(), 0, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::AttachOptions (0), 3, 0);
213 ARDOUR_UI::instance()->set_tip(*solo_button,_("Solo"));
214 ARDOUR_UI::instance()->set_tip(*mute_button,_("Mute"));
215 ARDOUR_UI::instance()->set_tip(route_group_button, _("Route Group"));
217 if (is_midi_track()) {
218 ARDOUR_UI::instance()->set_tip(automation_button, _("MIDI Controllers and Automation"));
220 ARDOUR_UI::instance()->set_tip(automation_button, _("Automation"));
225 controls_table.attach (automation_button, 6, 7, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
227 if (is_track() && track()->mode() == ARDOUR::Normal) {
228 controls_table.attach (playlist_button, 5, 6, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
233 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
234 _route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::route_property_changed, this, _1), gui_context());
238 str = gui_property ("layer-display");
240 set_layer_display (LayerDisplay (string_2_enum (str, _view->layer_display ())));
243 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
244 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
246 /* pick up the correct freeze state */
251 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_pixel));
252 ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
254 PropertyList* plist = new PropertyList();
256 plist->add (ARDOUR::Properties::mute, true);
257 plist->add (ARDOUR::Properties::solo, true);
259 route_group_menu = new RouteGroupMenu (_session, plist);
261 gm.get_gain_slider().signal_scroll_event().connect(sigc::mem_fun(*this, &RouteTimeAxisView::controls_ebox_scroll), false);
263 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
266 RouteTimeAxisView::~RouteTimeAxisView ()
268 CatchDeletion (this);
270 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
274 delete playlist_action_menu;
275 playlist_action_menu = 0;
280 _automation_tracks.clear ();
282 delete route_group_menu;
286 RouteTimeAxisView::post_construct ()
288 /* map current state of the route */
290 update_diskstream_display ();
291 setup_processor_menu_and_curves ();
292 reset_processor_automation_curves ();
295 /** Set up the processor menu for the current set of processors, and
296 * display automation curves for any parameters which have data.
299 RouteTimeAxisView::setup_processor_menu_and_curves ()
301 _subplugin_menu_map.clear ();
302 subplugin_menu.items().clear ();
303 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
304 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
308 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
310 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
311 if (_route->route_group()) {
312 _route->route_group()->remove (_route);
318 r.push_back (route ());
320 route_group_menu->build (r);
321 route_group_menu->menu()->popup (ev->button, ev->time);
327 RouteTimeAxisView::playlist_changed ()
333 RouteTimeAxisView::label_view ()
335 string x = _route->name();
337 if (x != name_label.get_text()) {
338 name_label.set_text (x);
344 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
346 if (what_changed.contains (ARDOUR::Properties::name)) {
352 RouteTimeAxisView::take_name_changed (void *src)
360 RouteTimeAxisView::playlist_click ()
362 build_playlist_menu ();
363 conditionally_add_to_selection ();
364 playlist_action_menu->popup (1, gtk_get_current_event_time());
368 RouteTimeAxisView::automation_click ()
370 conditionally_add_to_selection ();
371 build_automation_action_menu (false);
372 automation_action_menu->popup (1, gtk_get_current_event_time());
376 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
378 using namespace Menu_Helpers;
380 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
381 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
384 detach_menu (subplugin_menu);
386 _main_automation_menu_map.clear ();
387 delete automation_action_menu;
388 automation_action_menu = new Menu;
390 MenuList& items = automation_action_menu->items();
392 automation_action_menu->set_name ("ArdourContextMenu");
394 items.push_back (MenuElem (_("Show All Automation"),
395 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
397 items.push_back (MenuElem (_("Show Existing Automation"),
398 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
400 items.push_back (MenuElem (_("Hide All Automation"),
401 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
403 /* Attach the plugin submenu. It may have previously been used elsewhere,
404 so it was detached above
407 if (!subplugin_menu.items().empty()) {
408 items.push_back (SeparatorElem ());
409 items.push_back (MenuElem (_("Processor automation"), subplugin_menu));
410 items.back().set_sensitive (!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 ()) {
477 /* We're not connecting to signal_toggled() here; in the case where these two items are
478 set to be in the `inconsistent' state, it seems that one or other will end up active
479 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
480 select the active one, no toggled signal is emitted so nothing happens.
483 _ignore_set_layer_display = true;
485 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
486 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
487 i->set_active (overlaid != 0 && stacked == 0);
488 i->set_inconsistent (overlaid != 0 && stacked != 0);
489 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
491 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
492 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
493 i->set_active (overlaid == 0 && stacked != 0);
494 i->set_inconsistent (overlaid != 0 && stacked != 0);
495 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
497 _ignore_set_layer_display = false;
499 items.push_back (MenuElem (_("Layers"), *layers_menu));
501 if (!Profile->get_sae()) {
503 Menu* alignment_menu = manage (new Menu);
504 MenuList& alignment_items = alignment_menu->items();
505 alignment_menu->set_name ("ArdourContextMenu");
507 RadioMenuItem::Group align_group;
509 /* Same verbose hacks as for the layering options above */
515 boost::shared_ptr<Track> first_track;
517 TrackSelection const & s = _editor.get_selection().tracks;
518 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
519 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
520 if (!r || !r->is_track ()) {
525 first_track = r->track();
528 switch (r->track()->alignment_choice()) {
532 switch (r->track()->alignment_style()) {
533 case ExistingMaterial:
541 case UseExistingMaterial:
557 inconsistent = false;
566 if (!inconsistent && first_track) {
568 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
569 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
570 i->set_active (automatic != 0 && existing == 0 && capture == 0);
571 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
573 switch (first_track->alignment_choice()) {
575 switch (first_track->alignment_style()) {
576 case ExistingMaterial:
577 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
580 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
588 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
589 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
590 i->set_active (existing != 0 && capture == 0 && automatic == 0);
591 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
593 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
594 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
595 i->set_active (existing == 0 && capture != 0 && automatic == 0);
596 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
598 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
604 Menu* mode_menu = manage (new Menu);
605 MenuList& mode_items = mode_menu->items ();
606 mode_menu->set_name ("ArdourContextMenu");
608 RadioMenuItem::Group mode_group;
614 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
615 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
616 if (!r || !r->is_track ()) {
620 switch (r->track()->mode()) {
633 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
634 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
635 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
636 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
637 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
639 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
640 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
641 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
642 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
643 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
645 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
646 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
647 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
648 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
649 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
651 items.push_back (MenuElem (_("Mode"), *mode_menu));
655 items.push_back (SeparatorElem());
657 build_playlist_menu ();
658 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
659 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
662 route_group_menu->detach ();
665 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
666 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
668 r.push_back (rtv->route ());
673 r.push_back (route ());
676 route_group_menu->build (r);
677 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
679 build_automation_action_menu (true);
680 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
682 items.push_back (SeparatorElem());
686 TrackSelection const & s = _editor.get_selection().tracks;
687 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
688 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
693 if (r->route()->active()) {
700 items.push_back (CheckMenuElem (_("Active")));
701 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
702 bool click_sets_active = true;
703 if (active > 0 && inactive == 0) {
704 i->set_active (true);
705 click_sets_active = false;
706 } else if (active > 0 && inactive > 0) {
707 i->set_inconsistent (true);
709 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
711 items.push_back (SeparatorElem());
712 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
713 if (!Profile->get_sae()) {
714 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
716 items.push_front (SeparatorElem());
717 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
722 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
724 if (apply_to_selection) {
725 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
730 if (!track()->can_use_mode (mode, needs_bounce)) {
736 cerr << "would bounce this one\n";
741 track()->set_mode (mode);
743 rec_enable_button->remove ();
746 case ARDOUR::NonLayered:
748 rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
749 rec_enable_button->set_text (string());
751 case ARDOUR::Destructive:
752 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
753 rec_enable_button->set_text (string());
757 rec_enable_button->show_all ();
762 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
764 TimeAxisView::show_timestretch (start, end, layers, layer);
774 /* check that the time selection was made in our route, or our route group.
775 remember that route_group() == 0 implies the route is *not* in a edit group.
778 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
779 /* this doesn't apply to us */
783 /* ignore it if our edit group is not active */
785 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
790 if (timestretch_rect == 0) {
791 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
792 timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchFill());
793 timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchOutline());
796 timestretch_rect->show ();
797 timestretch_rect->raise_to_top ();
799 double const x1 = start / _editor.get_current_zoom();
800 double const x2 = (end - 1) / _editor.get_current_zoom();
802 timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
803 x2, current_height() * (layers - layer) / layers));
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);
843 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
846 gm.get_level_meter().setup_meters (gmlen, meter_width);
848 TimeAxisView::set_height (h);
851 _view->set_height ((double) current_height());
854 if (height >= preset_height (HeightNormal)) {
858 gm.get_gain_slider().show();
860 if (!_route || _route->is_monitor()) {
865 if (rec_enable_button)
866 rec_enable_button->show();
868 route_group_button.show();
869 automation_button.show();
871 if (is_track() && track()->mode() == ARDOUR::Normal) {
872 playlist_button.show();
879 gm.get_gain_slider().hide();
881 if (!_route || _route->is_monitor()) {
886 if (rec_enable_button)
887 rec_enable_button->show();
889 route_group_button.hide ();
890 automation_button.hide ();
892 if (is_track() && track()->mode() == ARDOUR::Normal) {
893 playlist_button.hide ();
898 if (height_changed && !no_redraw) {
899 /* only emit the signal if the height really changed */
905 RouteTimeAxisView::route_color_changed ()
908 _view->apply_color (color(), StreamView::RegionColor);
913 RouteTimeAxisView::reset_samples_per_pixel ()
915 set_samples_per_pixel (_editor.get_current_zoom());
919 RouteTimeAxisView::set_samples_per_pixel (double fpp)
924 speed = track()->speed();
928 _view->set_samples_per_pixel (fpp * speed);
931 TimeAxisView::set_samples_per_pixel (fpp * speed);
935 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
937 if (!mitem->get_active()) {
938 /* this is one of the two calls made when these radio menu items change status. this one
939 is for the item that became inactive, and we want to ignore it.
944 if (apply_to_selection) {
945 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
948 track()->set_align_choice (choice);
954 RouteTimeAxisView::rename_current_playlist ()
956 ArdourPrompter prompter (true);
959 boost::shared_ptr<Track> tr = track();
960 if (!tr || tr->destructive()) {
964 boost::shared_ptr<Playlist> pl = tr->playlist();
969 prompter.set_title (_("Rename Playlist"));
970 prompter.set_prompt (_("New name for playlist:"));
971 prompter.set_initial_text (pl->name());
972 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
973 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
975 switch (prompter.run ()) {
976 case Gtk::RESPONSE_ACCEPT:
977 prompter.get_result (name);
989 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
991 std::string ret (basename);
993 std::string const group_string = "." + route_group()->name() + ".";
995 // iterate through all playlists
997 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
998 std::string tmp = (*i)->name();
1000 std::string::size_type idx = tmp.find(group_string);
1001 // find those which belong to this group
1002 if (idx != string::npos) {
1003 tmp = tmp.substr(idx + group_string.length());
1005 // and find the largest current number
1007 if (x > maxnumber) {
1016 snprintf (buf, sizeof(buf), "%d", maxnumber);
1018 ret = this->name() + "." + route_group()->name () + "." + buf;
1024 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1028 boost::shared_ptr<Track> tr = track ();
1029 if (!tr || tr->destructive()) {
1033 boost::shared_ptr<const Playlist> pl = tr->playlist();
1040 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1041 name = resolve_new_group_playlist_name(name, playlists_before_op);
1044 while (_session->playlists->by_name(name)) {
1045 name = Playlist::bump_name (name, *_session);
1048 // TODO: The prompter "new" button should be de-activated if the user
1049 // specifies a playlist name which already exists in the session.
1053 ArdourPrompter prompter (true);
1055 prompter.set_title (_("New Copy Playlist"));
1056 prompter.set_prompt (_("Name for new playlist:"));
1057 prompter.set_initial_text (name);
1058 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1059 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1060 prompter.show_all ();
1062 switch (prompter.run ()) {
1063 case Gtk::RESPONSE_ACCEPT:
1064 prompter.get_result (name);
1072 if (name.length()) {
1073 tr->use_copy_playlist ();
1074 tr->playlist()->set_name (name);
1079 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1083 boost::shared_ptr<Track> tr = track ();
1084 if (!tr || tr->destructive()) {
1088 boost::shared_ptr<const Playlist> pl = tr->playlist();
1095 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1096 name = resolve_new_group_playlist_name(name,playlists_before_op);
1099 while (_session->playlists->by_name(name)) {
1100 name = Playlist::bump_name (name, *_session);
1106 ArdourPrompter prompter (true);
1108 prompter.set_title (_("New Playlist"));
1109 prompter.set_prompt (_("Name for new playlist:"));
1110 prompter.set_initial_text (name);
1111 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1112 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1114 switch (prompter.run ()) {
1115 case Gtk::RESPONSE_ACCEPT:
1116 prompter.get_result (name);
1124 if (name.length()) {
1125 tr->use_new_playlist ();
1126 tr->playlist()->set_name (name);
1131 RouteTimeAxisView::clear_playlist ()
1133 boost::shared_ptr<Track> tr = track ();
1134 if (!tr || tr->destructive()) {
1138 boost::shared_ptr<Playlist> pl = tr->playlist();
1143 _editor.clear_playlist (pl);
1147 RouteTimeAxisView::speed_changed ()
1149 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1153 RouteTimeAxisView::update_diskstream_display ()
1163 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1165 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1167 /* special case: select/deselect all tracks */
1168 if (_editor.get_selection().selected (this)) {
1169 _editor.get_selection().clear_tracks ();
1171 _editor.select_all_tracks ();
1177 switch (ArdourKeyboard::selection_type (ev->state)) {
1178 case Selection::Toggle:
1179 _editor.get_selection().toggle (this);
1182 case Selection::Set:
1183 _editor.get_selection().set (this);
1186 case Selection::Extend:
1187 _editor.extend_selection_to_track (*this);
1190 case Selection::Add:
1191 _editor.get_selection().add (this);
1197 RouteTimeAxisView::set_selected_points (PointSelection& points)
1199 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1200 (*i)->set_selected_points (points);
1205 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1208 _view->set_selected_regionviews (regions);
1212 /** Add the selectable things that we have to a list.
1213 * @param results List to add things to.
1216 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1221 speed = track()->speed();
1224 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1225 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1227 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1228 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1231 /* pick up visible automation tracks */
1233 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1234 if (!(*i)->hidden()) {
1235 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1241 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1244 _view->get_inverted_selectables (sel, results);
1247 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1248 if (!(*i)->hidden()) {
1249 (*i)->get_inverted_selectables (sel, results);
1257 RouteTimeAxisView::route_group () const
1259 return _route->route_group();
1263 RouteTimeAxisView::name() const
1265 return _route->name();
1268 boost::shared_ptr<Playlist>
1269 RouteTimeAxisView::playlist () const
1271 boost::shared_ptr<Track> tr;
1273 if ((tr = track()) != 0) {
1274 return tr->playlist();
1276 return boost::shared_ptr<Playlist> ();
1281 RouteTimeAxisView::name_entry_changed ()
1283 TimeAxisView::name_entry_changed ();
1285 string x = name_entry->get_text ();
1287 if (x == _route->name()) {
1291 strip_whitespace_edges (x);
1293 if (x.length() == 0) {
1294 name_entry->set_text (_route->name());
1298 if (_session->route_name_internal (x)) {
1299 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1301 name_entry->grab_focus ();
1302 } else if (RouteUI::verify_new_route_name (x)) {
1303 _route->set_name (x);
1305 name_entry->grab_focus ();
1309 boost::shared_ptr<Region>
1310 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1312 boost::shared_ptr<Playlist> pl = playlist ();
1315 return pl->find_next_region (pos, point, dir);
1318 return boost::shared_ptr<Region> ();
1322 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1324 boost::shared_ptr<Playlist> pl = playlist ();
1327 return pl->find_next_region_boundary (pos, dir);
1334 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1336 boost::shared_ptr<Playlist> what_we_got;
1337 boost::shared_ptr<Track> tr = track ();
1338 boost::shared_ptr<Playlist> playlist;
1341 /* route is a bus, not a track */
1345 playlist = tr->playlist();
1347 TimeSelection time (selection.time);
1348 float const speed = tr->speed();
1349 if (speed != 1.0f) {
1350 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1351 (*i).start = session_frame_to_track_frame((*i).start, speed);
1352 (*i).end = session_frame_to_track_frame((*i).end, speed);
1356 playlist->clear_changes ();
1357 playlist->clear_owned_changes ();
1361 if (playlist->cut (time) != 0) {
1362 vector<Command*> cmds;
1363 playlist->rdiff (cmds);
1364 _session->add_commands (cmds);
1366 _session->add_command (new StatefulDiffCommand (playlist));
1371 if ((what_we_got = playlist->cut (time)) != 0) {
1372 _editor.get_cut_buffer().add (what_we_got);
1373 vector<Command*> cmds;
1374 playlist->rdiff (cmds);
1375 _session->add_commands (cmds);
1377 _session->add_command (new StatefulDiffCommand (playlist));
1381 if ((what_we_got = playlist->copy (time)) != 0) {
1382 _editor.get_cut_buffer().add (what_we_got);
1387 if ((what_we_got = playlist->cut (time)) != 0) {
1389 vector<Command*> cmds;
1390 playlist->rdiff (cmds);
1391 _session->add_commands (cmds);
1392 _session->add_command (new StatefulDiffCommand (playlist));
1393 what_we_got->release ();
1400 RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
1406 boost::shared_ptr<Playlist> pl = playlist ();
1407 PlaylistSelection::iterator p;
1409 for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
1411 if (p == selection.playlists.end()) {
1415 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1417 if (track()->speed() != 1.0f) {
1418 pos = session_frame_to_track_frame (pos, track()->speed());
1419 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1422 pl->clear_changes ();
1423 pl->paste (*p, pos, times);
1424 _session->add_command (new StatefulDiffCommand (pl));
1430 struct PlaylistSorter {
1431 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1432 return a->sort_id() < b->sort_id();
1437 RouteTimeAxisView::build_playlist_menu ()
1439 using namespace Menu_Helpers;
1445 delete playlist_action_menu;
1446 playlist_action_menu = new Menu;
1447 playlist_action_menu->set_name ("ArdourContextMenu");
1449 MenuList& playlist_items = playlist_action_menu->items();
1450 playlist_action_menu->set_name ("ArdourContextMenu");
1451 playlist_items.clear();
1453 RadioMenuItem::Group playlist_group;
1454 boost::shared_ptr<Track> tr = track ();
1456 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1458 /* sort the playlists */
1460 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1462 /* add the playlists to the menu */
1463 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1464 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1465 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1466 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1468 if (tr->playlist()->id() == (*i)->id()) {
1474 playlist_items.push_back (SeparatorElem());
1475 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1476 playlist_items.push_back (SeparatorElem());
1478 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1479 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1480 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1483 // Use a label which tells the user what is happening
1484 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1485 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1489 playlist_items.push_back (SeparatorElem());
1490 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1491 playlist_items.push_back (SeparatorElem());
1493 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1497 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1499 assert (is_track());
1501 // exit if we were triggered by deactivating the old playlist
1502 if (!item->get_active()) {
1506 boost::shared_ptr<Playlist> pl (wpl.lock());
1512 if (track()->playlist() == pl) {
1513 // exit when use_playlist is called by the creation of the playlist menu
1514 // or the playlist choice is unchanged
1518 track()->use_playlist (pl);
1520 RouteGroup* rg = route_group();
1522 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1523 std::string group_string = "." + rg->name() + ".";
1525 std::string take_name = pl->name();
1526 std::string::size_type idx = take_name.find(group_string);
1528 if (idx == std::string::npos)
1531 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1533 boost::shared_ptr<RouteList> rl (rg->route_list());
1535 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1536 if ((*i) == this->route()) {
1540 std::string playlist_name = (*i)->name()+group_string+take_name;
1542 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1547 if (track->freeze_state() == Track::Frozen) {
1548 /* Don't change playlists of frozen tracks */
1552 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1554 // No playlist for this track for this take yet, make it
1555 track->use_new_playlist();
1556 track->playlist()->set_name(playlist_name);
1558 track->use_playlist(ipl);
1565 RouteTimeAxisView::update_playlist_tip ()
1567 RouteGroup* rg = route_group ();
1568 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1569 string group_string = "." + rg->name() + ".";
1571 string take_name = track()->playlist()->name();
1572 string::size_type idx = take_name.find(group_string);
1574 if (idx != string::npos) {
1575 /* find the bit containing the take number / name */
1576 take_name = take_name.substr (idx + group_string.length());
1578 /* set the playlist button tooltip to the take name */
1579 ARDOUR_UI::instance()->set_tip (
1581 string_compose(_("Take: %1.%2"),
1582 Glib::Markup::escape_text(rg->name()),
1583 Glib::Markup::escape_text(take_name))
1590 /* set the playlist button tooltip to the playlist name */
1591 ARDOUR_UI::instance()->set_tip (playlist_button, _("Playlist") + std::string(": ") + Glib::Markup::escape_text(track()->playlist()->name()));
1596 RouteTimeAxisView::show_playlist_selector ()
1598 _editor.playlist_selector().show_for (this);
1602 RouteTimeAxisView::map_frozen ()
1608 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1610 switch (track()->freeze_state()) {
1612 playlist_button.set_sensitive (false);
1613 rec_enable_button->set_sensitive (false);
1616 playlist_button.set_sensitive (true);
1617 rec_enable_button->set_sensitive (true);
1623 RouteTimeAxisView::color_handler ()
1625 //case cTimeStretchOutline:
1626 if (timestretch_rect) {
1627 timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchOutline());
1629 //case cTimeStretchFill:
1630 if (timestretch_rect) {
1631 timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchFill());
1637 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1638 * Will add track if necessary.
1641 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1643 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1644 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1647 /* it doesn't exist yet, so we don't care about the button state: just add it */
1648 create_automation_child (param, true);
1651 bool yn = menu->get_active();
1652 bool changed = false;
1654 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1656 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1657 will have done that for us.
1660 if (changed && !no_redraw) {
1668 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1670 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1676 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1678 if (menu && !_hidden) {
1679 ignore_toggle = true;
1680 menu->set_active (false);
1681 ignore_toggle = false;
1684 if (_route && !no_redraw) {
1691 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1693 if (apply_to_selection) {
1694 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1698 /* Show our automation */
1700 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1701 i->second->set_marked_for_display (true);
1703 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1706 menu->set_active(true);
1711 /* Show processor automation */
1713 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1714 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1715 if ((*ii)->view == 0) {
1716 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1719 (*ii)->menu_item->set_active (true);
1732 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1734 if (apply_to_selection) {
1735 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1739 /* Show our automation */
1741 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1742 if (i->second->has_automation()) {
1743 i->second->set_marked_for_display (true);
1745 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1747 menu->set_active(true);
1752 /* Show processor automation */
1754 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1755 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1756 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1757 (*ii)->menu_item->set_active (true);
1769 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1771 if (apply_to_selection) {
1772 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1776 /* Hide our automation */
1778 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1779 i->second->set_marked_for_display (false);
1781 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1784 menu->set_active (false);
1788 /* Hide processor automation */
1790 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1791 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1792 (*ii)->menu_item->set_active (false);
1803 RouteTimeAxisView::region_view_added (RegionView* rv)
1805 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1806 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1807 boost::shared_ptr<AutomationTimeAxisView> atv;
1809 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1814 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1815 (*i)->add_ghost(rv);
1819 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1821 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1827 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1829 parent.remove_processor_automation_node (this);
1833 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1836 remove_child (pan->view);
1840 RouteTimeAxisView::ProcessorAutomationNode*
1841 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1843 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1845 if ((*i)->processor == processor) {
1847 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1848 if ((*ii)->what == what) {
1858 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
1860 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1863 ProcessorAutomationNode* pan;
1865 if ((pan = find_processor_automation_node (processor, what)) == 0) {
1866 /* session state may never have been saved with new plugin */
1867 error << _("programming error: ")
1868 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
1869 processor->name(), what.type(), (int) what.channel(), what.id() )
1879 boost::shared_ptr<AutomationControl> control
1880 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
1882 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
1883 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
1884 _editor, *this, false, parent_canvas,
1885 processor->describe_parameter (what), processor->name()));
1887 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
1889 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
1892 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
1897 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
1900 pan->menu_item->set_active (false);
1909 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
1911 boost::shared_ptr<Processor> processor (p.lock ());
1913 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
1914 /* The Amp processor is a special case and is dealt with separately */
1918 set<Evoral::Parameter> existing;
1920 processor->what_has_data (existing);
1922 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
1924 Evoral::Parameter param (*i);
1925 boost::shared_ptr<AutomationLine> al;
1927 if ((al = find_processor_automation_curve (processor, param)) != 0) {
1930 add_processor_automation_curve (processor, param);
1936 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
1938 using namespace Menu_Helpers;
1942 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
1944 _automation_tracks[param] = track;
1946 /* existing state overrides "show" argument */
1947 string s = track->gui_property ("visible");
1949 show = string_is_affirmative (s);
1952 /* this might or might not change the visibility status, so don't rely on it */
1953 track->set_marked_for_display (show);
1955 if (show && !no_redraw) {
1959 if (!EventTypeMap::instance().is_midi_parameter(param)) {
1960 /* MIDI-related parameters are always in the menu, there's no
1961 reason to rebuild the menu just because we added a automation
1962 lane for one of them. But if we add a non-MIDI automation
1963 lane, then we need to invalidate the display menu.
1965 delete display_menu;
1971 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
1973 boost::shared_ptr<Processor> processor (p.lock ());
1975 if (!processor || !processor->display_to_user ()) {
1979 /* we use this override to veto the Amp processor from the plugin menu,
1980 as its automation lane can be accessed using the special "Fader" menu
1984 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
1988 using namespace Menu_Helpers;
1989 ProcessorAutomationInfo *rai;
1990 list<ProcessorAutomationInfo*>::iterator x;
1992 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
1994 if (automatable.empty()) {
1998 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
1999 if ((*x)->processor == processor) {
2004 if (x == processor_automation.end()) {
2006 rai = new ProcessorAutomationInfo (processor);
2007 processor_automation.push_back (rai);
2015 /* any older menu was deleted at the top of processors_changed()
2016 when we cleared the subplugin menu.
2019 rai->menu = manage (new Menu);
2020 MenuList& items = rai->menu->items();
2021 rai->menu->set_name ("ArdourContextMenu");
2025 std::set<Evoral::Parameter> has_visible_automation;
2026 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2028 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2030 ProcessorAutomationNode* pan;
2031 Gtk::CheckMenuItem* mitem;
2033 string name = processor->describe_parameter (*i);
2035 items.push_back (CheckMenuElem (name));
2036 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2038 _subplugin_menu_map[*i] = mitem;
2040 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2041 mitem->set_active(true);
2044 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2048 pan = new ProcessorAutomationNode (*i, mitem, *this);
2050 rai->lines.push_back (pan);
2054 pan->menu_item = mitem;
2058 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2061 /* add the menu for this processor, because the subplugin
2062 menu is always cleared at the top of processors_changed().
2063 this is the result of some poor design in gtkmm and/or
2067 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2072 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2073 RouteTimeAxisView::ProcessorAutomationNode* pan)
2075 bool showit = pan->menu_item->get_active();
2076 bool redraw = false;
2078 if (pan->view == 0 && showit) {
2079 add_processor_automation_curve (rai->processor, pan->what);
2083 if (pan->view && pan->view->set_marked_for_display (showit)) {
2087 if (redraw && !no_redraw) {
2093 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2095 if (c.type == RouteProcessorChange::MeterPointChange) {
2096 /* nothing to do if only the meter point has changed */
2100 using namespace Menu_Helpers;
2102 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2103 (*i)->valid = false;
2106 setup_processor_menu_and_curves ();
2108 bool deleted_processor_automation = false;
2110 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2112 list<ProcessorAutomationInfo*>::iterator tmp;
2120 processor_automation.erase (i);
2121 deleted_processor_automation = true;
2128 if (deleted_processor_automation && !no_redraw) {
2133 boost::shared_ptr<AutomationLine>
2134 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2136 ProcessorAutomationNode* pan;
2138 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2144 return boost::shared_ptr<AutomationLine>();
2148 RouteTimeAxisView::reset_processor_automation_curves ()
2150 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2156 RouteTimeAxisView::can_edit_name () const
2158 /* we do not allow track name changes if it is record enabled
2160 return !_route->record_enabled();
2164 RouteTimeAxisView::update_rec_display ()
2166 RouteUI::update_rec_display ();
2170 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2172 if (_ignore_set_layer_display) {
2176 if (apply_to_selection) {
2177 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2181 _view->set_layer_display (d);
2184 set_gui_property (X_("layer-display"), enum_2_string (d));
2189 RouteTimeAxisView::layer_display () const
2192 return _view->layer_display ();
2195 /* we don't know, since we don't have a _view, so just return something */
2201 boost::shared_ptr<AutomationTimeAxisView>
2202 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2204 AutomationTracks::iterator i = _automation_tracks.find(param);
2205 if (i != _automation_tracks.end()) {
2208 return boost::shared_ptr<AutomationTimeAxisView>();
2213 RouteTimeAxisView::fast_update ()
2215 gm.get_level_meter().update_meters ();
2219 RouteTimeAxisView::hide_meter ()
2222 gm.get_level_meter().hide_meters ();
2226 RouteTimeAxisView::show_meter ()
2232 RouteTimeAxisView::reset_meter ()
2234 if (Config->get_show_track_meters()) {
2235 int meter_width = 3;
2236 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2239 gm.get_level_meter().setup_meters (height - 9, meter_width);
2246 RouteTimeAxisView::clear_meter ()
2248 gm.get_level_meter().clear_meters ();
2252 RouteTimeAxisView::meter_changed ()
2254 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2256 if (_route && !no_redraw) {
2259 // reset peak when meter point changes
2260 gm.reset_peak_display();
2264 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2267 if (_route && !no_redraw) {
2273 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2275 using namespace Menu_Helpers;
2277 if (!_underlay_streams.empty()) {
2278 MenuList& parent_items = parent_menu->items();
2279 Menu* gs_menu = manage (new Menu);
2280 gs_menu->set_name ("ArdourContextMenu");
2281 MenuList& gs_items = gs_menu->items();
2283 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2285 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2286 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2287 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2293 RouteTimeAxisView::set_underlay_state()
2295 if (!underlay_xml_node) {
2299 XMLNodeList nlist = underlay_xml_node->children();
2300 XMLNodeConstIterator niter;
2301 XMLNode *child_node;
2303 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2304 child_node = *niter;
2306 if (child_node->name() != "Underlay") {
2310 XMLProperty* prop = child_node->property ("id");
2312 PBD::ID id (prop->value());
2314 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2317 add_underlay(v->view(), false);
2326 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2332 RouteTimeAxisView& other = v->trackview();
2334 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2335 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2336 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2340 _underlay_streams.push_back(v);
2341 other._underlay_mirrors.push_back(this);
2343 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2345 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2347 if (!underlay_xml_node) {
2348 underlay_xml_node = xml_node->add_child("Underlays");
2351 XMLNode* node = underlay_xml_node->add_child("Underlay");
2352 XMLProperty* prop = node->add_property("id");
2353 prop->set_value(v->trackview().route()->id().to_s());
2360 RouteTimeAxisView::remove_underlay (StreamView* v)
2366 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2367 RouteTimeAxisView& other = v->trackview();
2369 if (it != _underlay_streams.end()) {
2370 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2372 if (gm == other._underlay_mirrors.end()) {
2373 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2377 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2379 _underlay_streams.erase(it);
2380 other._underlay_mirrors.erase(gm);
2382 if (underlay_xml_node) {
2383 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2389 RouteTimeAxisView::set_button_names ()
2391 if (_route && _route->solo_safe()) {
2392 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2394 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2396 if (Config->get_solo_control_is_listen_control()) {
2397 switch (Config->get_listen_position()) {
2398 case AfterFaderListen:
2399 solo_button->set_text (_("A"));
2400 ARDOUR_UI::instance()->set_tip (*solo_button, _("After-fade listen (AFL)"));
2402 case PreFaderListen:
2403 solo_button->set_text (_("P"));
2404 ARDOUR_UI::instance()->set_tip (*solo_button, _("Pre-fade listen (PFL)"));
2408 solo_button->set_text (_("s"));
2409 ARDOUR_UI::instance()->set_tip (*solo_button, _("Solo"));
2411 mute_button->set_text (_("m"));
2415 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2417 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2418 if (i != _main_automation_menu_map.end()) {
2422 i = _subplugin_menu_map.find (param);
2423 if (i != _subplugin_menu_map.end()) {
2431 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2433 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2435 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2439 gain_track.reset (new AutomationTimeAxisView (_session,
2440 _route, _route->amp(), c, param,
2445 _route->amp()->describe_parameter(param)));
2448 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2451 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2455 void add_region_to_list (RegionView* rv, RegionList* l)
2457 l->push_back (rv->region());
2461 RouteTimeAxisView::combine_regions ()
2463 /* as of may 2011, we do not offer uncombine for MIDI tracks
2466 if (!is_audio_track()) {
2474 RegionList selected_regions;
2475 boost::shared_ptr<Playlist> playlist = track()->playlist();
2477 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2479 if (selected_regions.size() < 2) {
2483 playlist->clear_changes ();
2484 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2486 _session->add_command (new StatefulDiffCommand (playlist));
2487 /* make the new region be selected */
2489 return _view->find_view (compound_region);
2493 RouteTimeAxisView::uncombine_regions ()
2495 /* as of may 2011, we do not offer uncombine for MIDI tracks
2497 if (!is_audio_track()) {
2505 RegionList selected_regions;
2506 boost::shared_ptr<Playlist> playlist = track()->playlist();
2508 /* have to grab selected regions first because the uncombine is going
2509 * to change that in the middle of the list traverse
2512 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2514 playlist->clear_changes ();
2516 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2517 playlist->uncombine (*i);
2520 _session->add_command (new StatefulDiffCommand (playlist));
2524 RouteTimeAxisView::state_id() const
2526 return string_compose ("rtav %1", _route->id().to_s());
2531 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2533 TimeAxisView::remove_child (c);
2535 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2537 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2538 if (i->second == a) {
2539 _automation_tracks.erase (i);