2 Copyright (C) 2006 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include <sigc++/bind.h>
31 #include "pbd/error.h"
32 #include "pbd/stl_delete.h"
33 #include "pbd/whitespace.h"
34 #include "pbd/memento_command.h"
35 #include "pbd/enumwriter.h"
36 #include "pbd/stateful_diff_command.h"
38 #include <gtkmm/menu.h>
39 #include <gtkmm/menuitem.h>
40 #include <gtkmm2ext/gtk_ui.h>
41 #include <gtkmm2ext/selector.h>
42 #include <gtkmm2ext/bindable_button.h>
43 #include <gtkmm2ext/utils.h>
45 #include "ardour/amp.h"
46 #include "ardour/audioplaylist.h"
47 #include "ardour/diskstream.h"
48 #include "ardour/event_type_map.h"
49 #include "ardour/ladspa_plugin.h"
50 #include "ardour/location.h"
51 #include "ardour/panner.h"
52 #include "ardour/playlist.h"
53 #include "ardour/playlist.h"
54 #include "ardour/processor.h"
55 #include "ardour/profile.h"
56 #include "ardour/region_factory.h"
57 #include "ardour/route_group.h"
58 #include "ardour/session.h"
59 #include "ardour/session_playlist.h"
60 #include "ardour/debug.h"
61 #include "ardour/utils.h"
62 #include "evoral/Parameter.hpp"
64 #include "ardour_ui.h"
66 #include "global_signals.h"
67 #include "route_time_axis.h"
68 #include "automation_time_axis.h"
69 #include "canvas_impl.h"
70 #include "crossfade_view.h"
72 #include "gui_thread.h"
74 #include "playlist_selector.h"
75 #include "point_selection.h"
77 #include "public_editor.h"
78 #include "region_view.h"
79 #include "rgb_macros.h"
80 #include "selection.h"
81 #include "simplerect.h"
82 #include "streamview.h"
84 #include "route_group_menu.h"
86 #include "ardour/track.h"
90 using namespace ARDOUR
;
92 using namespace Gtkmm2ext
;
94 using namespace Editing
;
97 Glib::RefPtr
<Gdk::Pixbuf
> RouteTimeAxisView::slider
;
100 RouteTimeAxisView::setup_slider_pix ()
102 if ((slider
= ::get_icon ("fader_belt_h")) == 0) {
103 throw failed_constructor ();
107 RouteTimeAxisView::RouteTimeAxisView (PublicEditor
& ed
, Session
* sess
, boost::shared_ptr
<Route
> rt
, Canvas
& canvas
)
110 , TimeAxisView(sess
,ed
,(TimeAxisView
*) 0, canvas
)
111 , parent_canvas (canvas
)
112 , button_table (3, 3)
113 , route_group_button (_("g"))
114 , playlist_button (_("p"))
115 , automation_button (_("a"))
116 , gm (sess
, slider
, true, 115)
118 gm
.set_controls (_route
, _route
->shared_peak_meter(), _route
->amp());
119 gm
.get_level_meter().set_no_show_all();
120 gm
.get_level_meter().setup_meters(50);
123 playlist_action_menu
= 0;
124 automation_action_menu
= 0;
125 plugins_submenu_item
= 0;
129 if (!_route
->is_hidden()) {
130 _marked_for_display
= true;
134 update_solo_display ();
136 timestretch_rect
= 0;
139 ignore_toggle
= false;
141 route_group_button
.set_name ("TrackGroupButton");
142 playlist_button
.set_name ("TrackPlaylistButton");
143 automation_button
.set_name ("TrackAutomationButton");
145 route_group_button
.unset_flags (Gtk::CAN_FOCUS
);
146 playlist_button
.unset_flags (Gtk::CAN_FOCUS
);
147 automation_button
.unset_flags (Gtk::CAN_FOCUS
);
149 route_group_button
.signal_button_release_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click
), false);
150 playlist_button
.signal_clicked().connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click
));
151 automation_button
.signal_clicked().connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click
));
157 rec_enable_button
->remove ();
159 switch (track()->mode()) {
161 case ARDOUR::NonLayered
:
162 rec_enable_button
->add (*(manage (new Image (::get_icon (X_("record_normal_red"))))));
164 case ARDOUR::Destructive
:
165 rec_enable_button
->add (*(manage (new Image (::get_icon (X_("record_tape_red"))))));
168 rec_enable_button
->show_all ();
170 controls_table
.attach (*rec_enable_button
, 5, 6, 0, 1, Gtk::FILL
|Gtk::EXPAND
, Gtk::FILL
|Gtk::EXPAND
, 0, 0);
172 if (is_midi_track()) {
173 ARDOUR_UI::instance()->set_tip(*rec_enable_button
, _("Record (Right-click for Step Edit)"));
175 ARDOUR_UI::instance()->set_tip(*rec_enable_button
, _("Record"));
178 rec_enable_button
->set_sensitive (_session
->writable());
181 controls_hbox
.pack_start(gm
.get_level_meter(), false, false);
182 _route
->meter_change
.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed
, this), gui_context());
183 _route
->input()->changed
.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::io_changed
, this, _1
, _2
), gui_context());
184 _route
->output()->changed
.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::io_changed
, this, _1
, _2
), gui_context());
186 controls_table
.attach (*mute_button
, 6, 7, 0, 1, Gtk::FILL
|Gtk::EXPAND
, Gtk::FILL
|Gtk::EXPAND
, 0, 0);
188 if (!_route
->is_master()) {
189 controls_table
.attach (*solo_button
, 7, 8, 0, 1, Gtk::FILL
|Gtk::EXPAND
, Gtk::FILL
|Gtk::EXPAND
, 0, 0);
192 controls_table
.attach (route_group_button
, 7, 8, 1, 2, Gtk::FILL
|Gtk::EXPAND
, Gtk::FILL
|Gtk::EXPAND
, 0, 0);
193 controls_table
.attach (gm
.get_gain_slider(), 0, 5, 1, 2, Gtk::SHRINK
, Gtk::SHRINK
, 0, 0);
195 ARDOUR_UI::instance()->set_tip(*solo_button
,_("Solo"));
196 ARDOUR_UI::instance()->set_tip(*mute_button
,_("Mute"));
197 ARDOUR_UI::instance()->set_tip(route_group_button
, _("Route Group"));
198 ARDOUR_UI::instance()->set_tip(playlist_button
,_("Playlist"));
199 ARDOUR_UI::instance()->set_tip(automation_button
, _("Automation"));
203 controls_table
.attach (automation_button
, 6, 7, 1, 2, Gtk::FILL
|Gtk::EXPAND
, Gtk::FILL
|Gtk::EXPAND
);
205 if (is_track() && track()->mode() == ARDOUR::Normal
) {
206 controls_table
.attach (playlist_button
, 5, 6, 1, 2, Gtk::FILL
|Gtk::EXPAND
, Gtk::FILL
|Gtk::EXPAND
);
211 _route
->processors_changed
.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::processors_changed
, this, _1
), gui_context());
212 _route
->PropertyChanged
.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::route_property_changed
, this, _1
), gui_context());
216 track()->FreezeChange
.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen
, this), gui_context());
217 track()->SpeedChanged
.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed
, this), gui_context());
219 /* pick up the correct freeze state */
224 _editor
.ZoomChanged
.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_unit
));
225 _editor
.HorizontalPositionChanged
.connect (sigc::mem_fun (*this, &RouteTimeAxisView::horizontal_position_changed
));
226 ColorsChanged
.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler
));
228 PropertyList
* plist
= new PropertyList();
230 plist
->add (ARDOUR::Properties::edit
, true);
231 plist
->add (ARDOUR::Properties::mute
, true);
232 plist
->add (ARDOUR::Properties::solo
, true);
234 route_group_menu
= new RouteGroupMenu (_session
, plist
);
236 gm
.get_gain_slider().signal_scroll_event().connect(sigc::mem_fun(*this, &RouteTimeAxisView::controls_ebox_scroll
), false);
237 gm
.get_gain_slider().set_name ("TrackGainFader");
243 RouteTimeAxisView::~RouteTimeAxisView ()
245 CatchDeletion (this);
247 for (list
<ProcessorAutomationInfo
*>::iterator i
= processor_automation
.begin(); i
!= processor_automation
.end(); ++i
) {
251 delete playlist_action_menu
;
252 playlist_action_menu
= 0;
257 _automation_tracks
.clear ();
259 delete route_group_menu
;
263 RouteTimeAxisView::post_construct ()
265 /* map current state of the route */
267 update_diskstream_display ();
269 _subplugin_menu_map
.clear ();
270 subplugin_menu
.items().clear ();
271 _route
->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu
));
272 _route
->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves
));
273 reset_processor_automation_curves ();
277 RouteTimeAxisView::route_group_click (GdkEventButton
*ev
)
279 if (Keyboard::modifier_state_equals (ev
->state
, Keyboard::PrimaryModifier
)) {
280 if (_route
->route_group()) {
281 _route
->route_group()->remove (_route
);
287 r
.push_back (route ());
289 route_group_menu
->build (r
);
290 route_group_menu
->menu()->popup (ev
->button
, ev
->time
);
296 RouteTimeAxisView::playlist_changed ()
302 RouteTimeAxisView::label_view ()
304 string x
= _route
->name();
306 if (x
!= name_entry
.get_text()) {
307 name_entry
.set_text (x
);
310 if (x
!= name_label
.get_text()) {
311 name_label
.set_text (x
);
314 ARDOUR_UI::instance()->set_tip (name_entry
, x
);
318 RouteTimeAxisView::route_property_changed (const PropertyChange
& what_changed
)
320 if (what_changed
.contains (ARDOUR::Properties::name
)) {
326 RouteTimeAxisView::take_name_changed (void *src
)
334 RouteTimeAxisView::playlist_click ()
336 build_playlist_menu ();
337 conditionally_add_to_selection ();
338 playlist_action_menu
->popup (1, gtk_get_current_event_time());
342 RouteTimeAxisView::automation_click ()
344 conditionally_add_to_selection ();
345 build_automation_action_menu (false);
346 automation_action_menu
->popup (1, gtk_get_current_event_time());
350 RouteTimeAxisView::set_state (const XMLNode
& node
, int version
)
352 TimeAxisView::set_state (node
, version
);
354 XMLNodeList kids
= node
.children();
355 XMLNodeConstIterator iter
;
356 const XMLProperty
* prop
;
358 if (_view
&& (prop
= node
.property ("layer-display"))) {
359 set_layer_display (LayerDisplay (string_2_enum (prop
->value(), _view
->layer_display ())));
366 RouteTimeAxisView::build_automation_action_menu (bool for_selection
)
368 using namespace Menu_Helpers
;
370 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
371 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
374 detach_menu (subplugin_menu
);
376 _main_automation_menu_map
.clear ();
377 delete automation_action_menu
;
378 automation_action_menu
= new Menu
;
380 MenuList
& items
= automation_action_menu
->items();
382 automation_action_menu
->set_name ("ArdourContextMenu");
384 items
.push_back (MenuElem (_("Show All Automation"),
385 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation
), for_selection
)));
387 items
.push_back (MenuElem (_("Show Existing Automation"),
388 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation
), for_selection
)));
390 items
.push_back (MenuElem (_("Hide All Automation"),
391 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation
), for_selection
)));
393 items
.push_back (SeparatorElem ());
395 /* Attach the plugin submenu. It may have previously been used elsewhere,
396 so it was detached above */
398 items
.push_back (MenuElem (_("Plugins"), subplugin_menu
));
399 items
.back().set_sensitive (!subplugin_menu
.items().empty() && (!for_selection
|| _editor
.get_selection().tracks
.size() == 1));;
403 RouteTimeAxisView::build_display_menu ()
405 using namespace Menu_Helpers
;
409 TimeAxisView::build_display_menu ();
411 /* now fill it with our stuff */
413 MenuList
& items
= display_menu
->items();
414 display_menu
->set_name ("ArdourContextMenu");
416 items
.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color
)));
419 detach_menu (*_size_menu
);
422 items
.push_back (MenuElem (_("Height"), *_size_menu
));
424 items
.push_back (SeparatorElem());
426 if (!Profile
->get_sae()) {
427 items
.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog
)));
428 items
.back().set_sensitive (_editor
.get_selection().tracks
.size() <= 1);
429 items
.push_back (SeparatorElem());
432 // Hook for derived classes to add type specific stuff
433 append_extra_display_menu_items ();
437 Menu
* layers_menu
= manage (new Menu
);
438 MenuList
&layers_items
= layers_menu
->items();
439 layers_menu
->set_name("ArdourContextMenu");
441 RadioMenuItem::Group layers_group
;
443 /* Find out how many overlaid/stacked tracks we have in the selection */
447 TrackSelection
const & s
= _editor
.get_selection().tracks
;
448 for (TrackSelection::const_iterator i
= s
.begin(); i
!= s
.end(); ++i
) {
449 StreamView
* v
= (*i
)->view ();
454 switch (v
->layer_display ()) {
464 /* We're not connecting to signal_toggled() here; in the case where these two items are
465 set to be in the `inconsistent' state, it seems that one or other will end up active
466 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
467 select the active one, no toggled signal is emitted so nothing happens.
470 layers_items
.push_back (RadioMenuElem (layers_group
, _("Overlaid")));
471 RadioMenuItem
* i
= dynamic_cast<RadioMenuItem
*> (&layers_items
.back ());
472 i
->set_active (overlaid
!= 0 && stacked
== 0);
473 i
->set_inconsistent (overlaid
!= 0 && stacked
!= 0);
474 i
->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display
), Overlaid
, true));
476 layers_items
.push_back (
477 RadioMenuElem (layers_group
, _("Stacked"),
478 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display
), Stacked
, true))
481 i
= dynamic_cast<RadioMenuItem
*> (&layers_items
.back ());
482 i
->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display
), Stacked
, true));
483 i
->set_active (overlaid
== 0 && stacked
!= 0);
484 i
->set_inconsistent (overlaid
!= 0 && stacked
!= 0);
486 items
.push_back (MenuElem (_("Layers"), *layers_menu
));
488 if (!Profile
->get_sae()) {
490 Menu
* alignment_menu
= manage (new Menu
);
491 MenuList
& alignment_items
= alignment_menu
->items();
492 alignment_menu
->set_name ("ArdourContextMenu");
494 RadioMenuItem::Group align_group
;
496 /* Same verbose hacks as for the layering options above */
502 boost::shared_ptr
<Track
> first_track
;
504 TrackSelection
const & s
= _editor
.get_selection().tracks
;
505 for (TrackSelection::const_iterator i
= s
.begin(); i
!= s
.end(); ++i
) {
506 RouteTimeAxisView
* r
= dynamic_cast<RouteTimeAxisView
*> (*i
);
507 if (!r
|| !r
->is_track ()) {
512 first_track
= r
->track();
515 switch (r
->track()->alignment_choice()) {
519 switch (r
->track()->alignment_style()) {
520 case ExistingMaterial
:
528 case UseExistingMaterial
:
544 inconsistent
= false;
553 if (!inconsistent
&& first_track
) {
555 alignment_items
.push_back (RadioMenuElem (align_group
, _("Automatic (based on I/O connections)")));
556 i
= dynamic_cast<RadioMenuItem
*> (&alignment_items
.back());
557 i
->set_active (automatic
!= 0 && existing
== 0 && capture
== 0);
558 i
->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice
), i
, Automatic
, true));
560 switch (first_track
->alignment_choice()) {
562 switch (first_track
->alignment_style()) {
563 case ExistingMaterial
:
564 alignment_items
.push_back (MenuElem (_("(Currently: Existing Material)")));
567 alignment_items
.push_back (MenuElem (_("(Currently: Capture Time)")));
575 alignment_items
.push_back (RadioMenuElem (align_group
, _("Align With Existing Material")));
576 i
= dynamic_cast<RadioMenuItem
*> (&alignment_items
.back());
577 i
->set_active (existing
!= 0 && capture
== 0 && automatic
== 0);
578 i
->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice
), i
, UseExistingMaterial
, true));
580 alignment_items
.push_back (RadioMenuElem (align_group
, _("Align With Capture Time")));
581 i
= dynamic_cast<RadioMenuItem
*> (&alignment_items
.back());
582 i
->set_active (existing
== 0 && capture
!= 0 && automatic
== 0);
583 i
->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice
), i
, UseCaptureTime
, true));
585 items
.push_back (MenuElem (_("Alignment"), *alignment_menu
));
591 Menu
* mode_menu
= manage (new Menu
);
592 MenuList
& mode_items
= mode_menu
->items ();
593 mode_menu
->set_name ("ArdourContextMenu");
595 RadioMenuItem::Group mode_group
;
601 for (TrackSelection::const_iterator i
= s
.begin(); i
!= s
.end(); ++i
) {
602 RouteTimeAxisView
* r
= dynamic_cast<RouteTimeAxisView
*> (*i
);
603 if (!r
|| !r
->is_track ()) {
607 switch (r
->track()->mode()) {
620 mode_items
.push_back (RadioMenuElem (mode_group
, _("Normal Mode")));
621 i
= dynamic_cast<RadioMenuItem
*> (&mode_items
.back ());
622 i
->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode
), ARDOUR::Normal
, true));
623 i
->set_active (normal
!= 0 && tape
== 0 && non_layered
== 0);
624 i
->set_inconsistent (normal
!= 0 && (tape
!= 0 || non_layered
!= 0));
626 mode_items
.push_back (RadioMenuElem (mode_group
, _("Tape Mode")));
627 i
= dynamic_cast<RadioMenuItem
*> (&mode_items
.back ());
628 i
->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode
), ARDOUR::Destructive
, true));
629 i
->set_active (normal
== 0 && tape
!= 0 && non_layered
== 0);
630 i
->set_inconsistent (tape
!= 0 && (normal
!= 0 || non_layered
!= 0));
632 mode_items
.push_back (RadioMenuElem (mode_group
, _("Non-Layered Mode")));
633 i
= dynamic_cast<RadioMenuItem
*> (&mode_items
.back ());
634 i
->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode
), ARDOUR::NonLayered
, true));
635 i
->set_active (normal
== 0 && tape
== 0 && non_layered
!= 0);
636 i
->set_inconsistent (non_layered
!= 0 && (normal
!= 0 || tape
!= 0));
638 items
.push_back (MenuElem (_("Mode"), *mode_menu
));
641 color_mode_menu
= build_color_mode_menu();
642 if (color_mode_menu
) {
643 items
.push_back (MenuElem (_("Color Mode"), *color_mode_menu
));
646 items
.push_back (SeparatorElem());
648 build_playlist_menu ();
649 items
.push_back (MenuElem (_("Playlist"), *playlist_action_menu
));
650 items
.back().set_sensitive (_editor
.get_selection().tracks
.size() <= 1);
652 route_group_menu
->detach ();
655 for (TrackSelection::iterator i
= _editor
.get_selection().tracks
.begin(); i
!= _editor
.get_selection().tracks
.end(); ++i
) {
656 RouteTimeAxisView
* rtv
= dynamic_cast<RouteTimeAxisView
*> (*i
);
658 r
.push_back (rtv
->route ());
663 r
.push_back (route ());
666 route_group_menu
->build (r
);
667 items
.push_back (MenuElem (_("Route Group"), *route_group_menu
->menu ()));
669 build_automation_action_menu (true);
670 items
.push_back (MenuElem (_("Automation"), *automation_action_menu
));
672 items
.push_back (SeparatorElem());
677 TrackSelection
const & s
= _editor
.get_selection().tracks
;
678 for (TrackSelection::const_iterator i
= s
.begin(); i
!= s
.end(); ++i
) {
679 RouteTimeAxisView
* r
= dynamic_cast<RouteTimeAxisView
*> (*i
);
684 if (r
->route()->active()) {
691 items
.push_back (CheckMenuElem (_("Active")));
692 CheckMenuItem
* i
= dynamic_cast<CheckMenuItem
*> (&items
.back());
693 bool click_sets_active
= true;
694 if (active
> 0 && inactive
== 0) {
695 i
->set_active (true);
696 click_sets_active
= false;
697 } else if (active
> 0 && inactive
> 0) {
698 i
->set_inconsistent (true);
700 i
->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active
), click_sets_active
, true));
702 items
.push_back (SeparatorElem());
703 items
.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor
, &PublicEditor::hide_track_in_display
), this, true)));
704 if (!Profile
->get_sae()) {
705 items
.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route
), true)));
707 items
.push_front (SeparatorElem());
708 items
.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route
), true)));
713 RouteTimeAxisView::set_track_mode (TrackMode mode
, bool apply_to_selection
)
715 if (apply_to_selection
) {
716 _editor
.get_selection().tracks
.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode
, _1
, mode
, false));
721 if (!track()->can_use_mode (mode
, needs_bounce
)) {
727 cerr
<< "would bounce this one\n";
732 track()->set_mode (mode
);
734 rec_enable_button
->remove ();
737 case ARDOUR::NonLayered
:
739 rec_enable_button
->add (*(manage (new Image (::get_icon (X_("record_normal_red"))))));
741 case ARDOUR::Destructive
:
742 rec_enable_button
->add (*(manage (new Image (::get_icon (X_("record_tape_red"))))));
746 rec_enable_button
->show_all ();
751 RouteTimeAxisView::show_timestretch (framepos_t start
, framepos_t end
)
757 TimeAxisView::show_timestretch (start
, end
);
767 /* check that the time selection was made in our route, or our route group.
768 remember that route_group() == 0 implies the route is *not* in a edit group.
771 if (!(ts
.track
== this || (ts
.group
!= 0 && ts
.group
== _route
->route_group()))) {
772 /* this doesn't apply to us */
776 /* ignore it if our edit group is not active */
778 if ((ts
.track
!= this) && _route
->route_group() && !_route
->route_group()->is_active()) {
783 if (timestretch_rect
== 0) {
784 timestretch_rect
= new SimpleRect (*canvas_display ());
785 timestretch_rect
->property_x1() = 0.0;
786 timestretch_rect
->property_y1() = 0.0;
787 timestretch_rect
->property_x2() = 0.0;
788 timestretch_rect
->property_y2() = 0.0;
789 timestretch_rect
->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill
.get();
790 timestretch_rect
->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline
.get();
793 timestretch_rect
->show ();
794 timestretch_rect
->raise_to_top ();
796 x1
= start
/ _editor
.get_current_zoom();
797 x2
= (end
- 1) / _editor
.get_current_zoom();
798 y2
= current_height() - 2;
800 timestretch_rect
->property_x1() = x1
;
801 timestretch_rect
->property_y1() = 1.0;
802 timestretch_rect
->property_x2() = x2
;
803 timestretch_rect
->property_y2() = y2
;
807 RouteTimeAxisView::hide_timestretch ()
809 TimeAxisView::hide_timestretch ();
811 if (timestretch_rect
) {
812 timestretch_rect
->hide ();
817 RouteTimeAxisView::show_selection (TimeSelection
& ts
)
821 /* ignore it if our edit group is not active or if the selection was started
822 in some other track or route group (remember that route_group() == 0 means
823 that the track is not in an route group).
826 if (((ts
.track
!= this && !is_child (ts
.track
)) && _route
->route_group() && !_route
->route_group()->is_active()) ||
827 (!(ts
.track
== this || is_child (ts
.track
) || (ts
.group
!= 0 && ts
.group
== _route
->route_group())))) {
833 TimeAxisView::show_selection (ts
);
837 RouteTimeAxisView::set_height (uint32_t h
)
840 bool height_changed
= (height
== 0) || (h
!= height
);
841 gm
.get_level_meter().setup_meters (gmlen
);
843 TimeAxisView::set_height (h
);
848 _view
->set_height ((double) current_height());
852 snprintf (buf
, sizeof (buf
), "%u", height
);
853 xml_node
->add_property ("height", buf
);
855 if (height
>= preset_height (HeightNormal
)) {
859 gm
.get_gain_slider().show();
861 if (!_route
|| _route
->is_monitor()) {
866 if (rec_enable_button
)
867 rec_enable_button
->show();
869 route_group_button
.show();
870 automation_button
.show();
872 if (is_track() && track()->mode() == ARDOUR::Normal
) {
873 playlist_button
.show();
880 gm
.get_gain_slider().hide();
882 if (!_route
|| _route
->is_monitor()) {
887 if (rec_enable_button
)
888 rec_enable_button
->show();
890 route_group_button
.hide ();
891 automation_button
.hide ();
893 if (is_track() && track()->mode() == ARDOUR::Normal
) {
894 playlist_button
.hide ();
899 if (height_changed
&& !no_redraw
) {
900 /* only emit the signal if the height really changed */
901 _route
->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
906 RouteTimeAxisView::set_color (Gdk::Color
const & c
)
908 RouteUI::set_color (c
);
911 _view
->apply_color (_color
, StreamView::RegionColor
);
916 RouteTimeAxisView::reset_samples_per_unit ()
918 set_samples_per_unit (_editor
.get_current_zoom());
922 RouteTimeAxisView::horizontal_position_changed ()
925 _view
->horizontal_position_changed ();
930 RouteTimeAxisView::set_samples_per_unit (double spu
)
935 speed
= track()->speed();
939 _view
->set_samples_per_unit (spu
* speed
);
942 TimeAxisView::set_samples_per_unit (spu
* speed
);
946 RouteTimeAxisView::set_align_choice (RadioMenuItem
* mitem
, AlignChoice choice
, bool apply_to_selection
)
948 if (!mitem
->get_active()) {
949 /* this is one of the two calls made when these radio menu items change status. this one
950 is for the item that became inactive, and we want to ignore it.
955 if (apply_to_selection
) {
956 _editor
.get_selection().tracks
.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice
, _1
, mitem
, choice
, false));
959 track()->set_align_choice (choice
);
965 RouteTimeAxisView::rename_current_playlist ()
967 ArdourPrompter
prompter (true);
970 boost::shared_ptr
<Track
> tr
= track();
971 if (!tr
|| tr
->destructive()) {
975 boost::shared_ptr
<Playlist
> pl
= tr
->playlist();
980 prompter
.set_title (_("Rename Playlist"));
981 prompter
.set_prompt (_("New name for playlist:"));
982 prompter
.set_initial_text (pl
->name());
983 prompter
.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT
);
984 prompter
.set_response_sensitive (Gtk::RESPONSE_ACCEPT
, false);
986 switch (prompter
.run ()) {
987 case Gtk::RESPONSE_ACCEPT
:
988 prompter
.get_result (name
);
1000 RouteTimeAxisView::resolve_new_group_playlist_name(std::string
&basename
, vector
<boost::shared_ptr
<Playlist
> > const & playlists
)
1002 std::string
ret (basename
);
1004 std::string
const group_string
= "." + route_group()->name() + ".";
1006 // iterate through all playlists
1008 for (vector
<boost::shared_ptr
<Playlist
> >::const_iterator i
= playlists
.begin(); i
!= playlists
.end(); ++i
) {
1009 std::string tmp
= (*i
)->name();
1011 std::string::size_type idx
= tmp
.find(group_string
);
1012 // find those which belong to this group
1013 if (idx
!= string::npos
) {
1014 tmp
= tmp
.substr(idx
+ group_string
.length());
1016 // and find the largest current number
1017 int x
= atoi(tmp
.c_str());
1018 if (x
> maxnumber
) {
1027 snprintf (buf
, sizeof(buf
), "%d", maxnumber
);
1029 ret
= this->name() + "." + route_group()->name () + "." + buf
;
1035 RouteTimeAxisView::use_copy_playlist (bool prompt
, vector
<boost::shared_ptr
<Playlist
> > const & playlists_before_op
)
1039 boost::shared_ptr
<Track
> tr
= track ();
1040 if (!tr
|| tr
->destructive()) {
1044 boost::shared_ptr
<const Playlist
> pl
= tr
->playlist();
1051 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::edit
.property_id
)) {
1052 name
= resolve_new_group_playlist_name(name
, playlists_before_op
);
1055 while (_session
->playlists
->by_name(name
)) {
1056 name
= Playlist::bump_name (name
, *_session
);
1059 // TODO: The prompter "new" button should be de-activated if the user
1060 // specifies a playlist name which already exists in the session.
1064 ArdourPrompter
prompter (true);
1066 prompter
.set_title (_("New Copy Playlist"));
1067 prompter
.set_prompt (_("Name for new playlist:"));
1068 prompter
.set_initial_text (name
);
1069 prompter
.add_button (Gtk::Stock::NEW
, Gtk::RESPONSE_ACCEPT
);
1070 prompter
.set_response_sensitive (Gtk::RESPONSE_ACCEPT
, true);
1071 prompter
.show_all ();
1073 switch (prompter
.run ()) {
1074 case Gtk::RESPONSE_ACCEPT
:
1075 prompter
.get_result (name
);
1083 if (name
.length()) {
1084 tr
->use_copy_playlist ();
1085 tr
->playlist()->set_name (name
);
1090 RouteTimeAxisView::use_new_playlist (bool prompt
, vector
<boost::shared_ptr
<Playlist
> > const & playlists_before_op
)
1094 boost::shared_ptr
<Track
> tr
= track ();
1095 if (!tr
|| tr
->destructive()) {
1099 boost::shared_ptr
<const Playlist
> pl
= tr
->playlist();
1106 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::edit
.property_id
)) {
1107 name
= resolve_new_group_playlist_name(name
,playlists_before_op
);
1110 while (_session
->playlists
->by_name(name
)) {
1111 name
= Playlist::bump_name (name
, *_session
);
1117 ArdourPrompter
prompter (true);
1119 prompter
.set_title (_("New Playlist"));
1120 prompter
.set_prompt (_("Name for new playlist:"));
1121 prompter
.set_initial_text (name
);
1122 prompter
.add_button (Gtk::Stock::NEW
, Gtk::RESPONSE_ACCEPT
);
1123 prompter
.set_response_sensitive (Gtk::RESPONSE_ACCEPT
, true);
1125 switch (prompter
.run ()) {
1126 case Gtk::RESPONSE_ACCEPT
:
1127 prompter
.get_result (name
);
1135 if (name
.length()) {
1136 tr
->use_new_playlist ();
1137 tr
->playlist()->set_name (name
);
1142 RouteTimeAxisView::clear_playlist ()
1144 boost::shared_ptr
<Track
> tr
= track ();
1145 if (!tr
|| tr
->destructive()) {
1149 boost::shared_ptr
<Playlist
> pl
= tr
->playlist();
1154 _editor
.clear_playlist (pl
);
1158 RouteTimeAxisView::speed_changed ()
1160 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_unit
, this));
1164 RouteTimeAxisView::update_diskstream_display ()
1174 RouteTimeAxisView::selection_click (GdkEventButton
* ev
)
1176 if (Keyboard::modifier_state_equals (ev
->state
, (Keyboard::TertiaryModifier
|Keyboard::PrimaryModifier
))) {
1178 /* special case: select/deselect all tracks */
1179 if (_editor
.get_selection().selected (this)) {
1180 _editor
.get_selection().clear_tracks ();
1182 _editor
.select_all_tracks ();
1188 switch (ArdourKeyboard::selection_type (ev
->state
)) {
1189 case Selection::Toggle
:
1190 _editor
.get_selection().toggle (this);
1193 case Selection::Set
:
1194 _editor
.get_selection().set (this);
1197 case Selection::Extend
:
1198 _editor
.extend_selection_to_track (*this);
1201 case Selection::Add
:
1202 _editor
.get_selection().add (this);
1208 RouteTimeAxisView::set_selected_points (PointSelection
& points
)
1210 for (Children::iterator i
= children
.begin(); i
!= children
.end(); ++i
) {
1211 (*i
)->set_selected_points (points
);
1216 RouteTimeAxisView::set_selected_regionviews (RegionSelection
& regions
)
1219 _view
->set_selected_regionviews (regions
);
1223 /** Add the selectable things that we have to a list.
1224 * @param results List to add things to.
1227 RouteTimeAxisView::get_selectables (framepos_t start
, framepos_t end
, double top
, double bot
, list
<Selectable
*>& results
)
1232 speed
= track()->speed();
1235 framepos_t
const start_adjusted
= session_frame_to_track_frame(start
, speed
);
1236 framepos_t
const end_adjusted
= session_frame_to_track_frame(end
, speed
);
1238 if ((_view
&& ((top
< 0.0 && bot
< 0.0))) || touched (top
, bot
)) {
1239 _view
->get_selectables (start_adjusted
, end_adjusted
, top
, bot
, results
);
1242 /* pick up visible automation tracks */
1244 for (Children::iterator i
= children
.begin(); i
!= children
.end(); ++i
) {
1245 if (!(*i
)->hidden()) {
1246 (*i
)->get_selectables (start_adjusted
, end_adjusted
, top
, bot
, results
);
1252 RouteTimeAxisView::get_inverted_selectables (Selection
& sel
, list
<Selectable
*>& results
)
1255 _view
->get_inverted_selectables (sel
, results
);
1258 for (Children::iterator i
= children
.begin(); i
!= children
.end(); ++i
) {
1259 if (!(*i
)->hidden()) {
1260 (*i
)->get_inverted_selectables (sel
, results
);
1268 RouteTimeAxisView::route_group () const
1270 return _route
->route_group();
1274 RouteTimeAxisView::name() const
1276 return _route
->name();
1279 boost::shared_ptr
<Playlist
>
1280 RouteTimeAxisView::playlist () const
1282 boost::shared_ptr
<Track
> tr
;
1284 if ((tr
= track()) != 0) {
1285 return tr
->playlist();
1287 return boost::shared_ptr
<Playlist
> ();
1292 RouteTimeAxisView::name_entry_changed ()
1296 x
= name_entry
.get_text ();
1298 if (x
== _route
->name()) {
1302 strip_whitespace_edges(x
);
1304 if (x
.length() == 0) {
1305 name_entry
.set_text (_route
->name());
1309 if (!_session
->route_name_unique (x
)) {
1310 ARDOUR_UI::instance()->popup_error (_("A track already exists with that name"));
1311 name_entry
.set_text (_route
->name());
1312 } else if (_session
->route_name_internal (x
)) {
1313 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1315 name_entry
.set_text (_route
->name());
1317 _route
->set_name (x
);
1321 boost::shared_ptr
<Region
>
1322 RouteTimeAxisView::find_next_region (framepos_t pos
, RegionPoint point
, int32_t dir
)
1324 boost::shared_ptr
<Playlist
> pl
= playlist ();
1327 return pl
->find_next_region (pos
, point
, dir
);
1330 return boost::shared_ptr
<Region
> ();
1334 RouteTimeAxisView::find_next_region_boundary (framepos_t pos
, int32_t dir
)
1336 boost::shared_ptr
<Playlist
> pl
= playlist ();
1339 return pl
->find_next_region_boundary (pos
, dir
);
1346 RouteTimeAxisView::cut_copy_clear (Selection
& selection
, CutCopyOp op
)
1348 boost::shared_ptr
<Playlist
> what_we_got
;
1349 boost::shared_ptr
<Track
> tr
= track ();
1350 boost::shared_ptr
<Playlist
> playlist
;
1353 /* route is a bus, not a track */
1357 playlist
= tr
->playlist();
1359 TimeSelection
time (selection
.time
);
1360 float const speed
= tr
->speed();
1361 if (speed
!= 1.0f
) {
1362 for (TimeSelection::iterator i
= time
.begin(); i
!= time
.end(); ++i
) {
1363 (*i
).start
= session_frame_to_track_frame((*i
).start
, speed
);
1364 (*i
).end
= session_frame_to_track_frame((*i
).end
, speed
);
1368 playlist
->clear_changes ();
1369 playlist
->clear_owned_changes ();
1373 if (playlist
->cut (time
) != 0) {
1374 vector
<Command
*> cmds
;
1375 playlist
->rdiff (cmds
);
1376 _session
->add_commands (cmds
);
1378 _session
->add_command (new StatefulDiffCommand (playlist
));
1383 if ((what_we_got
= playlist
->cut (time
)) != 0) {
1384 _editor
.get_cut_buffer().add (what_we_got
);
1385 vector
<Command
*> cmds
;
1386 playlist
->rdiff (cmds
);
1387 _session
->add_commands (cmds
);
1389 _session
->add_command (new StatefulDiffCommand (playlist
));
1393 if ((what_we_got
= playlist
->copy (time
)) != 0) {
1394 _editor
.get_cut_buffer().add (what_we_got
);
1399 if ((what_we_got
= playlist
->cut (time
)) != 0) {
1401 vector
<Command
*> cmds
;
1402 playlist
->rdiff (cmds
);
1403 _session
->add_commands (cmds
);
1404 _session
->add_command (new StatefulDiffCommand (playlist
));
1405 what_we_got
->release ();
1412 RouteTimeAxisView::paste (framepos_t pos
, float times
, Selection
& selection
, size_t nth
)
1418 boost::shared_ptr
<Playlist
> pl
= playlist ();
1419 PlaylistSelection::iterator p
;
1421 for (p
= selection
.playlists
.begin(); p
!= selection
.playlists
.end() && nth
; ++p
, --nth
) {}
1423 if (p
== selection
.playlists
.end()) {
1427 DEBUG_TRACE (DEBUG::CutNPaste
, string_compose ("paste to %1\n", pos
));
1429 if (track()->speed() != 1.0f
) {
1430 pos
= session_frame_to_track_frame (pos
, track()->speed());
1431 DEBUG_TRACE (DEBUG::CutNPaste
, string_compose ("modified paste to %1\n", pos
));
1434 pl
->clear_changes ();
1435 pl
->paste (*p
, pos
, times
);
1436 _session
->add_command (new StatefulDiffCommand (pl
));
1442 struct PlaylistSorter
{
1443 bool operator() (boost::shared_ptr
<Playlist
> a
, boost::shared_ptr
<Playlist
> b
) const {
1444 return a
->sort_id() < b
->sort_id();
1449 RouteTimeAxisView::build_playlist_menu ()
1451 using namespace Menu_Helpers
;
1457 delete playlist_action_menu
;
1458 playlist_action_menu
= new Menu
;
1459 playlist_action_menu
->set_name ("ArdourContextMenu");
1461 MenuList
& playlist_items
= playlist_action_menu
->items();
1462 playlist_action_menu
->set_name ("ArdourContextMenu");
1463 playlist_items
.clear();
1465 vector
<boost::shared_ptr
<Playlist
> > playlists
, playlists_tr
;
1466 boost::shared_ptr
<Track
> tr
= track();
1467 RadioMenuItem::Group playlist_group
;
1469 _session
->playlists
->get (playlists
);
1471 /* find the playlists for this diskstream */
1472 for (vector
<boost::shared_ptr
<Playlist
> >::iterator i
= playlists
.begin(); i
!= playlists
.end(); ++i
) {
1473 if (((*i
)->get_orig_diskstream_id() == tr
->diskstream_id()) || (tr
->playlist()->id() == (*i
)->id())) {
1474 playlists_tr
.push_back(*i
);
1478 /* sort the playlists */
1480 sort (playlists_tr
.begin(), playlists_tr
.end(), cmp
);
1482 /* add the playlists to the menu */
1483 for (vector
<boost::shared_ptr
<Playlist
> >::iterator i
= playlists_tr
.begin(); i
!= playlists_tr
.end(); ++i
) {
1484 playlist_items
.push_back (RadioMenuElem (playlist_group
, (*i
)->name()));
1485 RadioMenuItem
*item
= static_cast<RadioMenuItem
*>(&playlist_items
.back());
1486 item
->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist
), item
, boost::weak_ptr
<Playlist
> (*i
)));
1488 if (tr
->playlist()->id() == (*i
)->id()) {
1494 playlist_items
.push_back (SeparatorElem());
1495 playlist_items
.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist
)));
1496 playlist_items
.push_back (SeparatorElem());
1498 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::edit
.property_id
)) {
1499 playlist_items
.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor
, &PublicEditor::new_playlists
), this)));
1500 playlist_items
.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor
, &PublicEditor::copy_playlists
), this)));
1503 // Use a label which tells the user what is happening
1504 playlist_items
.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor
, &PublicEditor::new_playlists
), this)));
1505 playlist_items
.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor
, &PublicEditor::copy_playlists
), this)));
1509 playlist_items
.push_back (SeparatorElem());
1510 playlist_items
.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor
, &PublicEditor::clear_playlists
), this)));
1511 playlist_items
.push_back (SeparatorElem());
1513 playlist_items
.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector
)));
1517 RouteTimeAxisView::use_playlist (RadioMenuItem
*item
, boost::weak_ptr
<Playlist
> wpl
)
1519 assert (is_track());
1521 // exit if we were triggered by deactivating the old playlist
1522 if (!item
->get_active()) {
1526 boost::shared_ptr
<Playlist
> pl (wpl
.lock());
1532 boost::shared_ptr
<AudioPlaylist
> apl
= boost::dynamic_pointer_cast
<AudioPlaylist
> (pl
);
1535 if (track()->playlist() == apl
) {
1536 // exit when use_playlist is called by the creation of the playlist menu
1537 // or the playlist choice is unchanged
1540 track()->use_playlist (apl
);
1542 RouteGroup
* rg
= route_group();
1544 if (rg
&& rg
->is_active() && rg
->enabled_property (ARDOUR::Properties::edit
.property_id
)) {
1545 std::string group_string
= "." + rg
->name() + ".";
1547 std::string take_name
= apl
->name();
1548 std::string::size_type idx
= take_name
.find(group_string
);
1550 if (idx
== std::string::npos
)
1553 take_name
= take_name
.substr(idx
+ group_string
.length()); // find the bit containing the take number / name
1555 boost::shared_ptr
<RouteList
> rl (rg
->route_list());
1557 for (RouteList::const_iterator i
= rl
->begin(); i
!= rl
->end(); ++i
) {
1558 if ( (*i
) == this->route()) {
1562 std::string playlist_name
= (*i
)->name()+group_string
+take_name
;
1564 boost::shared_ptr
<Track
> track
= boost::dynamic_pointer_cast
<Track
>(*i
);
1569 boost::shared_ptr
<Playlist
> ipl
= session()->playlists
->by_name(playlist_name
);
1571 // No playlist for this track for this take yet, make it
1572 track
->use_new_playlist();
1573 track
->playlist()->set_name(playlist_name
);
1575 track
->use_playlist(ipl
);
1583 RouteTimeAxisView::show_playlist_selector ()
1585 _editor
.playlist_selector().show_for (this);
1589 RouteTimeAxisView::map_frozen ()
1595 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen
)
1597 switch (track()->freeze_state()) {
1599 playlist_button
.set_sensitive (false);
1600 rec_enable_button
->set_sensitive (false);
1603 playlist_button
.set_sensitive (true);
1604 rec_enable_button
->set_sensitive (true);
1610 RouteTimeAxisView::color_handler ()
1612 //case cTimeStretchOutline:
1613 if (timestretch_rect
) {
1614 timestretch_rect
->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline
.get();
1616 //case cTimeStretchFill:
1617 if (timestretch_rect
) {
1618 timestretch_rect
->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill
.get();
1624 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1625 * Will add track if necessary.
1628 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter
& param
)
1630 boost::shared_ptr
<AutomationTimeAxisView
> track
= automation_child (param
);
1631 Gtk::CheckMenuItem
* menu
= automation_child_menu_item (param
);
1634 /* it doesn't exist yet, so we don't care about the button state: just add it */
1635 create_automation_child (param
, true);
1638 bool yn
= menu
->get_active();
1639 if (track
->set_visibility (menu
->get_active()) && yn
) {
1641 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1642 will have done that for us.
1646 _route
->gui_changed (X_("track_height"), (void *) 0); /* EMIT_SIGNAL */
1653 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param
)
1655 boost::shared_ptr
<AutomationTimeAxisView
> track
= automation_child (param
);
1661 Gtk::CheckMenuItem
* menu
= automation_child_menu_item (param
);
1663 if (menu
&& !_hidden
) {
1664 ignore_toggle
= true;
1665 menu
->set_active (false);
1666 ignore_toggle
= false;
1669 if (_route
&& !no_redraw
) {
1670 _route
->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1676 RouteTimeAxisView::show_all_automation (bool apply_to_selection
)
1678 if (apply_to_selection
) {
1679 _editor
.get_selection().tracks
.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation
, _1
, false));
1683 /* Show our automation */
1685 for (AutomationTracks::iterator i
= _automation_tracks
.begin(); i
!= _automation_tracks
.end(); ++i
) {
1686 i
->second
->set_visibility (true);
1688 Gtk::CheckMenuItem
* menu
= automation_child_menu_item (i
->first
);
1691 menu
->set_active(true);
1696 /* Show processor automation */
1698 for (list
<ProcessorAutomationInfo
*>::iterator i
= processor_automation
.begin(); i
!= processor_automation
.end(); ++i
) {
1699 for (vector
<ProcessorAutomationNode
*>::iterator ii
= (*i
)->lines
.begin(); ii
!= (*i
)->lines
.end(); ++ii
) {
1700 if ((*ii
)->view
== 0) {
1701 add_processor_automation_curve ((*i
)->processor
, (*ii
)->what
);
1704 (*ii
)->menu_item
->set_active (true);
1712 _route
->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1717 RouteTimeAxisView::show_existing_automation (bool apply_to_selection
)
1719 if (apply_to_selection
) {
1720 _editor
.get_selection().tracks
.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation
, _1
, false));
1724 /* Show our automation */
1726 for (AutomationTracks::iterator i
= _automation_tracks
.begin(); i
!= _automation_tracks
.end(); ++i
) {
1727 if (i
->second
->has_automation()) {
1728 i
->second
->set_visibility (true);
1730 Gtk::CheckMenuItem
* menu
= automation_child_menu_item (i
->first
);
1732 menu
->set_active(true);
1737 /* Show processor automation */
1739 for (list
<ProcessorAutomationInfo
*>::iterator i
= processor_automation
.begin(); i
!= processor_automation
.end(); ++i
) {
1740 for (vector
<ProcessorAutomationNode
*>::iterator ii
= (*i
)->lines
.begin(); ii
!= (*i
)->lines
.end(); ++ii
) {
1741 if ((*ii
)->view
!= 0 && (*i
)->processor
->control((*ii
)->what
)->list()->size() > 0) {
1742 (*ii
)->menu_item
->set_active (true);
1749 _route
->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1754 RouteTimeAxisView::hide_all_automation (bool apply_to_selection
)
1756 if (apply_to_selection
) {
1757 _editor
.get_selection().tracks
.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation
, _1
, false));
1761 /* Hide our automation */
1763 for (AutomationTracks::iterator i
= _automation_tracks
.begin(); i
!= _automation_tracks
.end(); ++i
) {
1764 i
->second
->set_visibility (false);
1766 Gtk::CheckMenuItem
* menu
= automation_child_menu_item (i
->first
);
1769 menu
->set_active (false);
1773 /* Hide processor automation */
1775 for (list
<ProcessorAutomationInfo
*>::iterator i
= processor_automation
.begin(); i
!= processor_automation
.end(); ++i
) {
1776 for (vector
<ProcessorAutomationNode
*>::iterator ii
= (*i
)->lines
.begin(); ii
!= (*i
)->lines
.end(); ++ii
) {
1777 (*ii
)->menu_item
->set_active (false);
1782 _route
->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1788 RouteTimeAxisView::region_view_added (RegionView
* rv
)
1790 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1791 for (Children::iterator i
= children
.begin(); i
!= children
.end(); ++i
) {
1792 boost::shared_ptr
<AutomationTimeAxisView
> atv
;
1794 if ((atv
= boost::dynamic_pointer_cast
<AutomationTimeAxisView
> (*i
)) != 0) {
1799 for (UnderlayMirrorList::iterator i
= _underlay_mirrors
.begin(); i
!= _underlay_mirrors
.end(); ++i
) {
1800 (*i
)->add_ghost(rv
);
1804 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1806 for (vector
<ProcessorAutomationNode
*>::iterator i
= lines
.begin(); i
!= lines
.end(); ++i
) {
1812 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1814 parent
.remove_processor_automation_node (this);
1818 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode
* pan
)
1821 remove_child (pan
->view
);
1825 RouteTimeAxisView::ProcessorAutomationNode
*
1826 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr
<Processor
> processor
, Evoral::Parameter what
)
1828 for (list
<ProcessorAutomationInfo
*>::iterator i
= processor_automation
.begin(); i
!= processor_automation
.end(); ++i
) {
1830 if ((*i
)->processor
== processor
) {
1832 for (vector
<ProcessorAutomationNode
*>::iterator ii
= (*i
)->lines
.begin(); ii
!= (*i
)->lines
.end(); ++ii
) {
1833 if ((*ii
)->what
== what
) {
1844 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr
<Processor
> processor
, Evoral::Parameter what
)
1847 ProcessorAutomationNode
* pan
;
1849 if ((pan
= find_processor_automation_node (processor
, what
)) == 0) {
1850 /* session state may never have been saved with new plugin */
1851 error
<< _("programming error: ")
1852 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
1853 processor
->name(), what
.type(), (int) what
.channel(), what
.id() )
1863 boost::shared_ptr
<AutomationControl
> control
1864 = boost::dynamic_pointer_cast
<AutomationControl
>(processor
->control(what
, true));
1866 pan
->view
= boost::shared_ptr
<AutomationTimeAxisView
>(
1867 new AutomationTimeAxisView (_session
, _route
, processor
, control
, control
->parameter (),
1868 _editor
, *this, false, parent_canvas
,
1869 processor
->describe_parameter (what
), processor
->name()));
1871 pan
->view
->Hiding
.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden
), pan
, processor
));
1873 add_automation_child (control
->parameter(), pan
->view
, pan
->view
->marked_for_display ());
1876 _view
->foreach_regionview (sigc::mem_fun(*pan
->view
.get(), &TimeAxisView::add_ghost
));
1881 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode
* pan
, boost::shared_ptr
<Processor
> i
)
1884 pan
->menu_item
->set_active (false);
1888 _route
->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1893 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr
<Processor
> p
)
1895 boost::shared_ptr
<Processor
> processor (p
.lock ());
1901 set
<Evoral::Parameter
> existing
;
1903 processor
->what_has_data (existing
);
1905 for (set
<Evoral::Parameter
>::iterator i
= existing
.begin(); i
!= existing
.end(); ++i
) {
1907 Evoral::Parameter
param (*i
);
1908 boost::shared_ptr
<AutomationLine
> al
;
1910 if ((al
= find_processor_automation_curve (processor
, param
)) != 0) {
1913 add_processor_automation_curve (processor
, param
);
1919 RouteTimeAxisView::add_automation_child (Evoral::Parameter param
, boost::shared_ptr
<AutomationTimeAxisView
> track
, bool show
)
1921 using namespace Menu_Helpers
;
1928 track
->Hiding
.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden
), param
));
1930 _automation_tracks
[param
] = track
;
1932 if ((node
= track
->get_state_node()) != 0) {
1933 if ((prop
= node
->property ("shown")) != 0) {
1934 /* existing state overrides "show" argument */
1935 show
= string_is_affirmative (prop
->value());
1939 track
->set_visibility (show
);
1942 _route
->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1945 if (!EventTypeMap::instance().is_midi_parameter(param
)) {
1946 /* MIDI-related parameters are always in the menu, there's no
1947 reason to rebuild the menu just because we added a automation
1948 lane for one of them. But if we add a non-MIDI automation
1949 lane, then we need to invalidate the display menu.
1951 delete display_menu
;
1957 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr
<Processor
> p
)
1959 boost::shared_ptr
<Processor
> processor (p
.lock ());
1961 if (!processor
|| !processor
->display_to_user ()) {
1965 /* we use this override to veto the Amp processor from the plugin menu,
1966 as its automation lane can be accessed using the special "Fader" menu
1970 if (boost::dynamic_pointer_cast
<Amp
> (processor
) != 0) {
1974 using namespace Menu_Helpers
;
1975 ProcessorAutomationInfo
*rai
;
1976 list
<ProcessorAutomationInfo
*>::iterator x
;
1978 const std::set
<Evoral::Parameter
>& automatable
= processor
->what_can_be_automated ();
1980 if (automatable
.empty()) {
1984 for (x
= processor_automation
.begin(); x
!= processor_automation
.end(); ++x
) {
1985 if ((*x
)->processor
== processor
) {
1990 if (x
== processor_automation
.end()) {
1992 rai
= new ProcessorAutomationInfo (processor
);
1993 processor_automation
.push_back (rai
);
2001 /* any older menu was deleted at the top of processors_changed()
2002 when we cleared the subplugin menu.
2005 rai
->menu
= manage (new Menu
);
2006 MenuList
& items
= rai
->menu
->items();
2007 rai
->menu
->set_name ("ArdourContextMenu");
2011 std::set
<Evoral::Parameter
> has_visible_automation
;
2012 AutomationTimeAxisView::what_has_visible_automation (processor
, has_visible_automation
);
2014 for (std::set
<Evoral::Parameter
>::const_iterator i
= automatable
.begin(); i
!= automatable
.end(); ++i
) {
2016 ProcessorAutomationNode
* pan
;
2017 CheckMenuItem
* mitem
;
2019 string name
= processor
->describe_parameter (*i
);
2021 items
.push_back (CheckMenuElem (name
));
2022 mitem
= dynamic_cast<CheckMenuItem
*> (&items
.back());
2024 _subplugin_menu_map
[*i
] = mitem
;
2026 if (has_visible_automation
.find((*i
)) != has_visible_automation
.end()) {
2027 mitem
->set_active(true);
2030 if ((pan
= find_processor_automation_node (processor
, *i
)) == 0) {
2034 pan
= new ProcessorAutomationNode (*i
, mitem
, *this);
2036 rai
->lines
.push_back (pan
);
2040 pan
->menu_item
= mitem
;
2044 mitem
->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled
), rai
, pan
));
2047 /* add the menu for this processor, because the subplugin
2048 menu is always cleared at the top of processors_changed().
2049 this is the result of some poor design in gtkmm and/or
2053 subplugin_menu
.items().push_back (MenuElem (processor
->name(), *rai
->menu
));
2058 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo
* rai
,
2059 RouteTimeAxisView::ProcessorAutomationNode
* pan
)
2061 bool showit
= pan
->menu_item
->get_active();
2062 bool redraw
= false;
2064 if (pan
->view
== 0 && showit
) {
2065 add_processor_automation_curve (rai
->processor
, pan
->what
);
2069 if (pan
->view
&& showit
!= pan
->view
->marked_for_display()) {
2070 pan
->view
->set_visibility (showit
);
2074 if (redraw
&& !no_redraw
) {
2075 _route
->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
2080 RouteTimeAxisView::processors_changed (RouteProcessorChange c
)
2082 if (c
.type
== RouteProcessorChange::MeterPointChange
) {
2083 /* nothing to do if only the meter point has changed */
2087 using namespace Menu_Helpers
;
2089 for (list
<ProcessorAutomationInfo
*>::iterator i
= processor_automation
.begin(); i
!= processor_automation
.end(); ++i
) {
2090 (*i
)->valid
= false;
2093 _subplugin_menu_map
.clear ();
2094 subplugin_menu
.items().clear ();
2096 _route
->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu
));
2097 _route
->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves
));
2099 bool deleted_processor_automation
= false;
2101 for (list
<ProcessorAutomationInfo
*>::iterator i
= processor_automation
.begin(); i
!= processor_automation
.end(); ) {
2103 list
<ProcessorAutomationInfo
*>::iterator tmp
;
2111 processor_automation
.erase (i
);
2112 deleted_processor_automation
= true;
2119 if (deleted_processor_automation
&& !no_redraw
) {
2120 _route
->gui_changed ("track_height", this);
2124 boost::shared_ptr
<AutomationLine
>
2125 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr
<Processor
> processor
, Evoral::Parameter what
)
2127 ProcessorAutomationNode
* pan
;
2129 if ((pan
= find_processor_automation_node (processor
, what
)) != 0) {
2135 return boost::shared_ptr
<AutomationLine
>();
2139 RouteTimeAxisView::reset_processor_automation_curves ()
2141 for (ProcessorAutomationCurves::iterator i
= processor_automation_curves
.begin(); i
!= processor_automation_curves
.end(); ++i
) {
2147 RouteTimeAxisView::update_rec_display ()
2149 RouteUI::update_rec_display ();
2150 name_entry
.set_sensitive (!_route
->record_enabled());
2154 RouteTimeAxisView::set_layer_display (LayerDisplay d
, bool apply_to_selection
)
2156 if (apply_to_selection
) {
2157 _editor
.get_selection().tracks
.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display
, _1
, d
, false));
2161 _view
->set_layer_display (d
);
2165 xml_node
->add_property (N_("layer-display"), enum_2_string (d
));
2170 RouteTimeAxisView::layer_display () const
2173 return _view
->layer_display ();
2176 /* we don't know, since we don't have a _view, so just return something */
2182 boost::shared_ptr
<AutomationTimeAxisView
>
2183 RouteTimeAxisView::automation_child(Evoral::Parameter param
)
2185 AutomationTracks::iterator i
= _automation_tracks
.find(param
);
2186 if (i
!= _automation_tracks
.end()) {
2189 return boost::shared_ptr
<AutomationTimeAxisView
>();
2194 RouteTimeAxisView::fast_update ()
2196 gm
.get_level_meter().update_meters ();
2200 RouteTimeAxisView::hide_meter ()
2203 gm
.get_level_meter().hide_meters ();
2207 RouteTimeAxisView::show_meter ()
2213 RouteTimeAxisView::reset_meter ()
2215 if (Config
->get_show_track_meters()) {
2216 gm
.get_level_meter().setup_meters (height
-5);
2223 RouteTimeAxisView::clear_meter ()
2225 gm
.get_level_meter().clear_meters ();
2229 RouteTimeAxisView::meter_changed ()
2231 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed
)
2236 RouteTimeAxisView::io_changed (IOChange
/*change*/, void */
*src*/
)
2242 RouteTimeAxisView::build_underlay_menu(Gtk::Menu
* parent_menu
)
2244 using namespace Menu_Helpers
;
2246 if (!_underlay_streams
.empty()) {
2247 MenuList
& parent_items
= parent_menu
->items();
2248 Menu
* gs_menu
= manage (new Menu
);
2249 gs_menu
->set_name ("ArdourContextMenu");
2250 MenuList
& gs_items
= gs_menu
->items();
2252 parent_items
.push_back (MenuElem (_("Underlays"), *gs_menu
));
2254 for(UnderlayList::iterator it
= _underlay_streams
.begin(); it
!= _underlay_streams
.end(); ++it
) {
2255 gs_items
.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it
)->trackview().name()),
2256 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay
), *it
)));
2262 RouteTimeAxisView::set_underlay_state()
2264 if (!underlay_xml_node
) {
2268 XMLNodeList nlist
= underlay_xml_node
->children();
2269 XMLNodeConstIterator niter
;
2270 XMLNode
*child_node
;
2272 for (niter
= nlist
.begin(); niter
!= nlist
.end(); ++niter
) {
2273 child_node
= *niter
;
2275 if (child_node
->name() != "Underlay") {
2279 XMLProperty
* prop
= child_node
->property ("id");
2281 PBD::ID
id (prop
->value());
2283 RouteTimeAxisView
* v
= _editor
.get_route_view_by_route_id (id
);
2286 add_underlay(v
->view(), false);
2295 RouteTimeAxisView::add_underlay (StreamView
* v
, bool update_xml
)
2301 RouteTimeAxisView
& other
= v
->trackview();
2303 if (find(_underlay_streams
.begin(), _underlay_streams
.end(), v
) == _underlay_streams
.end()) {
2304 if (find(other
._underlay_mirrors
.begin(), other
._underlay_mirrors
.end(), this) != other
._underlay_mirrors
.end()) {
2305 fatal
<< _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg
;
2309 _underlay_streams
.push_back(v
);
2310 other
._underlay_mirrors
.push_back(this);
2312 v
->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost
));
2315 if (!underlay_xml_node
) {
2317 underlay_xml_node
= xml_node
->add_child("Underlays");
2320 XMLNode
* node
= underlay_xml_node
->add_child("Underlay");
2321 XMLProperty
* prop
= node
->add_property("id");
2322 prop
->set_value(v
->trackview().route()->id().to_s());
2328 RouteTimeAxisView::remove_underlay (StreamView
* v
)
2334 UnderlayList::iterator it
= find(_underlay_streams
.begin(), _underlay_streams
.end(), v
);
2335 RouteTimeAxisView
& other
= v
->trackview();
2337 if (it
!= _underlay_streams
.end()) {
2338 UnderlayMirrorList::iterator gm
= find(other
._underlay_mirrors
.begin(), other
._underlay_mirrors
.end(), this);
2340 if (gm
== other
._underlay_mirrors
.end()) {
2341 fatal
<< _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg
;
2345 v
->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost
));
2347 _underlay_streams
.erase(it
);
2348 other
._underlay_mirrors
.erase(gm
);
2350 if (underlay_xml_node
) {
2351 underlay_xml_node
->remove_nodes_and_delete("id", v
->trackview().route()->id().to_s());
2357 RouteTimeAxisView::set_button_names ()
2359 rec_enable_button_label
.set_text (_("r"));
2361 if (_route
&& _route
->solo_safe()) {
2362 solo_button_label
.set_text (X_("!"));
2364 if (Config
->get_solo_control_is_listen_control()) {
2365 switch (Config
->get_listen_position()) {
2366 case AfterFaderListen
:
2367 solo_button_label
.set_text (_("A"));
2369 case PreFaderListen
:
2370 solo_button_label
.set_text (_("P"));
2374 solo_button_label
.set_text (_("s"));
2377 mute_button_label
.set_text (_("m"));
2381 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param
)
2383 ParameterMenuMap::iterator i
= _main_automation_menu_map
.find (param
);
2384 if (i
!= _main_automation_menu_map
.end()) {
2388 i
= _subplugin_menu_map
.find (param
);
2389 if (i
!= _subplugin_menu_map
.end()) {
2397 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter
& param
, bool show
)
2399 boost::shared_ptr
<AutomationControl
> c
= _route
->gain_control();
2401 error
<< "Route has no gain automation, unable to add automation track view." << endmsg
;
2405 gain_track
.reset (new AutomationTimeAxisView (_session
,
2406 _route
, _route
->amp(), c
, param
,
2411 _route
->amp()->describe_parameter(param
)));
2414 _view
->foreach_regionview (sigc::mem_fun (*gain_track
.get(), &TimeAxisView::add_ghost
));
2417 add_automation_child (Evoral::Parameter(GainAutomation
), gain_track
, show
);
2421 void add_region_to_list (RegionView
* rv
, Playlist::RegionList
* l
)
2423 l
->push_back (rv
->region());
2427 RouteTimeAxisView::combine_regions ()
2429 /* as of may 2011, we do not offer uncombine for MIDI tracks
2432 if (!is_audio_track()) {
2440 Playlist::RegionList selected_regions
;
2441 boost::shared_ptr
<Playlist
> playlist
= track()->playlist();
2443 _view
->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list
), &selected_regions
));
2445 if (selected_regions
.size() < 2) {
2449 playlist
->clear_changes ();
2450 boost::shared_ptr
<Region
> compound_region
= playlist
->combine (selected_regions
);
2452 _session
->add_command (new StatefulDiffCommand (playlist
));
2453 /* make the new region be selected */
2455 return _view
->find_view (compound_region
);
2459 RouteTimeAxisView::uncombine_regions ()
2461 /* as of may 2011, we do not offer uncombine for MIDI tracks
2463 if (!is_audio_track()) {
2471 Playlist::RegionList selected_regions
;
2472 boost::shared_ptr
<Playlist
> playlist
= track()->playlist();
2474 /* have to grab selected regions first because the uncombine is going
2475 * to change that in the middle of the list traverse
2478 _view
->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list
), &selected_regions
));
2480 playlist
->clear_changes ();
2482 for (Playlist::RegionList::iterator i
= selected_regions
.begin(); i
!= selected_regions
.end(); ++i
) {
2483 playlist
->uncombine (*i
);
2486 _session
->add_command (new StatefulDiffCommand (playlist
));