2 Copyright (C) 2006 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include <sigc++/bind.h>
31 #include "pbd/error.h"
32 #include "pbd/stl_delete.h"
33 #include "pbd/whitespace.h"
34 #include "pbd/memento_command.h"
35 #include "pbd/enumwriter.h"
36 #include "pbd/stateful_diff_command.h"
38 #include <gtkmm/menu.h>
39 #include <gtkmm/menuitem.h>
40 #include <gtkmm2ext/gtk_ui.h>
41 #include <gtkmm2ext/selector.h>
42 #include <gtkmm2ext/bindable_button.h>
43 #include <gtkmm2ext/utils.h>
45 #include "ardour/amp.h"
46 #include "ardour/meter.h"
47 #include "ardour/event_type_map.h"
48 #include "ardour/processor.h"
49 #include "ardour/profile.h"
50 #include "ardour/route_group.h"
51 #include "ardour/session.h"
52 #include "ardour/session_playlists.h"
54 #include "evoral/Parameter.hpp"
56 #include "canvas/debug.h"
58 #include "ardour_ui.h"
59 #include "ardour_button.h"
61 #include "global_signals.h"
62 #include "route_time_axis.h"
63 #include "automation_time_axis.h"
65 #include "gui_thread.h"
67 #include "playlist_selector.h"
68 #include "point_selection.h"
70 #include "public_editor.h"
71 #include "region_view.h"
72 #include "rgb_macros.h"
73 #include "selection.h"
74 #include "streamview.h"
76 #include "route_group_menu.h"
78 #include "ardour/track.h"
82 using namespace ARDOUR;
83 using namespace ARDOUR_UI_UTILS;
85 using namespace Gtkmm2ext;
87 using namespace Editing;
91 RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, ArdourCanvas::Canvas& canvas)
94 , TimeAxisView(sess,ed,(TimeAxisView*) 0, canvas)
96 , parent_canvas (canvas)
99 , route_group_button (_("G"))
100 , playlist_button (_("P"))
101 , automation_button (_("A"))
102 , automation_action_menu (0)
103 , plugins_submenu_item (0)
104 , route_group_menu (0)
105 , playlist_action_menu (0)
107 , color_mode_menu (0)
108 , gm (sess, true, 75, 20)
109 , _ignore_set_layer_display (false)
111 number_label.set_name("route button");
112 number_label.set_alignment(.5, .5);
113 number_label.set_fallthrough_to_parent (true);
115 sess->config.ParameterChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::parameter_changed, this, _1), gui_context());
119 RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
121 RouteUI::set_route (rt);
123 CANVAS_DEBUG_NAME (_canvas_display, string_compose ("main for %1", rt->name()));
124 CANVAS_DEBUG_NAME (selection_group, string_compose ("selections for %1", rt->name()));
125 CANVAS_DEBUG_NAME (_ghost_group, string_compose ("ghosts for %1", rt->name()));
128 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
131 gm.set_controls (_route, _route->shared_peak_meter(), _route->amp());
132 gm.get_level_meter().set_no_show_all();
133 gm.get_level_meter().setup_meters(50, meter_width);
134 gm.update_gain_sensitive ();
136 string str = gui_property ("height");
138 set_height (atoi (str));
140 set_height (preset_height (HeightNormal));
143 if (!_route->is_auditioner()) {
144 if (gui_property ("visible").empty()) {
145 set_gui_property ("visible", true);
148 set_gui_property ("visible", false);
152 update_solo_display ();
154 timestretch_rect = 0;
157 ignore_toggle = false;
159 route_group_button.set_name ("route button");
160 playlist_button.set_name ("route button");
161 automation_button.set_name ("route button");
163 route_group_button.signal_button_release_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
164 playlist_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click));
165 automation_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click));
171 switch (track()->mode()) {
173 case ARDOUR::NonLayered:
174 rec_enable_button->set_image (Glib::RefPtr<Gdk::Pixbuf>());
175 rec_enable_button->set_markup ("<span color=\"#f46f6f\">\u25CF</span>");
177 case ARDOUR::Destructive:
178 rec_enable_button->set_text (string());
179 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
183 controls_table.attach (*rec_enable_button, 0, 1, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
185 if (is_midi_track()) {
186 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
187 gm.set_fader_name ("MidiTrackFader");
189 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record"));
190 gm.set_fader_name ("AudioTrackFader");
193 rec_enable_button->set_sensitive (_session->writable());
195 /* set playlist button tip to the current playlist, and make it update when it changes */
196 update_playlist_tip ();
197 track()->PlaylistChanged.connect (*this, invalidator (*this), ui_bind(&RouteTimeAxisView::update_playlist_tip, this), gui_context());
200 gm.set_fader_name ("AudioBusFader");
203 Gtk::VBox *mtrbox = manage(new Gtk::VBox());
204 mtrbox->pack_start(gm.get_level_meter(), false, false, 2);
205 top_hbox.pack_end(*mtrbox, false, false, 4);
208 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
209 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
210 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
211 _route->track_number_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::label_view, this), gui_context());
213 controls_table.attach (*mute_button, 1, 2, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
215 if (!_route->is_master()) {
216 controls_table.attach (*solo_button, 2, 3, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
219 if (!ARDOUR::Profile->get_trx()) {
220 controls_table.attach (route_group_button, 2, 3, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
221 name_table.attach (gm.get_gain_slider(), 0, 1, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 2);
224 ARDOUR_UI::instance()->set_tip(*solo_button,_("Solo"));
225 ARDOUR_UI::instance()->set_tip(*mute_button,_("Mute"));
226 ARDOUR_UI::instance()->set_tip(route_group_button, _("Route Group"));
228 if (is_midi_track()) {
229 ARDOUR_UI::instance()->set_tip(automation_button, _("MIDI Controllers and Automation"));
231 ARDOUR_UI::instance()->set_tip(automation_button, _("Automation"));
234 update_track_number_visibility();
237 if (!ARDOUR::Profile->get_trx()) {
238 controls_table.attach (automation_button, 1, 2, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
241 if (!ARDOUR::Profile->get_trx() && is_track() && track()->mode() == ARDOUR::Normal) {
242 controls_table.attach (playlist_button, 0, 1, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
247 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
248 _route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::route_property_changed, this, _1), gui_context());
252 str = gui_property ("layer-display");
254 set_layer_display (LayerDisplay (string_2_enum (str, _view->layer_display ())));
257 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
258 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
260 /* pick up the correct freeze state */
265 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_pixel));
266 ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
268 PropertyList* plist = new PropertyList();
270 plist->add (ARDOUR::Properties::mute, true);
271 plist->add (ARDOUR::Properties::solo, true);
273 route_group_menu = new RouteGroupMenu (_session, plist);
275 gm.get_gain_slider().signal_scroll_event().connect(sigc::mem_fun(*this, &RouteTimeAxisView::controls_ebox_scroll), false);
277 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
280 RouteTimeAxisView::~RouteTimeAxisView ()
282 CatchDeletion (this);
284 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
288 delete playlist_action_menu;
289 playlist_action_menu = 0;
294 _automation_tracks.clear ();
296 delete route_group_menu;
300 RouteTimeAxisView::post_construct ()
302 /* map current state of the route */
304 update_diskstream_display ();
305 setup_processor_menu_and_curves ();
306 reset_processor_automation_curves ();
309 /** Set up the processor menu for the current set of processors, and
310 * display automation curves for any parameters which have data.
313 RouteTimeAxisView::setup_processor_menu_and_curves ()
315 _subplugin_menu_map.clear ();
316 subplugin_menu.items().clear ();
317 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
318 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
322 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
324 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
325 if (_route->route_group()) {
326 _route->route_group()->remove (_route);
332 r.push_back (route ());
334 route_group_menu->build (r);
335 route_group_menu->menu()->popup (ev->button, ev->time);
341 RouteTimeAxisView::playlist_changed ()
347 RouteTimeAxisView::label_view ()
349 string x = _route->name ();
350 if (x != name_label.get_text ()) {
351 name_label.set_text (x);
353 const int64_t track_number = _route->track_number ();
354 if (track_number == 0) {
355 number_label.set_text ("");
357 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
362 RouteTimeAxisView::update_track_number_visibility ()
364 bool show_label = _session->config.get_track_name_number();
366 if (_route && _route->is_master()) {
370 if (number_label.get_parent()) {
371 name_hbox.remove (number_label);
374 // controls_table.resize ( 2, 4 );
375 name_hbox.pack_start(number_label, false, false, 2);
376 // controls_table.attach (number_label, 3, 4, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
377 const int tnw = std::max(2u, _session->track_number_decimals()) * 8; // TODO 8 = max_width_of_digit_0_to_9()
378 number_label.set_size_request(3 + tnw, -1);
379 number_label.show ();
381 // controls_table.resize ( 2, 3 );
382 number_label.hide ();
387 RouteTimeAxisView::parameter_changed (string const & p)
389 if (p == "track-name-number") {
390 update_track_number_visibility();
395 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
397 if (what_changed.contains (ARDOUR::Properties::name)) {
403 RouteTimeAxisView::take_name_changed (void *src)
411 RouteTimeAxisView::playlist_click ()
413 build_playlist_menu ();
414 conditionally_add_to_selection ();
415 playlist_action_menu->popup (1, gtk_get_current_event_time());
419 RouteTimeAxisView::automation_click ()
421 conditionally_add_to_selection ();
422 build_automation_action_menu (false);
423 automation_action_menu->popup (1, gtk_get_current_event_time());
427 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
429 using namespace Menu_Helpers;
431 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
432 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
435 detach_menu (subplugin_menu);
437 _main_automation_menu_map.clear ();
438 delete automation_action_menu;
439 automation_action_menu = new Menu;
441 MenuList& items = automation_action_menu->items();
443 automation_action_menu->set_name ("ArdourContextMenu");
445 items.push_back (MenuElem (_("Show All Automation"),
446 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
448 items.push_back (MenuElem (_("Show Existing Automation"),
449 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
451 items.push_back (MenuElem (_("Hide All Automation"),
452 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
454 /* Attach the plugin submenu. It may have previously been used elsewhere,
455 so it was detached above
458 if (!subplugin_menu.items().empty()) {
459 items.push_back (SeparatorElem ());
460 items.push_back (MenuElem (_("Processor automation"), subplugin_menu));
461 items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);;
466 RouteTimeAxisView::build_display_menu ()
468 using namespace Menu_Helpers;
472 TimeAxisView::build_display_menu ();
474 /* now fill it with our stuff */
476 MenuList& items = display_menu->items();
477 display_menu->set_name ("ArdourContextMenu");
479 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
481 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
483 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
485 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
487 items.push_back (SeparatorElem());
490 detach_menu (*_size_menu);
493 items.push_back (MenuElem (_("Height"), *_size_menu));
495 items.push_back (SeparatorElem());
497 if (!Profile->get_sae()) {
498 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
499 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
500 items.push_back (SeparatorElem());
503 // Hook for derived classes to add type specific stuff
504 append_extra_display_menu_items ();
508 Menu* layers_menu = manage (new Menu);
509 MenuList &layers_items = layers_menu->items();
510 layers_menu->set_name("ArdourContextMenu");
512 RadioMenuItem::Group layers_group;
514 /* Find out how many overlaid/stacked tracks we have in the selection */
518 TrackSelection const & s = _editor.get_selection().tracks;
519 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
520 StreamView* v = (*i)->view ();
525 switch (v->layer_display ()) {
536 /* We're not connecting to signal_toggled() here; in the case where these two items are
537 set to be in the `inconsistent' state, it seems that one or other will end up active
538 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
539 select the active one, no toggled signal is emitted so nothing happens.
542 _ignore_set_layer_display = true;
544 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
545 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
546 i->set_active (overlaid != 0 && stacked == 0);
547 i->set_inconsistent (overlaid != 0 && stacked != 0);
548 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
550 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
551 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
552 i->set_active (overlaid == 0 && stacked != 0);
553 i->set_inconsistent (overlaid != 0 && stacked != 0);
554 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
556 _ignore_set_layer_display = false;
558 items.push_back (MenuElem (_("Layers"), *layers_menu));
560 if (!Profile->get_sae()) {
562 Menu* alignment_menu = manage (new Menu);
563 MenuList& alignment_items = alignment_menu->items();
564 alignment_menu->set_name ("ArdourContextMenu");
566 RadioMenuItem::Group align_group;
568 /* Same verbose hacks as for the layering options above */
574 boost::shared_ptr<Track> first_track;
576 TrackSelection const & s = _editor.get_selection().tracks;
577 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
578 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
579 if (!r || !r->is_track ()) {
584 first_track = r->track();
587 switch (r->track()->alignment_choice()) {
591 switch (r->track()->alignment_style()) {
592 case ExistingMaterial:
600 case UseExistingMaterial:
616 inconsistent = false;
625 if (!inconsistent && first_track) {
627 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
628 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
629 i->set_active (automatic != 0 && existing == 0 && capture == 0);
630 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
632 switch (first_track->alignment_choice()) {
634 switch (first_track->alignment_style()) {
635 case ExistingMaterial:
636 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
639 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
647 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
648 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
649 i->set_active (existing != 0 && capture == 0 && automatic == 0);
650 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
652 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
653 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
654 i->set_active (existing == 0 && capture != 0 && automatic == 0);
655 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
657 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
663 Menu* mode_menu = manage (new Menu);
664 MenuList& mode_items = mode_menu->items ();
665 mode_menu->set_name ("ArdourContextMenu");
667 RadioMenuItem::Group mode_group;
673 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
674 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
675 if (!r || !r->is_track ()) {
679 switch (r->track()->mode()) {
692 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
693 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
694 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
695 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
696 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
698 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
699 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
700 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
701 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
702 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
704 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
705 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
706 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
707 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
708 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
710 items.push_back (MenuElem (_("Mode"), *mode_menu));
714 items.push_back (SeparatorElem());
716 build_playlist_menu ();
717 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
718 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
721 route_group_menu->detach ();
724 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
725 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
727 r.push_back (rtv->route ());
732 r.push_back (route ());
735 route_group_menu->build (r);
736 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
738 build_automation_action_menu (true);
739 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
741 items.push_back (SeparatorElem());
745 TrackSelection const & s = _editor.get_selection().tracks;
746 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
747 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
752 if (r->route()->active()) {
759 items.push_back (CheckMenuElem (_("Active")));
760 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
761 bool click_sets_active = true;
762 if (active > 0 && inactive == 0) {
763 i->set_active (true);
764 click_sets_active = false;
765 } else if (active > 0 && inactive > 0) {
766 i->set_inconsistent (true);
768 i->set_sensitive(! _session->transport_rolling());
769 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
771 items.push_back (SeparatorElem());
772 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
773 if (!Profile->get_sae()) {
774 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
776 items.push_front (SeparatorElem());
777 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
782 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
784 if (apply_to_selection) {
785 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
788 bool needs_bounce = false;
790 if (!track()->can_use_mode (mode, needs_bounce)) {
796 cerr << "would bounce this one\n";
801 track()->set_mode (mode);
803 rec_enable_button->remove ();
806 case ARDOUR::NonLayered:
808 rec_enable_button->set_image (Glib::RefPtr<Gdk::Pixbuf>());
809 rec_enable_button->set_markup ("<span color=\"#f46f6f\">\u25CF</span>");
811 case ARDOUR::Destructive:
812 rec_enable_button->set_text (string());
813 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
817 rec_enable_button->show_all ();
822 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
824 TimeAxisView::show_timestretch (start, end, layers, layer);
834 /* check that the time selection was made in our route, or our route group.
835 remember that route_group() == 0 implies the route is *not* in a edit group.
838 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
839 /* this doesn't apply to us */
843 /* ignore it if our edit group is not active */
845 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
850 if (timestretch_rect == 0) {
851 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
852 timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchFill());
853 timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchOutline());
856 timestretch_rect->show ();
857 timestretch_rect->raise_to_top ();
859 double const x1 = start / _editor.get_current_zoom();
860 double const x2 = (end - 1) / _editor.get_current_zoom();
862 timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
863 x2, current_height() * (layers - layer) / layers));
867 RouteTimeAxisView::hide_timestretch ()
869 TimeAxisView::hide_timestretch ();
871 if (timestretch_rect) {
872 timestretch_rect->hide ();
877 RouteTimeAxisView::show_selection (TimeSelection& ts)
881 /* ignore it if our edit group is not active or if the selection was started
882 in some other track or route group (remember that route_group() == 0 means
883 that the track is not in an route group).
886 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
887 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
893 TimeAxisView::show_selection (ts);
897 RouteTimeAxisView::set_height (uint32_t h)
900 bool height_changed = (height == 0) || (h != height);
903 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
906 gm.get_level_meter().setup_meters (gmlen, meter_width);
908 TimeAxisView::set_height (h);
911 _view->set_height ((double) current_height());
914 if (height >= preset_height (HeightNormal)) {
918 gm.get_gain_slider().show();
920 if (!_route || _route->is_monitor()) {
925 if (rec_enable_button)
926 rec_enable_button->show();
928 route_group_button.show();
929 automation_button.show();
931 if (is_track() && track()->mode() == ARDOUR::Normal) {
932 playlist_button.show();
939 gm.get_gain_slider().hide();
941 if (!_route || _route->is_monitor()) {
946 if (rec_enable_button)
947 rec_enable_button->show();
949 route_group_button.hide ();
950 automation_button.hide ();
952 if (is_track() && track()->mode() == ARDOUR::Normal) {
953 playlist_button.hide ();
958 if (height_changed && !no_redraw) {
959 /* only emit the signal if the height really changed */
965 RouteTimeAxisView::route_color_changed ()
968 _view->apply_color (color(), StreamView::RegionColor);
971 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
975 RouteTimeAxisView::reset_samples_per_pixel ()
977 set_samples_per_pixel (_editor.get_current_zoom());
981 RouteTimeAxisView::set_samples_per_pixel (double fpp)
986 speed = track()->speed();
990 _view->set_samples_per_pixel (fpp * speed);
993 TimeAxisView::set_samples_per_pixel (fpp * speed);
997 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
999 if (!mitem->get_active()) {
1000 /* this is one of the two calls made when these radio menu items change status. this one
1001 is for the item that became inactive, and we want to ignore it.
1006 if (apply_to_selection) {
1007 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
1010 track()->set_align_choice (choice);
1016 RouteTimeAxisView::rename_current_playlist ()
1018 ArdourPrompter prompter (true);
1021 boost::shared_ptr<Track> tr = track();
1022 if (!tr || tr->destructive()) {
1026 boost::shared_ptr<Playlist> pl = tr->playlist();
1031 prompter.set_title (_("Rename Playlist"));
1032 prompter.set_prompt (_("New name for playlist:"));
1033 prompter.set_initial_text (pl->name());
1034 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1035 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1037 switch (prompter.run ()) {
1038 case Gtk::RESPONSE_ACCEPT:
1039 prompter.get_result (name);
1040 if (name.length()) {
1041 pl->set_name (name);
1051 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1053 std::string ret (basename);
1055 std::string const group_string = "." + route_group()->name() + ".";
1057 // iterate through all playlists
1059 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1060 std::string tmp = (*i)->name();
1062 std::string::size_type idx = tmp.find(group_string);
1063 // find those which belong to this group
1064 if (idx != string::npos) {
1065 tmp = tmp.substr(idx + group_string.length());
1067 // and find the largest current number
1069 if (x > maxnumber) {
1078 snprintf (buf, sizeof(buf), "%d", maxnumber);
1080 ret = this->name() + "." + route_group()->name () + "." + buf;
1086 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1090 boost::shared_ptr<Track> tr = track ();
1091 if (!tr || tr->destructive()) {
1095 boost::shared_ptr<const Playlist> pl = tr->playlist();
1102 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1103 name = resolve_new_group_playlist_name(name, playlists_before_op);
1106 while (_session->playlists->by_name(name)) {
1107 name = Playlist::bump_name (name, *_session);
1110 // TODO: The prompter "new" button should be de-activated if the user
1111 // specifies a playlist name which already exists in the session.
1115 ArdourPrompter prompter (true);
1117 prompter.set_title (_("New Copy Playlist"));
1118 prompter.set_prompt (_("Name for new playlist:"));
1119 prompter.set_initial_text (name);
1120 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1121 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1122 prompter.show_all ();
1124 switch (prompter.run ()) {
1125 case Gtk::RESPONSE_ACCEPT:
1126 prompter.get_result (name);
1134 if (name.length()) {
1135 tr->use_copy_playlist ();
1136 tr->playlist()->set_name (name);
1141 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1145 boost::shared_ptr<Track> tr = track ();
1146 if (!tr || tr->destructive()) {
1150 boost::shared_ptr<const Playlist> pl = tr->playlist();
1157 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1158 name = resolve_new_group_playlist_name(name,playlists_before_op);
1161 while (_session->playlists->by_name(name)) {
1162 name = Playlist::bump_name (name, *_session);
1168 ArdourPrompter prompter (true);
1170 prompter.set_title (_("New Playlist"));
1171 prompter.set_prompt (_("Name for new playlist:"));
1172 prompter.set_initial_text (name);
1173 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1174 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1176 switch (prompter.run ()) {
1177 case Gtk::RESPONSE_ACCEPT:
1178 prompter.get_result (name);
1186 if (name.length()) {
1187 tr->use_new_playlist ();
1188 tr->playlist()->set_name (name);
1193 RouteTimeAxisView::clear_playlist ()
1195 boost::shared_ptr<Track> tr = track ();
1196 if (!tr || tr->destructive()) {
1200 boost::shared_ptr<Playlist> pl = tr->playlist();
1205 _editor.clear_playlist (pl);
1209 RouteTimeAxisView::speed_changed ()
1211 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1215 RouteTimeAxisView::update_diskstream_display ()
1225 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1227 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1229 /* special case: select/deselect all tracks */
1230 if (_editor.get_selection().selected (this)) {
1231 _editor.get_selection().clear_tracks ();
1233 _editor.select_all_tracks ();
1239 switch (ArdourKeyboard::selection_type (ev->state)) {
1240 case Selection::Toggle:
1241 _editor.get_selection().toggle (this);
1244 case Selection::Set:
1245 _editor.get_selection().set (this);
1248 case Selection::Extend:
1249 _editor.extend_selection_to_track (*this);
1252 case Selection::Add:
1253 _editor.get_selection().add (this);
1259 RouteTimeAxisView::set_selected_points (PointSelection& points)
1261 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1262 (*i)->set_selected_points (points);
1267 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1270 _view->set_selected_regionviews (regions);
1274 /** Add the selectable things that we have to a list.
1275 * @param results List to add things to.
1278 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1283 speed = track()->speed();
1286 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1287 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1289 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1290 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1293 /* pick up visible automation tracks */
1295 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1296 if (!(*i)->hidden()) {
1297 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1303 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1306 _view->get_inverted_selectables (sel, results);
1309 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1310 if (!(*i)->hidden()) {
1311 (*i)->get_inverted_selectables (sel, results);
1319 RouteTimeAxisView::route_group () const
1321 return _route->route_group();
1325 RouteTimeAxisView::name() const
1327 return _route->name();
1330 boost::shared_ptr<Playlist>
1331 RouteTimeAxisView::playlist () const
1333 boost::shared_ptr<Track> tr;
1335 if ((tr = track()) != 0) {
1336 return tr->playlist();
1338 return boost::shared_ptr<Playlist> ();
1343 RouteTimeAxisView::name_entry_changed ()
1345 TimeAxisView::name_entry_changed ();
1347 string x = name_entry->get_text ();
1349 if (x == _route->name()) {
1353 strip_whitespace_edges (x);
1355 if (x.length() == 0) {
1356 name_entry->set_text (_route->name());
1360 if (_session->route_name_internal (x)) {
1361 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1363 name_entry->grab_focus ();
1364 } else if (RouteUI::verify_new_route_name (x)) {
1365 _route->set_name (x);
1367 name_entry->grab_focus ();
1371 boost::shared_ptr<Region>
1372 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1374 boost::shared_ptr<Playlist> pl = playlist ();
1377 return pl->find_next_region (pos, point, dir);
1380 return boost::shared_ptr<Region> ();
1384 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1386 boost::shared_ptr<Playlist> pl = playlist ();
1389 return pl->find_next_region_boundary (pos, dir);
1396 RouteTimeAxisView::fade_range (TimeSelection& selection)
1398 boost::shared_ptr<Playlist> what_we_got;
1399 boost::shared_ptr<Track> tr = track ();
1400 boost::shared_ptr<Playlist> playlist;
1403 /* route is a bus, not a track */
1407 playlist = tr->playlist();
1409 TimeSelection time (selection);
1410 float const speed = tr->speed();
1411 if (speed != 1.0f) {
1412 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1413 (*i).start = session_frame_to_track_frame((*i).start, speed);
1414 (*i).end = session_frame_to_track_frame((*i).end, speed);
1418 playlist->clear_changes ();
1419 playlist->clear_owned_changes ();
1421 playlist->fade_range (time);
1423 vector<Command*> cmds;
1424 playlist->rdiff (cmds);
1425 _session->add_commands (cmds);
1426 _session->add_command (new StatefulDiffCommand (playlist));
1431 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1433 boost::shared_ptr<Playlist> what_we_got;
1434 boost::shared_ptr<Track> tr = track ();
1435 boost::shared_ptr<Playlist> playlist;
1438 /* route is a bus, not a track */
1442 playlist = tr->playlist();
1444 TimeSelection time (selection.time);
1445 float const speed = tr->speed();
1446 if (speed != 1.0f) {
1447 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1448 (*i).start = session_frame_to_track_frame((*i).start, speed);
1449 (*i).end = session_frame_to_track_frame((*i).end, speed);
1453 playlist->clear_changes ();
1454 playlist->clear_owned_changes ();
1458 if (playlist->cut (time) != 0) {
1459 if (Config->get_edit_mode() == Ripple)
1460 playlist->ripple(time.start(), -time.length(), NULL);
1461 // no need to exclude any regions from rippling here
1463 vector<Command*> cmds;
1464 playlist->rdiff (cmds);
1465 _session->add_commands (cmds);
1467 _session->add_command (new StatefulDiffCommand (playlist));
1472 if ((what_we_got = playlist->cut (time)) != 0) {
1473 _editor.get_cut_buffer().add (what_we_got);
1474 if (Config->get_edit_mode() == Ripple)
1475 playlist->ripple(time.start(), -time.length(), NULL);
1476 // no need to exclude any regions from rippling here
1478 vector<Command*> cmds;
1479 playlist->rdiff (cmds);
1480 _session->add_commands (cmds);
1482 _session->add_command (new StatefulDiffCommand (playlist));
1486 if ((what_we_got = playlist->copy (time)) != 0) {
1487 _editor.get_cut_buffer().add (what_we_got);
1492 if ((what_we_got = playlist->cut (time)) != 0) {
1493 if (Config->get_edit_mode() == Ripple)
1494 playlist->ripple(time.start(), -time.length(), NULL);
1495 // no need to exclude any regions from rippling here
1497 vector<Command*> cmds;
1498 playlist->rdiff (cmds);
1499 _session->add_commands (cmds);
1500 _session->add_command (new StatefulDiffCommand (playlist));
1501 what_we_got->release ();
1508 RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
1514 boost::shared_ptr<Playlist> pl = playlist ();
1515 PlaylistSelection::iterator p;
1517 for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
1519 if (p == selection.playlists.end()) {
1523 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1525 if (track()->speed() != 1.0f) {
1526 pos = session_frame_to_track_frame (pos, track()->speed());
1527 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1530 pl->clear_changes ();
1531 if (Config->get_edit_mode() == Ripple) {
1532 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent_with_endspace();
1533 framecnt_t amount = extent.second - extent.first;
1534 pl->ripple(pos, amount * times, boost::shared_ptr<Region>());
1536 pl->paste (*p, pos, times);
1538 vector<Command*> cmds;
1540 _session->add_commands (cmds);
1542 _session->add_command (new StatefulDiffCommand (pl));
1548 struct PlaylistSorter {
1549 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1550 return a->sort_id() < b->sort_id();
1555 RouteTimeAxisView::build_playlist_menu ()
1557 using namespace Menu_Helpers;
1563 delete playlist_action_menu;
1564 playlist_action_menu = new Menu;
1565 playlist_action_menu->set_name ("ArdourContextMenu");
1567 MenuList& playlist_items = playlist_action_menu->items();
1568 playlist_action_menu->set_name ("ArdourContextMenu");
1569 playlist_items.clear();
1571 RadioMenuItem::Group playlist_group;
1572 boost::shared_ptr<Track> tr = track ();
1574 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1576 /* sort the playlists */
1578 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1580 /* add the playlists to the menu */
1581 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1582 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1583 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1584 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1586 if (tr->playlist()->id() == (*i)->id()) {
1592 playlist_items.push_back (SeparatorElem());
1593 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1594 playlist_items.push_back (SeparatorElem());
1596 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1597 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1598 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1601 // Use a label which tells the user what is happening
1602 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1603 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1607 playlist_items.push_back (SeparatorElem());
1608 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1609 playlist_items.push_back (SeparatorElem());
1611 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1615 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1617 assert (is_track());
1619 // exit if we were triggered by deactivating the old playlist
1620 if (!item->get_active()) {
1624 boost::shared_ptr<Playlist> pl (wpl.lock());
1630 if (track()->playlist() == pl) {
1631 // exit when use_playlist is called by the creation of the playlist menu
1632 // or the playlist choice is unchanged
1636 track()->use_playlist (pl);
1638 RouteGroup* rg = route_group();
1640 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1641 std::string group_string = "." + rg->name() + ".";
1643 std::string take_name = pl->name();
1644 std::string::size_type idx = take_name.find(group_string);
1646 if (idx == std::string::npos)
1649 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1651 boost::shared_ptr<RouteList> rl (rg->route_list());
1653 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1654 if ((*i) == this->route()) {
1658 std::string playlist_name = (*i)->name()+group_string+take_name;
1660 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1665 if (track->freeze_state() == Track::Frozen) {
1666 /* Don't change playlists of frozen tracks */
1670 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1672 // No playlist for this track for this take yet, make it
1673 track->use_new_playlist();
1674 track->playlist()->set_name(playlist_name);
1676 track->use_playlist(ipl);
1683 RouteTimeAxisView::update_playlist_tip ()
1685 RouteGroup* rg = route_group ();
1686 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1687 string group_string = "." + rg->name() + ".";
1689 string take_name = track()->playlist()->name();
1690 string::size_type idx = take_name.find(group_string);
1692 if (idx != string::npos) {
1693 /* find the bit containing the take number / name */
1694 take_name = take_name.substr (idx + group_string.length());
1696 /* set the playlist button tooltip to the take name */
1697 ARDOUR_UI::instance()->set_tip (
1699 string_compose(_("Take: %1.%2"),
1700 Glib::Markup::escape_text(rg->name()),
1701 Glib::Markup::escape_text(take_name))
1708 /* set the playlist button tooltip to the playlist name */
1709 ARDOUR_UI::instance()->set_tip (playlist_button, _("Playlist") + std::string(": ") + Glib::Markup::escape_text(track()->playlist()->name()));
1714 RouteTimeAxisView::show_playlist_selector ()
1716 _editor.playlist_selector().show_for (this);
1720 RouteTimeAxisView::map_frozen ()
1726 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1728 switch (track()->freeze_state()) {
1730 playlist_button.set_sensitive (false);
1731 rec_enable_button->set_sensitive (false);
1734 playlist_button.set_sensitive (true);
1735 rec_enable_button->set_sensitive (true);
1741 RouteTimeAxisView::color_handler ()
1743 //case cTimeStretchOutline:
1744 if (timestretch_rect) {
1745 timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchOutline());
1747 //case cTimeStretchFill:
1748 if (timestretch_rect) {
1749 timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchFill());
1755 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1756 * Will add track if necessary.
1759 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1761 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1762 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1765 /* it doesn't exist yet, so we don't care about the button state: just add it */
1766 create_automation_child (param, true);
1769 bool yn = menu->get_active();
1770 bool changed = false;
1772 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1774 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1775 will have done that for us.
1778 if (changed && !no_redraw) {
1786 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1788 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1794 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1796 if (menu && !_hidden) {
1797 ignore_toggle = true;
1798 menu->set_active (false);
1799 ignore_toggle = false;
1802 if (_route && !no_redraw) {
1809 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1811 if (apply_to_selection) {
1812 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1816 /* Show our automation */
1818 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1819 i->second->set_marked_for_display (true);
1821 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1824 menu->set_active(true);
1829 /* Show processor automation */
1831 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1832 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1833 if ((*ii)->view == 0) {
1834 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1837 (*ii)->menu_item->set_active (true);
1850 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1852 if (apply_to_selection) {
1853 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1857 /* Show our automation */
1859 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1860 if (i->second->has_automation()) {
1861 i->second->set_marked_for_display (true);
1863 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1865 menu->set_active(true);
1870 /* Show processor automation */
1872 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1873 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1874 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1875 (*ii)->menu_item->set_active (true);
1887 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1889 if (apply_to_selection) {
1890 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1894 /* Hide our automation */
1896 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1897 i->second->set_marked_for_display (false);
1899 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1902 menu->set_active (false);
1906 /* Hide processor automation */
1908 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1909 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1910 (*ii)->menu_item->set_active (false);
1921 RouteTimeAxisView::region_view_added (RegionView* rv)
1923 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1924 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1925 boost::shared_ptr<AutomationTimeAxisView> atv;
1927 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1932 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1933 (*i)->add_ghost(rv);
1937 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1939 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1945 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1947 parent.remove_processor_automation_node (this);
1951 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1954 remove_child (pan->view);
1958 RouteTimeAxisView::ProcessorAutomationNode*
1959 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1961 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1963 if ((*i)->processor == processor) {
1965 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1966 if ((*ii)->what == what) {
1976 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
1978 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1981 ProcessorAutomationNode* pan;
1983 if ((pan = find_processor_automation_node (processor, what)) == 0) {
1984 /* session state may never have been saved with new plugin */
1985 error << _("programming error: ")
1986 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
1987 processor->name(), what.type(), (int) what.channel(), what.id() )
1997 boost::shared_ptr<AutomationControl> control
1998 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
2000 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
2001 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
2002 _editor, *this, false, parent_canvas,
2003 processor->describe_parameter (what), processor->name()));
2005 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
2007 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
2010 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
2015 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
2018 pan->menu_item->set_active (false);
2027 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
2029 boost::shared_ptr<Processor> processor (p.lock ());
2031 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
2032 /* The Amp processor is a special case and is dealt with separately */
2036 set<Evoral::Parameter> existing;
2038 processor->what_has_data (existing);
2040 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
2042 Evoral::Parameter param (*i);
2043 boost::shared_ptr<AutomationLine> al;
2045 if ((al = find_processor_automation_curve (processor, param)) != 0) {
2048 add_processor_automation_curve (processor, param);
2054 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
2056 using namespace Menu_Helpers;
2060 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
2062 _automation_tracks[param] = track;
2064 /* existing state overrides "show" argument */
2065 string s = track->gui_property ("visible");
2067 show = string_is_affirmative (s);
2070 /* this might or might not change the visibility status, so don't rely on it */
2071 track->set_marked_for_display (show);
2073 if (show && !no_redraw) {
2077 if (!EventTypeMap::instance().is_midi_parameter(param)) {
2078 /* MIDI-related parameters are always in the menu, there's no
2079 reason to rebuild the menu just because we added a automation
2080 lane for one of them. But if we add a non-MIDI automation
2081 lane, then we need to invalidate the display menu.
2083 delete display_menu;
2089 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2091 boost::shared_ptr<Processor> processor (p.lock ());
2093 if (!processor || !processor->display_to_user ()) {
2097 /* we use this override to veto the Amp processor from the plugin menu,
2098 as its automation lane can be accessed using the special "Fader" menu
2102 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
2106 using namespace Menu_Helpers;
2107 ProcessorAutomationInfo *rai;
2108 list<ProcessorAutomationInfo*>::iterator x;
2110 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2112 if (automatable.empty()) {
2116 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2117 if ((*x)->processor == processor) {
2122 if (x == processor_automation.end()) {
2124 rai = new ProcessorAutomationInfo (processor);
2125 processor_automation.push_back (rai);
2133 /* any older menu was deleted at the top of processors_changed()
2134 when we cleared the subplugin menu.
2137 rai->menu = manage (new Menu);
2138 MenuList& items = rai->menu->items();
2139 rai->menu->set_name ("ArdourContextMenu");
2143 std::set<Evoral::Parameter> has_visible_automation;
2144 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2146 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2148 ProcessorAutomationNode* pan;
2149 Gtk::CheckMenuItem* mitem;
2151 string name = processor->describe_parameter (*i);
2153 items.push_back (CheckMenuElem (name));
2154 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2156 _subplugin_menu_map[*i] = mitem;
2158 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2159 mitem->set_active(true);
2162 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2166 pan = new ProcessorAutomationNode (*i, mitem, *this);
2168 rai->lines.push_back (pan);
2172 pan->menu_item = mitem;
2176 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2179 /* add the menu for this processor, because the subplugin
2180 menu is always cleared at the top of processors_changed().
2181 this is the result of some poor design in gtkmm and/or
2185 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2190 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2191 RouteTimeAxisView::ProcessorAutomationNode* pan)
2193 bool showit = pan->menu_item->get_active();
2194 bool redraw = false;
2196 if (pan->view == 0 && showit) {
2197 add_processor_automation_curve (rai->processor, pan->what);
2201 if (pan->view && pan->view->set_marked_for_display (showit)) {
2205 if (redraw && !no_redraw) {
2211 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2213 if (c.type == RouteProcessorChange::MeterPointChange) {
2214 /* nothing to do if only the meter point has changed */
2218 using namespace Menu_Helpers;
2220 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2221 (*i)->valid = false;
2224 setup_processor_menu_and_curves ();
2226 bool deleted_processor_automation = false;
2228 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2230 list<ProcessorAutomationInfo*>::iterator tmp;
2238 processor_automation.erase (i);
2239 deleted_processor_automation = true;
2246 if (deleted_processor_automation && !no_redraw) {
2251 boost::shared_ptr<AutomationLine>
2252 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2254 ProcessorAutomationNode* pan;
2256 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2262 return boost::shared_ptr<AutomationLine>();
2266 RouteTimeAxisView::reset_processor_automation_curves ()
2268 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2274 RouteTimeAxisView::can_edit_name () const
2276 /* we do not allow track name changes if it is record enabled
2278 return !_route->record_enabled();
2282 RouteTimeAxisView::blink_rec_display (bool onoff)
2284 RouteUI::blink_rec_display (onoff);
2288 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2290 if (_ignore_set_layer_display) {
2294 if (apply_to_selection) {
2295 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2299 _view->set_layer_display (d);
2302 set_gui_property (X_("layer-display"), enum_2_string (d));
2307 RouteTimeAxisView::layer_display () const
2310 return _view->layer_display ();
2313 /* we don't know, since we don't have a _view, so just return something */
2319 boost::shared_ptr<AutomationTimeAxisView>
2320 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2322 AutomationTracks::iterator i = _automation_tracks.find(param);
2323 if (i != _automation_tracks.end()) {
2326 return boost::shared_ptr<AutomationTimeAxisView>();
2331 RouteTimeAxisView::fast_update ()
2333 gm.get_level_meter().update_meters ();
2337 RouteTimeAxisView::hide_meter ()
2340 gm.get_level_meter().hide_meters ();
2344 RouteTimeAxisView::show_meter ()
2350 RouteTimeAxisView::reset_meter ()
2352 if (Config->get_show_track_meters()) {
2353 int meter_width = 3;
2354 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2357 gm.get_level_meter().setup_meters (height - 9, meter_width);
2364 RouteTimeAxisView::clear_meter ()
2366 gm.get_level_meter().clear_meters ();
2370 RouteTimeAxisView::meter_changed ()
2372 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2374 if (_route && !no_redraw) {
2377 // reset peak when meter point changes
2378 gm.reset_peak_display();
2382 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2385 if (_route && !no_redraw) {
2391 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2393 using namespace Menu_Helpers;
2395 if (!_underlay_streams.empty()) {
2396 MenuList& parent_items = parent_menu->items();
2397 Menu* gs_menu = manage (new Menu);
2398 gs_menu->set_name ("ArdourContextMenu");
2399 MenuList& gs_items = gs_menu->items();
2401 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2403 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2404 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2405 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2411 RouteTimeAxisView::set_underlay_state()
2413 if (!underlay_xml_node) {
2417 XMLNodeList nlist = underlay_xml_node->children();
2418 XMLNodeConstIterator niter;
2419 XMLNode *child_node;
2421 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2422 child_node = *niter;
2424 if (child_node->name() != "Underlay") {
2428 XMLProperty* prop = child_node->property ("id");
2430 PBD::ID id (prop->value());
2432 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2435 add_underlay(v->view(), false);
2444 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2450 RouteTimeAxisView& other = v->trackview();
2452 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2453 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2454 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2458 _underlay_streams.push_back(v);
2459 other._underlay_mirrors.push_back(this);
2461 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2463 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2465 if (!underlay_xml_node) {
2466 underlay_xml_node = xml_node->add_child("Underlays");
2469 XMLNode* node = underlay_xml_node->add_child("Underlay");
2470 XMLProperty* prop = node->add_property("id");
2471 prop->set_value(v->trackview().route()->id().to_s());
2478 RouteTimeAxisView::remove_underlay (StreamView* v)
2484 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2485 RouteTimeAxisView& other = v->trackview();
2487 if (it != _underlay_streams.end()) {
2488 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2490 if (gm == other._underlay_mirrors.end()) {
2491 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2495 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2497 _underlay_streams.erase(it);
2498 other._underlay_mirrors.erase(gm);
2500 if (underlay_xml_node) {
2501 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2507 RouteTimeAxisView::set_button_names ()
2509 if (_route && _route->solo_safe()) {
2510 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2512 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2514 if (Config->get_solo_control_is_listen_control()) {
2515 switch (Config->get_listen_position()) {
2516 case AfterFaderListen:
2517 solo_button->set_text (_("A"));
2518 ARDOUR_UI::instance()->set_tip (*solo_button, _("After-fade listen (AFL)"));
2520 case PreFaderListen:
2521 solo_button->set_text (_("P"));
2522 ARDOUR_UI::instance()->set_tip (*solo_button, _("Pre-fade listen (PFL)"));
2526 solo_button->set_text (_("S"));
2527 ARDOUR_UI::instance()->set_tip (*solo_button, _("Solo"));
2529 mute_button->set_text (_("M"));
2533 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2535 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2536 if (i != _main_automation_menu_map.end()) {
2540 i = _subplugin_menu_map.find (param);
2541 if (i != _subplugin_menu_map.end()) {
2549 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2551 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2553 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2557 gain_track.reset (new AutomationTimeAxisView (_session,
2558 _route, _route->amp(), c, param,
2563 _route->amp()->describe_parameter(param)));
2566 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2569 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2573 RouteTimeAxisView::create_mute_automation_child (const Evoral::Parameter& param, bool show)
2575 boost::shared_ptr<AutomationControl> c = _route->mute_control();
2577 error << "Route has no mute automation, unable to add automation track view." << endmsg;
2581 mute_track.reset (new AutomationTimeAxisView (_session,
2582 _route, _route, c, param,
2587 _route->describe_parameter(param)));
2590 _view->foreach_regionview (sigc::mem_fun (*mute_track.get(), &TimeAxisView::add_ghost));
2593 add_automation_child (Evoral::Parameter(MuteAutomation), mute_track, show);
2597 void add_region_to_list (RegionView* rv, RegionList* l)
2599 l->push_back (rv->region());
2603 RouteTimeAxisView::combine_regions ()
2605 /* as of may 2011, we do not offer uncombine for MIDI tracks
2608 if (!is_audio_track()) {
2616 RegionList selected_regions;
2617 boost::shared_ptr<Playlist> playlist = track()->playlist();
2619 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2621 if (selected_regions.size() < 2) {
2625 playlist->clear_changes ();
2626 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2628 _session->add_command (new StatefulDiffCommand (playlist));
2629 /* make the new region be selected */
2631 return _view->find_view (compound_region);
2635 RouteTimeAxisView::uncombine_regions ()
2637 /* as of may 2011, we do not offer uncombine for MIDI tracks
2639 if (!is_audio_track()) {
2647 RegionList selected_regions;
2648 boost::shared_ptr<Playlist> playlist = track()->playlist();
2650 /* have to grab selected regions first because the uncombine is going
2651 * to change that in the middle of the list traverse
2654 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2656 playlist->clear_changes ();
2658 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2659 playlist->uncombine (*i);
2662 _session->add_command (new StatefulDiffCommand (playlist));
2666 RouteTimeAxisView::state_id() const
2668 return string_compose ("rtav %1", _route->id().to_s());
2673 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2675 TimeAxisView::remove_child (c);
2677 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2679 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2680 if (i->second == a) {
2681 _automation_tracks.erase (i);