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
, Canvas
& canvas
)
110 , TimeAxisView(sess
,ed
,(TimeAxisView
*) 0, canvas
)
112 , parent_canvas (canvas
)
113 , button_table (3, 3)
114 , route_group_button (_("g"))
115 , playlist_button (_("p"))
116 , automation_button (_("a"))
117 , automation_action_menu (0)
118 , plugins_submenu_item (0)
119 , route_group_menu (0)
120 , playlist_action_menu (0)
122 , color_mode_menu (0)
123 , gm (sess
, slider
, true, 115)
128 RouteTimeAxisView::set_route (boost::shared_ptr
<Route
> rt
)
130 RouteUI::set_route (rt
);
132 gm
.set_controls (_route
, _route
->shared_peak_meter(), _route
->amp());
133 gm
.get_level_meter().set_no_show_all();
134 gm
.get_level_meter().setup_meters(50);
136 string str
= gui_property ("height");
138 set_height (atoi (str
));
140 set_height (preset_height (HeightNormal
));
143 if (!_route
->is_hidden()) {
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 ("TrackGroupButton");
160 playlist_button
.set_name ("TrackPlaylistButton");
161 automation_button
.set_name ("TrackAutomationButton");
163 route_group_button
.unset_flags (Gtk::CAN_FOCUS
);
164 playlist_button
.unset_flags (Gtk::CAN_FOCUS
);
165 automation_button
.unset_flags (Gtk::CAN_FOCUS
);
167 route_group_button
.signal_button_release_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click
), false);
168 playlist_button
.signal_clicked().connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click
));
169 automation_button
.signal_clicked().connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click
));
175 rec_enable_button
->remove ();
177 switch (track()->mode()) {
179 case ARDOUR::NonLayered
:
180 rec_enable_button
->add (*(manage (new Image (::get_icon (X_("record_normal_red"))))));
182 case ARDOUR::Destructive
:
183 rec_enable_button
->add (*(manage (new Image (::get_icon (X_("record_tape_red"))))));
186 rec_enable_button
->show_all ();
188 controls_table
.attach (*rec_enable_button
, 5, 6, 0, 1, Gtk::FILL
|Gtk::EXPAND
, Gtk::FILL
|Gtk::EXPAND
, 0, 0);
190 if (is_midi_track()) {
191 ARDOUR_UI::instance()->set_tip(*rec_enable_button
, _("Record (Right-click for Step Edit)"));
193 ARDOUR_UI::instance()->set_tip(*rec_enable_button
, _("Record"));
196 rec_enable_button
->set_sensitive (_session
->writable());
199 controls_hbox
.pack_start(gm
.get_level_meter(), false, false);
200 _route
->meter_change
.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed
, this), gui_context());
201 _route
->input()->changed
.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::io_changed
, this, _1
, _2
), gui_context());
202 _route
->output()->changed
.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::io_changed
, this, _1
, _2
), gui_context());
204 controls_table
.attach (*mute_button
, 6, 7, 0, 1, Gtk::FILL
|Gtk::EXPAND
, Gtk::FILL
|Gtk::EXPAND
, 0, 0);
206 if (!_route
->is_master()) {
207 controls_table
.attach (*solo_button
, 7, 8, 0, 1, Gtk::FILL
|Gtk::EXPAND
, Gtk::FILL
|Gtk::EXPAND
, 0, 0);
210 controls_table
.attach (route_group_button
, 7, 8, 1, 2, Gtk::FILL
|Gtk::EXPAND
, Gtk::FILL
|Gtk::EXPAND
, 0, 0);
211 controls_table
.attach (gm
.get_gain_slider(), 0, 5, 1, 2, Gtk::SHRINK
, Gtk::SHRINK
, 0, 0);
213 ARDOUR_UI::instance()->set_tip(*solo_button
,_("Solo"));
214 ARDOUR_UI::instance()->set_tip(*mute_button
,_("Mute"));
215 ARDOUR_UI::instance()->set_tip(route_group_button
, _("Route Group"));
216 ARDOUR_UI::instance()->set_tip(playlist_button
,_("Playlist"));
217 ARDOUR_UI::instance()->set_tip(automation_button
, _("Automation"));
221 controls_table
.attach (automation_button
, 6, 7, 1, 2, Gtk::FILL
|Gtk::EXPAND
, Gtk::FILL
|Gtk::EXPAND
);
223 if (is_track() && track()->mode() == ARDOUR::Normal
) {
224 controls_table
.attach (playlist_button
, 5, 6, 1, 2, Gtk::FILL
|Gtk::EXPAND
, Gtk::FILL
|Gtk::EXPAND
);
229 _route
->processors_changed
.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::processors_changed
, this, _1
), gui_context());
230 _route
->PropertyChanged
.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::route_property_changed
, this, _1
), gui_context());
234 str
= gui_property ("layer-display");
236 set_layer_display (LayerDisplay (string_2_enum (str
, _view
->layer_display ())));
239 track()->FreezeChange
.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen
, this), gui_context());
240 track()->SpeedChanged
.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed
, this), gui_context());
242 /* pick up the correct freeze state */
246 _editor
.ZoomChanged
.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_unit
));
247 _editor
.HorizontalPositionChanged
.connect (sigc::mem_fun (*this, &RouteTimeAxisView::horizontal_position_changed
));
248 ColorsChanged
.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler
));
250 PropertyList
* plist
= new PropertyList();
252 plist
->add (ARDOUR::Properties::edit
, true);
253 plist
->add (ARDOUR::Properties::mute
, true);
254 plist
->add (ARDOUR::Properties::solo
, true);
256 route_group_menu
= new RouteGroupMenu (_session
, plist
);
258 gm
.get_gain_slider().signal_scroll_event().connect(sigc::mem_fun(*this, &RouteTimeAxisView::controls_ebox_scroll
), false);
259 gm
.get_gain_slider().set_name ("TrackGainFader");
265 RouteTimeAxisView::~RouteTimeAxisView ()
267 CatchDeletion (this);
269 for (list
<ProcessorAutomationInfo
*>::iterator i
= processor_automation
.begin(); i
!= processor_automation
.end(); ++i
) {
273 delete playlist_action_menu
;
274 playlist_action_menu
= 0;
279 _automation_tracks
.clear ();
281 delete route_group_menu
;
285 RouteTimeAxisView::post_construct ()
287 /* map current state of the route */
289 update_diskstream_display ();
290 setup_processor_menu_and_curves ();
291 reset_processor_automation_curves ();
294 /** Set up the processor menu for the current set of processors, and
295 * display automation curves for any parameters which have data.
298 RouteTimeAxisView::setup_processor_menu_and_curves ()
300 _subplugin_menu_map
.clear ();
301 subplugin_menu
.items().clear ();
302 _route
->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu
));
303 _route
->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves
));
307 RouteTimeAxisView::route_group_click (GdkEventButton
*ev
)
309 if (Keyboard::modifier_state_equals (ev
->state
, Keyboard::PrimaryModifier
)) {
310 if (_route
->route_group()) {
311 _route
->route_group()->remove (_route
);
317 r
.push_back (route ());
319 route_group_menu
->build (r
);
320 route_group_menu
->menu()->popup (ev
->button
, ev
->time
);
326 RouteTimeAxisView::playlist_changed ()
332 RouteTimeAxisView::label_view ()
334 string x
= _route
->name();
336 if (x
!= name_entry
.get_text()) {
337 name_entry
.set_text (x
);
340 if (x
!= name_label
.get_text()) {
341 name_label
.set_text (x
);
344 ARDOUR_UI::instance()->set_tip (name_entry
, x
);
348 RouteTimeAxisView::route_property_changed (const PropertyChange
& what_changed
)
350 if (what_changed
.contains (ARDOUR::Properties::name
)) {
356 RouteTimeAxisView::take_name_changed (void *src
)
364 RouteTimeAxisView::playlist_click ()
366 build_playlist_menu ();
367 conditionally_add_to_selection ();
368 playlist_action_menu
->popup (1, gtk_get_current_event_time());
372 RouteTimeAxisView::automation_click ()
374 conditionally_add_to_selection ();
375 build_automation_action_menu (false);
376 automation_action_menu
->popup (1, gtk_get_current_event_time());
380 RouteTimeAxisView::build_automation_action_menu (bool for_selection
)
382 using namespace Menu_Helpers
;
384 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
385 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
388 detach_menu (subplugin_menu
);
390 _main_automation_menu_map
.clear ();
391 delete automation_action_menu
;
392 automation_action_menu
= new Menu
;
394 MenuList
& items
= automation_action_menu
->items();
396 automation_action_menu
->set_name ("ArdourContextMenu");
398 items
.push_back (MenuElem (_("Show All Automation"),
399 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation
), for_selection
)));
401 items
.push_back (MenuElem (_("Show Existing Automation"),
402 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation
), for_selection
)));
404 items
.push_back (MenuElem (_("Hide All Automation"),
405 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation
), for_selection
)));
407 items
.push_back (SeparatorElem ());
409 /* Attach the plugin submenu. It may have previously been used elsewhere,
410 so it was detached above */
412 items
.push_back (MenuElem (_("Plugins"), subplugin_menu
));
413 items
.back().set_sensitive (!subplugin_menu
.items().empty() && (!for_selection
|| _editor
.get_selection().tracks
.size() == 1));;
417 RouteTimeAxisView::build_display_menu ()
419 using namespace Menu_Helpers
;
423 TimeAxisView::build_display_menu ();
425 /* now fill it with our stuff */
427 MenuList
& items
= display_menu
->items();
428 display_menu
->set_name ("ArdourContextMenu");
430 items
.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color
)));
433 detach_menu (*_size_menu
);
436 items
.push_back (MenuElem (_("Height"), *_size_menu
));
438 items
.push_back (SeparatorElem());
440 if (!Profile
->get_sae()) {
441 items
.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog
)));
442 items
.back().set_sensitive (_editor
.get_selection().tracks
.size() <= 1);
443 items
.push_back (SeparatorElem());
446 // Hook for derived classes to add type specific stuff
447 append_extra_display_menu_items ();
451 Menu
* layers_menu
= manage (new Menu
);
452 MenuList
&layers_items
= layers_menu
->items();
453 layers_menu
->set_name("ArdourContextMenu");
455 RadioMenuItem::Group layers_group
;
457 /* Find out how many overlaid/stacked tracks we have in the selection */
461 TrackSelection
const & s
= _editor
.get_selection().tracks
;
462 for (TrackSelection::const_iterator i
= s
.begin(); i
!= s
.end(); ++i
) {
463 StreamView
* v
= (*i
)->view ();
468 switch (v
->layer_display ()) {
478 /* We're not connecting to signal_toggled() here; in the case where these two items are
479 set to be in the `inconsistent' state, it seems that one or other will end up active
480 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
481 select the active one, no toggled signal is emitted so nothing happens.
484 layers_items
.push_back (RadioMenuElem (layers_group
, _("Overlaid")));
485 RadioMenuItem
* i
= dynamic_cast<RadioMenuItem
*> (&layers_items
.back ());
486 i
->set_active (overlaid
!= 0 && stacked
== 0);
487 i
->set_inconsistent (overlaid
!= 0 && stacked
!= 0);
488 i
->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display
), Overlaid
, true));
490 layers_items
.push_back (
491 RadioMenuElem (layers_group
, _("Stacked"),
492 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display
), Stacked
, true))
495 i
= dynamic_cast<RadioMenuItem
*> (&layers_items
.back ());
496 i
->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display
), Stacked
, true));
497 i
->set_active (overlaid
== 0 && stacked
!= 0);
498 i
->set_inconsistent (overlaid
!= 0 && stacked
!= 0);
500 items
.push_back (MenuElem (_("Layers"), *layers_menu
));
502 if (!Profile
->get_sae()) {
504 Menu
* alignment_menu
= manage (new Menu
);
505 MenuList
& alignment_items
= alignment_menu
->items();
506 alignment_menu
->set_name ("ArdourContextMenu");
508 RadioMenuItem::Group align_group
;
510 /* Same verbose hacks as for the layering options above */
516 boost::shared_ptr
<Track
> first_track
;
518 TrackSelection
const & s
= _editor
.get_selection().tracks
;
519 for (TrackSelection::const_iterator i
= s
.begin(); i
!= s
.end(); ++i
) {
520 RouteTimeAxisView
* r
= dynamic_cast<RouteTimeAxisView
*> (*i
);
521 if (!r
|| !r
->is_track ()) {
526 first_track
= r
->track();
529 switch (r
->track()->alignment_choice()) {
533 switch (r
->track()->alignment_style()) {
534 case ExistingMaterial
:
542 case UseExistingMaterial
:
558 inconsistent
= false;
567 if (!inconsistent
&& first_track
) {
569 alignment_items
.push_back (RadioMenuElem (align_group
, _("Automatic (based on I/O connections)")));
570 i
= dynamic_cast<RadioMenuItem
*> (&alignment_items
.back());
571 i
->set_active (automatic
!= 0 && existing
== 0 && capture
== 0);
572 i
->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice
), i
, Automatic
, true));
574 switch (first_track
->alignment_choice()) {
576 switch (first_track
->alignment_style()) {
577 case ExistingMaterial
:
578 alignment_items
.push_back (MenuElem (_("(Currently: Existing Material)")));
581 alignment_items
.push_back (MenuElem (_("(Currently: Capture Time)")));
589 alignment_items
.push_back (RadioMenuElem (align_group
, _("Align With Existing Material")));
590 i
= dynamic_cast<RadioMenuItem
*> (&alignment_items
.back());
591 i
->set_active (existing
!= 0 && capture
== 0 && automatic
== 0);
592 i
->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice
), i
, UseExistingMaterial
, true));
594 alignment_items
.push_back (RadioMenuElem (align_group
, _("Align With Capture Time")));
595 i
= dynamic_cast<RadioMenuItem
*> (&alignment_items
.back());
596 i
->set_active (existing
== 0 && capture
!= 0 && automatic
== 0);
597 i
->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice
), i
, UseCaptureTime
, true));
599 items
.push_back (MenuElem (_("Alignment"), *alignment_menu
));
605 Menu
* mode_menu
= manage (new Menu
);
606 MenuList
& mode_items
= mode_menu
->items ();
607 mode_menu
->set_name ("ArdourContextMenu");
609 RadioMenuItem::Group mode_group
;
615 for (TrackSelection::const_iterator i
= s
.begin(); i
!= s
.end(); ++i
) {
616 RouteTimeAxisView
* r
= dynamic_cast<RouteTimeAxisView
*> (*i
);
617 if (!r
|| !r
->is_track ()) {
621 switch (r
->track()->mode()) {
634 mode_items
.push_back (RadioMenuElem (mode_group
, _("Normal Mode")));
635 i
= dynamic_cast<RadioMenuItem
*> (&mode_items
.back ());
636 i
->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode
), ARDOUR::Normal
, true));
637 i
->set_active (normal
!= 0 && tape
== 0 && non_layered
== 0);
638 i
->set_inconsistent (normal
!= 0 && (tape
!= 0 || non_layered
!= 0));
640 mode_items
.push_back (RadioMenuElem (mode_group
, _("Tape Mode")));
641 i
= dynamic_cast<RadioMenuItem
*> (&mode_items
.back ());
642 i
->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode
), ARDOUR::Destructive
, true));
643 i
->set_active (normal
== 0 && tape
!= 0 && non_layered
== 0);
644 i
->set_inconsistent (tape
!= 0 && (normal
!= 0 || non_layered
!= 0));
646 mode_items
.push_back (RadioMenuElem (mode_group
, _("Non-Layered Mode")));
647 i
= dynamic_cast<RadioMenuItem
*> (&mode_items
.back ());
648 i
->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode
), ARDOUR::NonLayered
, true));
649 i
->set_active (normal
== 0 && tape
== 0 && non_layered
!= 0);
650 i
->set_inconsistent (non_layered
!= 0 && (normal
!= 0 || tape
!= 0));
652 items
.push_back (MenuElem (_("Mode"), *mode_menu
));
655 color_mode_menu
= build_color_mode_menu();
656 if (color_mode_menu
) {
657 items
.push_back (MenuElem (_("Color Mode"), *color_mode_menu
));
660 items
.push_back (SeparatorElem());
662 build_playlist_menu ();
663 items
.push_back (MenuElem (_("Playlist"), *playlist_action_menu
));
664 items
.back().set_sensitive (_editor
.get_selection().tracks
.size() <= 1);
666 route_group_menu
->detach ();
669 for (TrackSelection::iterator i
= _editor
.get_selection().tracks
.begin(); i
!= _editor
.get_selection().tracks
.end(); ++i
) {
670 RouteTimeAxisView
* rtv
= dynamic_cast<RouteTimeAxisView
*> (*i
);
672 r
.push_back (rtv
->route ());
677 r
.push_back (route ());
680 route_group_menu
->build (r
);
681 items
.push_back (MenuElem (_("Route Group"), *route_group_menu
->menu ()));
683 build_automation_action_menu (true);
684 items
.push_back (MenuElem (_("Automation"), *automation_action_menu
));
686 items
.push_back (SeparatorElem());
691 TrackSelection
const & s
= _editor
.get_selection().tracks
;
692 for (TrackSelection::const_iterator i
= s
.begin(); i
!= s
.end(); ++i
) {
693 RouteTimeAxisView
* r
= dynamic_cast<RouteTimeAxisView
*> (*i
);
698 if (r
->route()->active()) {
705 items
.push_back (CheckMenuElem (_("Active")));
706 CheckMenuItem
* i
= dynamic_cast<CheckMenuItem
*> (&items
.back());
707 bool click_sets_active
= true;
708 if (active
> 0 && inactive
== 0) {
709 i
->set_active (true);
710 click_sets_active
= false;
711 } else if (active
> 0 && inactive
> 0) {
712 i
->set_inconsistent (true);
714 i
->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active
), click_sets_active
, true));
716 items
.push_back (SeparatorElem());
717 items
.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor
, &PublicEditor::hide_track_in_display
), this, true)));
718 if (!Profile
->get_sae()) {
719 items
.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route
), true)));
721 items
.push_front (SeparatorElem());
722 items
.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route
), true)));
727 RouteTimeAxisView::set_track_mode (TrackMode mode
, bool apply_to_selection
)
729 if (apply_to_selection
) {
730 _editor
.get_selection().tracks
.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode
, _1
, mode
, false));
735 if (!track()->can_use_mode (mode
, needs_bounce
)) {
741 cerr
<< "would bounce this one\n";
746 track()->set_mode (mode
);
748 rec_enable_button
->remove ();
751 case ARDOUR::NonLayered
:
753 rec_enable_button
->add (*(manage (new Image (::get_icon (X_("record_normal_red"))))));
755 case ARDOUR::Destructive
:
756 rec_enable_button
->add (*(manage (new Image (::get_icon (X_("record_tape_red"))))));
760 rec_enable_button
->show_all ();
765 RouteTimeAxisView::show_timestretch (framepos_t start
, framepos_t end
)
771 TimeAxisView::show_timestretch (start
, end
);
781 /* check that the time selection was made in our route, or our route group.
782 remember that route_group() == 0 implies the route is *not* in a edit group.
785 if (!(ts
.track
== this || (ts
.group
!= 0 && ts
.group
== _route
->route_group()))) {
786 /* this doesn't apply to us */
790 /* ignore it if our edit group is not active */
792 if ((ts
.track
!= this) && _route
->route_group() && !_route
->route_group()->is_active()) {
797 if (timestretch_rect
== 0) {
798 timestretch_rect
= new SimpleRect (*canvas_display ());
799 timestretch_rect
->property_x1() = 0.0;
800 timestretch_rect
->property_y1() = 0.0;
801 timestretch_rect
->property_x2() = 0.0;
802 timestretch_rect
->property_y2() = 0.0;
803 timestretch_rect
->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill
.get();
804 timestretch_rect
->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline
.get();
807 timestretch_rect
->show ();
808 timestretch_rect
->raise_to_top ();
810 x1
= start
/ _editor
.get_current_zoom();
811 x2
= (end
- 1) / _editor
.get_current_zoom();
812 y2
= current_height() - 2;
814 timestretch_rect
->property_x1() = x1
;
815 timestretch_rect
->property_y1() = 1.0;
816 timestretch_rect
->property_x2() = x2
;
817 timestretch_rect
->property_y2() = y2
;
821 RouteTimeAxisView::hide_timestretch ()
823 TimeAxisView::hide_timestretch ();
825 if (timestretch_rect
) {
826 timestretch_rect
->hide ();
831 RouteTimeAxisView::show_selection (TimeSelection
& ts
)
835 /* ignore it if our edit group is not active or if the selection was started
836 in some other track or route group (remember that route_group() == 0 means
837 that the track is not in an route group).
840 if (((ts
.track
!= this && !is_child (ts
.track
)) && _route
->route_group() && !_route
->route_group()->is_active()) ||
841 (!(ts
.track
== this || is_child (ts
.track
) || (ts
.group
!= 0 && ts
.group
== _route
->route_group())))) {
847 TimeAxisView::show_selection (ts
);
851 RouteTimeAxisView::set_height (uint32_t h
)
854 bool height_changed
= (height
== 0) || (h
!= height
);
855 gm
.get_level_meter().setup_meters (gmlen
);
857 TimeAxisView::set_height (h
);
860 _view
->set_height ((double) current_height());
863 if (height
>= preset_height (HeightNormal
)) {
867 gm
.get_gain_slider().show();
869 if (!_route
|| _route
->is_monitor()) {
874 if (rec_enable_button
)
875 rec_enable_button
->show();
877 route_group_button
.show();
878 automation_button
.show();
880 if (is_track() && track()->mode() == ARDOUR::Normal
) {
881 playlist_button
.show();
888 gm
.get_gain_slider().hide();
890 if (!_route
|| _route
->is_monitor()) {
895 if (rec_enable_button
)
896 rec_enable_button
->show();
898 route_group_button
.hide ();
899 automation_button
.hide ();
901 if (is_track() && track()->mode() == ARDOUR::Normal
) {
902 playlist_button
.hide ();
907 if (height_changed
&& !no_redraw
) {
908 /* only emit the signal if the height really changed */
914 RouteTimeAxisView::set_color (Gdk::Color
const & c
)
916 RouteUI::set_color (c
);
919 _view
->apply_color (_color
, StreamView::RegionColor
);
924 RouteTimeAxisView::reset_samples_per_unit ()
926 set_samples_per_unit (_editor
.get_current_zoom());
930 RouteTimeAxisView::horizontal_position_changed ()
933 _view
->horizontal_position_changed ();
938 RouteTimeAxisView::set_samples_per_unit (double spu
)
943 speed
= track()->speed();
947 _view
->set_samples_per_unit (spu
* speed
);
950 TimeAxisView::set_samples_per_unit (spu
* speed
);
954 RouteTimeAxisView::set_align_choice (RadioMenuItem
* mitem
, AlignChoice choice
, bool apply_to_selection
)
956 if (!mitem
->get_active()) {
957 /* this is one of the two calls made when these radio menu items change status. this one
958 is for the item that became inactive, and we want to ignore it.
963 if (apply_to_selection
) {
964 _editor
.get_selection().tracks
.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice
, _1
, mitem
, choice
, false));
967 track()->set_align_choice (choice
);
973 RouteTimeAxisView::rename_current_playlist ()
975 ArdourPrompter
prompter (true);
978 boost::shared_ptr
<Track
> tr
= track();
979 if (!tr
|| tr
->destructive()) {
983 boost::shared_ptr
<Playlist
> pl
= tr
->playlist();
988 prompter
.set_title (_("Rename Playlist"));
989 prompter
.set_prompt (_("New name for playlist:"));
990 prompter
.set_initial_text (pl
->name());
991 prompter
.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT
);
992 prompter
.set_response_sensitive (Gtk::RESPONSE_ACCEPT
, false);
994 switch (prompter
.run ()) {
995 case Gtk::RESPONSE_ACCEPT
:
996 prompter
.get_result (name
);
1008 RouteTimeAxisView::resolve_new_group_playlist_name(std::string
&basename
, vector
<boost::shared_ptr
<Playlist
> > const & playlists
)
1010 std::string
ret (basename
);
1012 std::string
const group_string
= "." + route_group()->name() + ".";
1014 // iterate through all playlists
1016 for (vector
<boost::shared_ptr
<Playlist
> >::const_iterator i
= playlists
.begin(); i
!= playlists
.end(); ++i
) {
1017 std::string tmp
= (*i
)->name();
1019 std::string::size_type idx
= tmp
.find(group_string
);
1020 // find those which belong to this group
1021 if (idx
!= string::npos
) {
1022 tmp
= tmp
.substr(idx
+ group_string
.length());
1024 // and find the largest current number
1025 int x
= atoi(tmp
.c_str());
1026 if (x
> maxnumber
) {
1035 snprintf (buf
, sizeof(buf
), "%d", maxnumber
);
1037 ret
= this->name() + "." + route_group()->name () + "." + buf
;
1043 RouteTimeAxisView::use_copy_playlist (bool prompt
, vector
<boost::shared_ptr
<Playlist
> > const & playlists_before_op
)
1047 boost::shared_ptr
<Track
> tr
= track ();
1048 if (!tr
|| tr
->destructive()) {
1052 boost::shared_ptr
<const Playlist
> pl
= tr
->playlist();
1059 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::edit
.property_id
)) {
1060 name
= resolve_new_group_playlist_name(name
, playlists_before_op
);
1063 while (_session
->playlists
->by_name(name
)) {
1064 name
= Playlist::bump_name (name
, *_session
);
1067 // TODO: The prompter "new" button should be de-activated if the user
1068 // specifies a playlist name which already exists in the session.
1072 ArdourPrompter
prompter (true);
1074 prompter
.set_title (_("New Copy Playlist"));
1075 prompter
.set_prompt (_("Name for new playlist:"));
1076 prompter
.set_initial_text (name
);
1077 prompter
.add_button (Gtk::Stock::NEW
, Gtk::RESPONSE_ACCEPT
);
1078 prompter
.set_response_sensitive (Gtk::RESPONSE_ACCEPT
, true);
1079 prompter
.show_all ();
1081 switch (prompter
.run ()) {
1082 case Gtk::RESPONSE_ACCEPT
:
1083 prompter
.get_result (name
);
1091 if (name
.length()) {
1092 tr
->use_copy_playlist ();
1093 tr
->playlist()->set_name (name
);
1098 RouteTimeAxisView::use_new_playlist (bool prompt
, vector
<boost::shared_ptr
<Playlist
> > const & playlists_before_op
)
1102 boost::shared_ptr
<Track
> tr
= track ();
1103 if (!tr
|| tr
->destructive()) {
1107 boost::shared_ptr
<const Playlist
> pl
= tr
->playlist();
1114 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::edit
.property_id
)) {
1115 name
= resolve_new_group_playlist_name(name
,playlists_before_op
);
1118 while (_session
->playlists
->by_name(name
)) {
1119 name
= Playlist::bump_name (name
, *_session
);
1125 ArdourPrompter
prompter (true);
1127 prompter
.set_title (_("New Playlist"));
1128 prompter
.set_prompt (_("Name for new playlist:"));
1129 prompter
.set_initial_text (name
);
1130 prompter
.add_button (Gtk::Stock::NEW
, Gtk::RESPONSE_ACCEPT
);
1131 prompter
.set_response_sensitive (Gtk::RESPONSE_ACCEPT
, true);
1133 switch (prompter
.run ()) {
1134 case Gtk::RESPONSE_ACCEPT
:
1135 prompter
.get_result (name
);
1143 if (name
.length()) {
1144 tr
->use_new_playlist ();
1145 tr
->playlist()->set_name (name
);
1150 RouteTimeAxisView::clear_playlist ()
1152 boost::shared_ptr
<Track
> tr
= track ();
1153 if (!tr
|| tr
->destructive()) {
1157 boost::shared_ptr
<Playlist
> pl
= tr
->playlist();
1162 _editor
.clear_playlist (pl
);
1166 RouteTimeAxisView::speed_changed ()
1168 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_unit
, this));
1172 RouteTimeAxisView::update_diskstream_display ()
1182 RouteTimeAxisView::selection_click (GdkEventButton
* ev
)
1184 if (Keyboard::modifier_state_equals (ev
->state
, (Keyboard::TertiaryModifier
|Keyboard::PrimaryModifier
))) {
1186 /* special case: select/deselect all tracks */
1187 if (_editor
.get_selection().selected (this)) {
1188 _editor
.get_selection().clear_tracks ();
1190 _editor
.select_all_tracks ();
1196 switch (ArdourKeyboard::selection_type (ev
->state
)) {
1197 case Selection::Toggle
:
1198 _editor
.get_selection().toggle (this);
1201 case Selection::Set
:
1202 _editor
.get_selection().set (this);
1205 case Selection::Extend
:
1206 _editor
.extend_selection_to_track (*this);
1209 case Selection::Add
:
1210 _editor
.get_selection().add (this);
1216 RouteTimeAxisView::set_selected_points (PointSelection
& points
)
1218 for (Children::iterator i
= children
.begin(); i
!= children
.end(); ++i
) {
1219 (*i
)->set_selected_points (points
);
1224 RouteTimeAxisView::set_selected_regionviews (RegionSelection
& regions
)
1227 _view
->set_selected_regionviews (regions
);
1231 /** Add the selectable things that we have to a list.
1232 * @param results List to add things to.
1235 RouteTimeAxisView::get_selectables (framepos_t start
, framepos_t end
, double top
, double bot
, list
<Selectable
*>& results
)
1240 speed
= track()->speed();
1243 framepos_t
const start_adjusted
= session_frame_to_track_frame(start
, speed
);
1244 framepos_t
const end_adjusted
= session_frame_to_track_frame(end
, speed
);
1246 if ((_view
&& ((top
< 0.0 && bot
< 0.0))) || touched (top
, bot
)) {
1247 _view
->get_selectables (start_adjusted
, end_adjusted
, top
, bot
, results
);
1250 /* pick up visible automation tracks */
1252 for (Children::iterator i
= children
.begin(); i
!= children
.end(); ++i
) {
1253 if (!(*i
)->hidden()) {
1254 (*i
)->get_selectables (start_adjusted
, end_adjusted
, top
, bot
, results
);
1260 RouteTimeAxisView::get_inverted_selectables (Selection
& sel
, list
<Selectable
*>& results
)
1263 _view
->get_inverted_selectables (sel
, results
);
1266 for (Children::iterator i
= children
.begin(); i
!= children
.end(); ++i
) {
1267 if (!(*i
)->hidden()) {
1268 (*i
)->get_inverted_selectables (sel
, results
);
1276 RouteTimeAxisView::route_group () const
1278 return _route
->route_group();
1282 RouteTimeAxisView::name() const
1284 return _route
->name();
1287 boost::shared_ptr
<Playlist
>
1288 RouteTimeAxisView::playlist () const
1290 boost::shared_ptr
<Track
> tr
;
1292 if ((tr
= track()) != 0) {
1293 return tr
->playlist();
1295 return boost::shared_ptr
<Playlist
> ();
1300 RouteTimeAxisView::name_entry_changed ()
1302 string x
= name_entry
.get_text ();
1304 if (x
== _route
->name()) {
1308 strip_whitespace_edges (x
);
1310 if (x
.length() == 0) {
1311 name_entry
.set_text (_route
->name());
1315 if (_session
->route_name_internal (x
)) {
1316 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1318 name_entry
.grab_focus ();
1319 } else if (RouteUI::verify_new_route_name (x
)) {
1320 _route
->set_name (x
);
1322 name_entry
.grab_focus ();
1326 boost::shared_ptr
<Region
>
1327 RouteTimeAxisView::find_next_region (framepos_t pos
, RegionPoint point
, int32_t dir
)
1329 boost::shared_ptr
<Playlist
> pl
= playlist ();
1332 return pl
->find_next_region (pos
, point
, dir
);
1335 return boost::shared_ptr
<Region
> ();
1339 RouteTimeAxisView::find_next_region_boundary (framepos_t pos
, int32_t dir
)
1341 boost::shared_ptr
<Playlist
> pl
= playlist ();
1344 return pl
->find_next_region_boundary (pos
, dir
);
1351 RouteTimeAxisView::cut_copy_clear (Selection
& selection
, CutCopyOp op
)
1353 boost::shared_ptr
<Playlist
> what_we_got
;
1354 boost::shared_ptr
<Track
> tr
= track ();
1355 boost::shared_ptr
<Playlist
> playlist
;
1358 /* route is a bus, not a track */
1362 playlist
= tr
->playlist();
1364 TimeSelection
time (selection
.time
);
1365 float const speed
= tr
->speed();
1366 if (speed
!= 1.0f
) {
1367 for (TimeSelection::iterator i
= time
.begin(); i
!= time
.end(); ++i
) {
1368 (*i
).start
= session_frame_to_track_frame((*i
).start
, speed
);
1369 (*i
).end
= session_frame_to_track_frame((*i
).end
, speed
);
1373 playlist
->clear_changes ();
1374 playlist
->clear_owned_changes ();
1378 if (playlist
->cut (time
) != 0) {
1379 vector
<Command
*> cmds
;
1380 playlist
->rdiff (cmds
);
1381 _session
->add_commands (cmds
);
1383 _session
->add_command (new StatefulDiffCommand (playlist
));
1388 if ((what_we_got
= playlist
->cut (time
)) != 0) {
1389 _editor
.get_cut_buffer().add (what_we_got
);
1390 vector
<Command
*> cmds
;
1391 playlist
->rdiff (cmds
);
1392 _session
->add_commands (cmds
);
1394 _session
->add_command (new StatefulDiffCommand (playlist
));
1398 if ((what_we_got
= playlist
->copy (time
)) != 0) {
1399 _editor
.get_cut_buffer().add (what_we_got
);
1404 if ((what_we_got
= playlist
->cut (time
)) != 0) {
1406 vector
<Command
*> cmds
;
1407 playlist
->rdiff (cmds
);
1408 _session
->add_commands (cmds
);
1409 _session
->add_command (new StatefulDiffCommand (playlist
));
1410 what_we_got
->release ();
1417 RouteTimeAxisView::paste (framepos_t pos
, float times
, Selection
& selection
, size_t nth
)
1423 boost::shared_ptr
<Playlist
> pl
= playlist ();
1424 PlaylistSelection::iterator p
;
1426 for (p
= selection
.playlists
.begin(); p
!= selection
.playlists
.end() && nth
; ++p
, --nth
) {}
1428 if (p
== selection
.playlists
.end()) {
1432 DEBUG_TRACE (DEBUG::CutNPaste
, string_compose ("paste to %1\n", pos
));
1434 if (track()->speed() != 1.0f
) {
1435 pos
= session_frame_to_track_frame (pos
, track()->speed());
1436 DEBUG_TRACE (DEBUG::CutNPaste
, string_compose ("modified paste to %1\n", pos
));
1439 pl
->clear_changes ();
1440 pl
->paste (*p
, pos
, times
);
1441 _session
->add_command (new StatefulDiffCommand (pl
));
1447 struct PlaylistSorter
{
1448 bool operator() (boost::shared_ptr
<Playlist
> a
, boost::shared_ptr
<Playlist
> b
) const {
1449 return a
->sort_id() < b
->sort_id();
1454 RouteTimeAxisView::build_playlist_menu ()
1456 using namespace Menu_Helpers
;
1462 delete playlist_action_menu
;
1463 playlist_action_menu
= new Menu
;
1464 playlist_action_menu
->set_name ("ArdourContextMenu");
1466 MenuList
& playlist_items
= playlist_action_menu
->items();
1467 playlist_action_menu
->set_name ("ArdourContextMenu");
1468 playlist_items
.clear();
1470 vector
<boost::shared_ptr
<Playlist
> > playlists
, playlists_tr
;
1471 boost::shared_ptr
<Track
> tr
= track();
1472 RadioMenuItem::Group playlist_group
;
1474 _session
->playlists
->get (playlists
);
1476 /* find the playlists for this diskstream */
1477 for (vector
<boost::shared_ptr
<Playlist
> >::iterator i
= playlists
.begin(); i
!= playlists
.end(); ++i
) {
1478 if (((*i
)->get_orig_diskstream_id() == tr
->diskstream_id()) || (tr
->playlist()->id() == (*i
)->id())) {
1479 playlists_tr
.push_back(*i
);
1483 /* sort the playlists */
1485 sort (playlists_tr
.begin(), playlists_tr
.end(), cmp
);
1487 /* add the playlists to the menu */
1488 for (vector
<boost::shared_ptr
<Playlist
> >::iterator i
= playlists_tr
.begin(); i
!= playlists_tr
.end(); ++i
) {
1489 playlist_items
.push_back (RadioMenuElem (playlist_group
, (*i
)->name()));
1490 RadioMenuItem
*item
= static_cast<RadioMenuItem
*>(&playlist_items
.back());
1491 item
->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist
), item
, boost::weak_ptr
<Playlist
> (*i
)));
1493 if (tr
->playlist()->id() == (*i
)->id()) {
1499 playlist_items
.push_back (SeparatorElem());
1500 playlist_items
.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist
)));
1501 playlist_items
.push_back (SeparatorElem());
1503 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::edit
.property_id
)) {
1504 playlist_items
.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor
, &PublicEditor::new_playlists
), this)));
1505 playlist_items
.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor
, &PublicEditor::copy_playlists
), this)));
1508 // Use a label which tells the user what is happening
1509 playlist_items
.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor
, &PublicEditor::new_playlists
), this)));
1510 playlist_items
.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor
, &PublicEditor::copy_playlists
), this)));
1514 playlist_items
.push_back (SeparatorElem());
1515 playlist_items
.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor
, &PublicEditor::clear_playlists
), this)));
1516 playlist_items
.push_back (SeparatorElem());
1518 playlist_items
.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector
)));
1522 RouteTimeAxisView::use_playlist (RadioMenuItem
*item
, boost::weak_ptr
<Playlist
> wpl
)
1524 assert (is_track());
1526 // exit if we were triggered by deactivating the old playlist
1527 if (!item
->get_active()) {
1531 boost::shared_ptr
<Playlist
> pl (wpl
.lock());
1537 if (track()->playlist() == pl
) {
1538 // exit when use_playlist is called by the creation of the playlist menu
1539 // or the playlist choice is unchanged
1543 track()->use_playlist (pl
);
1545 RouteGroup
* rg
= route_group();
1547 if (rg
&& rg
->is_active() && rg
->enabled_property (ARDOUR::Properties::edit
.property_id
)) {
1548 std::string group_string
= "." + rg
->name() + ".";
1550 std::string take_name
= pl
->name();
1551 std::string::size_type idx
= take_name
.find(group_string
);
1553 if (idx
== std::string::npos
)
1556 take_name
= take_name
.substr(idx
+ group_string
.length()); // find the bit containing the take number / name
1558 boost::shared_ptr
<RouteList
> rl (rg
->route_list());
1560 for (RouteList::const_iterator i
= rl
->begin(); i
!= rl
->end(); ++i
) {
1561 if ( (*i
) == this->route()) {
1565 std::string playlist_name
= (*i
)->name()+group_string
+take_name
;
1567 boost::shared_ptr
<Track
> track
= boost::dynamic_pointer_cast
<Track
>(*i
);
1572 boost::shared_ptr
<Playlist
> ipl
= session()->playlists
->by_name(playlist_name
);
1574 // No playlist for this track for this take yet, make it
1575 track
->use_new_playlist();
1576 track
->playlist()->set_name(playlist_name
);
1578 track
->use_playlist(ipl
);
1585 RouteTimeAxisView::show_playlist_selector ()
1587 _editor
.playlist_selector().show_for (this);
1591 RouteTimeAxisView::map_frozen ()
1597 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen
)
1599 switch (track()->freeze_state()) {
1601 playlist_button
.set_sensitive (false);
1602 rec_enable_button
->set_sensitive (false);
1605 playlist_button
.set_sensitive (true);
1606 rec_enable_button
->set_sensitive (true);
1612 RouteTimeAxisView::color_handler ()
1614 //case cTimeStretchOutline:
1615 if (timestretch_rect
) {
1616 timestretch_rect
->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline
.get();
1618 //case cTimeStretchFill:
1619 if (timestretch_rect
) {
1620 timestretch_rect
->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill
.get();
1626 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1627 * Will add track if necessary.
1630 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter
& param
)
1632 boost::shared_ptr
<AutomationTimeAxisView
> track
= automation_child (param
);
1633 Gtk::CheckMenuItem
* menu
= automation_child_menu_item (param
);
1636 /* it doesn't exist yet, so we don't care about the button state: just add it */
1637 create_automation_child (param
, true);
1640 bool yn
= menu
->get_active();
1641 bool changed
= false;
1643 if ((changed
= track
->set_marked_for_display (menu
->get_active())) && yn
) {
1645 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1646 will have done that for us.
1649 if (changed
&& !no_redraw
) {
1657 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param
)
1659 boost::shared_ptr
<AutomationTimeAxisView
> track
= automation_child (param
);
1665 Gtk::CheckMenuItem
* menu
= automation_child_menu_item (param
);
1667 if (menu
&& !_hidden
) {
1668 ignore_toggle
= true;
1669 menu
->set_active (false);
1670 ignore_toggle
= false;
1673 if (_route
&& !no_redraw
) {
1680 RouteTimeAxisView::show_all_automation (bool apply_to_selection
)
1682 if (apply_to_selection
) {
1683 _editor
.get_selection().tracks
.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation
, _1
, false));
1687 /* Show our automation */
1689 for (AutomationTracks::iterator i
= _automation_tracks
.begin(); i
!= _automation_tracks
.end(); ++i
) {
1690 i
->second
->set_marked_for_display (true);
1692 Gtk::CheckMenuItem
* menu
= automation_child_menu_item (i
->first
);
1695 menu
->set_active(true);
1700 /* Show processor automation */
1702 for (list
<ProcessorAutomationInfo
*>::iterator i
= processor_automation
.begin(); i
!= processor_automation
.end(); ++i
) {
1703 for (vector
<ProcessorAutomationNode
*>::iterator ii
= (*i
)->lines
.begin(); ii
!= (*i
)->lines
.end(); ++ii
) {
1704 if ((*ii
)->view
== 0) {
1705 add_processor_automation_curve ((*i
)->processor
, (*ii
)->what
);
1708 (*ii
)->menu_item
->set_active (true);
1721 RouteTimeAxisView::show_existing_automation (bool apply_to_selection
)
1723 if (apply_to_selection
) {
1724 _editor
.get_selection().tracks
.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation
, _1
, false));
1728 /* Show our automation */
1730 for (AutomationTracks::iterator i
= _automation_tracks
.begin(); i
!= _automation_tracks
.end(); ++i
) {
1731 if (i
->second
->has_automation()) {
1732 i
->second
->set_marked_for_display (true);
1734 Gtk::CheckMenuItem
* menu
= automation_child_menu_item (i
->first
);
1736 menu
->set_active(true);
1741 /* Show processor automation */
1743 for (list
<ProcessorAutomationInfo
*>::iterator i
= processor_automation
.begin(); i
!= processor_automation
.end(); ++i
) {
1744 for (vector
<ProcessorAutomationNode
*>::iterator ii
= (*i
)->lines
.begin(); ii
!= (*i
)->lines
.end(); ++ii
) {
1745 if ((*ii
)->view
!= 0 && (*i
)->processor
->control((*ii
)->what
)->list()->size() > 0) {
1746 (*ii
)->menu_item
->set_active (true);
1758 RouteTimeAxisView::hide_all_automation (bool apply_to_selection
)
1760 if (apply_to_selection
) {
1761 _editor
.get_selection().tracks
.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation
, _1
, false));
1765 /* Hide our automation */
1767 for (AutomationTracks::iterator i
= _automation_tracks
.begin(); i
!= _automation_tracks
.end(); ++i
) {
1768 i
->second
->set_marked_for_display (false);
1770 Gtk::CheckMenuItem
* menu
= automation_child_menu_item (i
->first
);
1773 menu
->set_active (false);
1777 /* Hide processor automation */
1779 for (list
<ProcessorAutomationInfo
*>::iterator i
= processor_automation
.begin(); i
!= processor_automation
.end(); ++i
) {
1780 for (vector
<ProcessorAutomationNode
*>::iterator ii
= (*i
)->lines
.begin(); ii
!= (*i
)->lines
.end(); ++ii
) {
1781 (*ii
)->menu_item
->set_active (false);
1792 RouteTimeAxisView::region_view_added (RegionView
* rv
)
1794 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1795 for (Children::iterator i
= children
.begin(); i
!= children
.end(); ++i
) {
1796 boost::shared_ptr
<AutomationTimeAxisView
> atv
;
1798 if ((atv
= boost::dynamic_pointer_cast
<AutomationTimeAxisView
> (*i
)) != 0) {
1803 for (UnderlayMirrorList::iterator i
= _underlay_mirrors
.begin(); i
!= _underlay_mirrors
.end(); ++i
) {
1804 (*i
)->add_ghost(rv
);
1808 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1810 for (vector
<ProcessorAutomationNode
*>::iterator i
= lines
.begin(); i
!= lines
.end(); ++i
) {
1816 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1818 parent
.remove_processor_automation_node (this);
1822 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode
* pan
)
1825 remove_child (pan
->view
);
1829 RouteTimeAxisView::ProcessorAutomationNode
*
1830 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr
<Processor
> processor
, Evoral::Parameter what
)
1832 for (list
<ProcessorAutomationInfo
*>::iterator i
= processor_automation
.begin(); i
!= processor_automation
.end(); ++i
) {
1834 if ((*i
)->processor
== processor
) {
1836 for (vector
<ProcessorAutomationNode
*>::iterator ii
= (*i
)->lines
.begin(); ii
!= (*i
)->lines
.end(); ++ii
) {
1837 if ((*ii
)->what
== what
) {
1847 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
1849 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr
<Processor
> processor
, Evoral::Parameter what
)
1852 ProcessorAutomationNode
* pan
;
1854 if ((pan
= find_processor_automation_node (processor
, what
)) == 0) {
1855 /* session state may never have been saved with new plugin */
1856 error
<< _("programming error: ")
1857 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
1858 processor
->name(), what
.type(), (int) what
.channel(), what
.id() )
1868 boost::shared_ptr
<AutomationControl
> control
1869 = boost::dynamic_pointer_cast
<AutomationControl
>(processor
->control(what
, true));
1871 pan
->view
= boost::shared_ptr
<AutomationTimeAxisView
>(
1872 new AutomationTimeAxisView (_session
, _route
, processor
, control
, control
->parameter (),
1873 _editor
, *this, false, parent_canvas
,
1874 processor
->describe_parameter (what
), processor
->name()));
1876 pan
->view
->Hiding
.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden
), pan
, processor
));
1878 add_automation_child (control
->parameter(), pan
->view
, pan
->view
->marked_for_display ());
1881 _view
->foreach_regionview (sigc::mem_fun(*pan
->view
.get(), &TimeAxisView::add_ghost
));
1886 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode
* pan
, boost::shared_ptr
<Processor
> i
)
1889 pan
->menu_item
->set_active (false);
1898 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr
<Processor
> p
)
1900 boost::shared_ptr
<Processor
> processor (p
.lock ());
1902 if (!processor
|| boost::dynamic_pointer_cast
<Amp
> (processor
)) {
1903 /* The Amp processor is a special case and is dealt with separately */
1907 set
<Evoral::Parameter
> existing
;
1909 processor
->what_has_data (existing
);
1911 for (set
<Evoral::Parameter
>::iterator i
= existing
.begin(); i
!= existing
.end(); ++i
) {
1913 Evoral::Parameter
param (*i
);
1914 boost::shared_ptr
<AutomationLine
> al
;
1916 if ((al
= find_processor_automation_curve (processor
, param
)) != 0) {
1919 add_processor_automation_curve (processor
, param
);
1925 RouteTimeAxisView::add_automation_child (Evoral::Parameter param
, boost::shared_ptr
<AutomationTimeAxisView
> track
, bool show
)
1927 using namespace Menu_Helpers
;
1931 track
->Hiding
.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden
), param
));
1933 _automation_tracks
[param
] = track
;
1935 /* existing state overrides "show" argument */
1936 string s
= track
->gui_property ("visible");
1938 show
= string_is_affirmative (s
);
1941 /* this might or might not change the visibility status, so don't rely on it */
1942 track
->set_marked_for_display (show
);
1944 if (show
&& !no_redraw
) {
1948 if (!EventTypeMap::instance().is_midi_parameter(param
)) {
1949 /* MIDI-related parameters are always in the menu, there's no
1950 reason to rebuild the menu just because we added a automation
1951 lane for one of them. But if we add a non-MIDI automation
1952 lane, then we need to invalidate the display menu.
1954 delete display_menu
;
1960 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr
<Processor
> p
)
1962 boost::shared_ptr
<Processor
> processor (p
.lock ());
1964 if (!processor
|| !processor
->display_to_user ()) {
1968 /* we use this override to veto the Amp processor from the plugin menu,
1969 as its automation lane can be accessed using the special "Fader" menu
1973 if (boost::dynamic_pointer_cast
<Amp
> (processor
) != 0) {
1977 using namespace Menu_Helpers
;
1978 ProcessorAutomationInfo
*rai
;
1979 list
<ProcessorAutomationInfo
*>::iterator x
;
1981 const std::set
<Evoral::Parameter
>& automatable
= processor
->what_can_be_automated ();
1983 if (automatable
.empty()) {
1987 for (x
= processor_automation
.begin(); x
!= processor_automation
.end(); ++x
) {
1988 if ((*x
)->processor
== processor
) {
1993 if (x
== processor_automation
.end()) {
1995 rai
= new ProcessorAutomationInfo (processor
);
1996 processor_automation
.push_back (rai
);
2004 /* any older menu was deleted at the top of processors_changed()
2005 when we cleared the subplugin menu.
2008 rai
->menu
= manage (new Menu
);
2009 MenuList
& items
= rai
->menu
->items();
2010 rai
->menu
->set_name ("ArdourContextMenu");
2014 std::set
<Evoral::Parameter
> has_visible_automation
;
2015 AutomationTimeAxisView::what_has_visible_automation (processor
, has_visible_automation
);
2017 for (std::set
<Evoral::Parameter
>::const_iterator i
= automatable
.begin(); i
!= automatable
.end(); ++i
) {
2019 ProcessorAutomationNode
* pan
;
2020 CheckMenuItem
* mitem
;
2022 string name
= processor
->describe_parameter (*i
);
2024 items
.push_back (CheckMenuElem (name
));
2025 mitem
= dynamic_cast<CheckMenuItem
*> (&items
.back());
2027 _subplugin_menu_map
[*i
] = mitem
;
2029 if (has_visible_automation
.find((*i
)) != has_visible_automation
.end()) {
2030 mitem
->set_active(true);
2033 if ((pan
= find_processor_automation_node (processor
, *i
)) == 0) {
2037 pan
= new ProcessorAutomationNode (*i
, mitem
, *this);
2039 rai
->lines
.push_back (pan
);
2043 pan
->menu_item
= mitem
;
2047 mitem
->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled
), rai
, pan
));
2050 /* add the menu for this processor, because the subplugin
2051 menu is always cleared at the top of processors_changed().
2052 this is the result of some poor design in gtkmm and/or
2056 subplugin_menu
.items().push_back (MenuElem (processor
->name(), *rai
->menu
));
2061 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo
* rai
,
2062 RouteTimeAxisView::ProcessorAutomationNode
* pan
)
2064 bool showit
= pan
->menu_item
->get_active();
2065 bool redraw
= false;
2067 if (pan
->view
== 0 && showit
) {
2068 add_processor_automation_curve (rai
->processor
, pan
->what
);
2072 if (pan
->view
&& pan
->view
->set_marked_for_display (showit
)) {
2076 if (redraw
&& !no_redraw
) {
2082 RouteTimeAxisView::processors_changed (RouteProcessorChange c
)
2084 if (c
.type
== RouteProcessorChange::MeterPointChange
) {
2085 /* nothing to do if only the meter point has changed */
2089 using namespace Menu_Helpers
;
2091 for (list
<ProcessorAutomationInfo
*>::iterator i
= processor_automation
.begin(); i
!= processor_automation
.end(); ++i
) {
2092 (*i
)->valid
= false;
2095 setup_processor_menu_and_curves ();
2097 bool deleted_processor_automation
= false;
2099 for (list
<ProcessorAutomationInfo
*>::iterator i
= processor_automation
.begin(); i
!= processor_automation
.end(); ) {
2101 list
<ProcessorAutomationInfo
*>::iterator tmp
;
2109 processor_automation
.erase (i
);
2110 deleted_processor_automation
= true;
2117 if (deleted_processor_automation
&& !no_redraw
) {
2122 boost::shared_ptr
<AutomationLine
>
2123 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr
<Processor
> processor
, Evoral::Parameter what
)
2125 ProcessorAutomationNode
* pan
;
2127 if ((pan
= find_processor_automation_node (processor
, what
)) != 0) {
2133 return boost::shared_ptr
<AutomationLine
>();
2137 RouteTimeAxisView::reset_processor_automation_curves ()
2139 for (ProcessorAutomationCurves::iterator i
= processor_automation_curves
.begin(); i
!= processor_automation_curves
.end(); ++i
) {
2145 RouteTimeAxisView::update_rec_display ()
2147 RouteUI::update_rec_display ();
2148 name_entry
.set_sensitive (!_route
->record_enabled());
2152 RouteTimeAxisView::set_layer_display (LayerDisplay d
, bool apply_to_selection
)
2154 if (apply_to_selection
) {
2155 _editor
.get_selection().tracks
.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display
, _1
, d
, false));
2159 _view
->set_layer_display (d
);
2162 set_gui_property (X_("layer-display"), enum_2_string (d
));
2167 RouteTimeAxisView::layer_display () const
2170 return _view
->layer_display ();
2173 /* we don't know, since we don't have a _view, so just return something */
2179 boost::shared_ptr
<AutomationTimeAxisView
>
2180 RouteTimeAxisView::automation_child(Evoral::Parameter param
)
2182 AutomationTracks::iterator i
= _automation_tracks
.find(param
);
2183 if (i
!= _automation_tracks
.end()) {
2186 return boost::shared_ptr
<AutomationTimeAxisView
>();
2191 RouteTimeAxisView::fast_update ()
2193 gm
.get_level_meter().update_meters ();
2197 RouteTimeAxisView::hide_meter ()
2200 gm
.get_level_meter().hide_meters ();
2204 RouteTimeAxisView::show_meter ()
2210 RouteTimeAxisView::reset_meter ()
2212 if (Config
->get_show_track_meters()) {
2213 gm
.get_level_meter().setup_meters (height
-5);
2220 RouteTimeAxisView::clear_meter ()
2222 gm
.get_level_meter().clear_meters ();
2226 RouteTimeAxisView::meter_changed ()
2228 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed
)
2233 RouteTimeAxisView::io_changed (IOChange
/*change*/, void */
*src*/
)
2239 RouteTimeAxisView::build_underlay_menu(Gtk::Menu
* parent_menu
)
2241 using namespace Menu_Helpers
;
2243 if (!_underlay_streams
.empty()) {
2244 MenuList
& parent_items
= parent_menu
->items();
2245 Menu
* gs_menu
= manage (new Menu
);
2246 gs_menu
->set_name ("ArdourContextMenu");
2247 MenuList
& gs_items
= gs_menu
->items();
2249 parent_items
.push_back (MenuElem (_("Underlays"), *gs_menu
));
2251 for(UnderlayList::iterator it
= _underlay_streams
.begin(); it
!= _underlay_streams
.end(); ++it
) {
2252 gs_items
.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it
)->trackview().name()),
2253 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay
), *it
)));
2259 RouteTimeAxisView::set_underlay_state()
2261 if (!underlay_xml_node
) {
2265 XMLNodeList nlist
= underlay_xml_node
->children();
2266 XMLNodeConstIterator niter
;
2267 XMLNode
*child_node
;
2269 for (niter
= nlist
.begin(); niter
!= nlist
.end(); ++niter
) {
2270 child_node
= *niter
;
2272 if (child_node
->name() != "Underlay") {
2276 XMLProperty
* prop
= child_node
->property ("id");
2278 PBD::ID
id (prop
->value());
2280 RouteTimeAxisView
* v
= _editor
.get_route_view_by_route_id (id
);
2283 add_underlay(v
->view(), false);
2292 RouteTimeAxisView::add_underlay (StreamView
* v
, bool update_xml
)
2298 RouteTimeAxisView
& other
= v
->trackview();
2300 if (find(_underlay_streams
.begin(), _underlay_streams
.end(), v
) == _underlay_streams
.end()) {
2301 if (find(other
._underlay_mirrors
.begin(), other
._underlay_mirrors
.end(), this) != other
._underlay_mirrors
.end()) {
2302 fatal
<< _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg
;
2306 _underlay_streams
.push_back(v
);
2307 other
._underlay_mirrors
.push_back(this);
2309 v
->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost
));
2311 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2313 if (!underlay_xml_node
) {
2314 underlay_xml_node
= xml_node
->add_child("Underlays");
2317 XMLNode
* node
= underlay_xml_node
->add_child("Underlay");
2318 XMLProperty
* prop
= node
->add_property("id");
2319 prop
->set_value(v
->trackview().route()->id().to_s());
2326 RouteTimeAxisView::remove_underlay (StreamView
* v
)
2332 UnderlayList::iterator it
= find(_underlay_streams
.begin(), _underlay_streams
.end(), v
);
2333 RouteTimeAxisView
& other
= v
->trackview();
2335 if (it
!= _underlay_streams
.end()) {
2336 UnderlayMirrorList::iterator gm
= find(other
._underlay_mirrors
.begin(), other
._underlay_mirrors
.end(), this);
2338 if (gm
== other
._underlay_mirrors
.end()) {
2339 fatal
<< _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg
;
2343 v
->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost
));
2345 _underlay_streams
.erase(it
);
2346 other
._underlay_mirrors
.erase(gm
);
2348 if (underlay_xml_node
) {
2349 underlay_xml_node
->remove_nodes_and_delete("id", v
->trackview().route()->id().to_s());
2355 RouteTimeAxisView::set_button_names ()
2357 rec_enable_button_label
.set_text (_("r"));
2359 if (_route
&& _route
->solo_safe()) {
2360 solo_button_label
.set_text (X_("!"));
2362 if (Config
->get_solo_control_is_listen_control()) {
2363 switch (Config
->get_listen_position()) {
2364 case AfterFaderListen
:
2365 solo_button_label
.set_text (_("A"));
2367 case PreFaderListen
:
2368 solo_button_label
.set_text (_("P"));
2372 solo_button_label
.set_text (_("s"));
2375 mute_button_label
.set_text (_("m"));
2379 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param
)
2381 ParameterMenuMap::iterator i
= _main_automation_menu_map
.find (param
);
2382 if (i
!= _main_automation_menu_map
.end()) {
2386 i
= _subplugin_menu_map
.find (param
);
2387 if (i
!= _subplugin_menu_map
.end()) {
2395 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter
& param
, bool show
)
2397 boost::shared_ptr
<AutomationControl
> c
= _route
->gain_control();
2399 error
<< "Route has no gain automation, unable to add automation track view." << endmsg
;
2403 gain_track
.reset (new AutomationTimeAxisView (_session
,
2404 _route
, _route
->amp(), c
, param
,
2409 _route
->amp()->describe_parameter(param
)));
2412 _view
->foreach_regionview (sigc::mem_fun (*gain_track
.get(), &TimeAxisView::add_ghost
));
2415 add_automation_child (Evoral::Parameter(GainAutomation
), gain_track
, show
);
2419 void add_region_to_list (RegionView
* rv
, Playlist::RegionList
* l
)
2421 l
->push_back (rv
->region());
2425 RouteTimeAxisView::combine_regions ()
2427 /* as of may 2011, we do not offer uncombine for MIDI tracks
2430 if (!is_audio_track()) {
2438 Playlist::RegionList selected_regions
;
2439 boost::shared_ptr
<Playlist
> playlist
= track()->playlist();
2441 _view
->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list
), &selected_regions
));
2443 if (selected_regions
.size() < 2) {
2447 playlist
->clear_changes ();
2448 boost::shared_ptr
<Region
> compound_region
= playlist
->combine (selected_regions
);
2450 _session
->add_command (new StatefulDiffCommand (playlist
));
2451 /* make the new region be selected */
2453 return _view
->find_view (compound_region
);
2457 RouteTimeAxisView::uncombine_regions ()
2459 /* as of may 2011, we do not offer uncombine for MIDI tracks
2461 if (!is_audio_track()) {
2469 Playlist::RegionList selected_regions
;
2470 boost::shared_ptr
<Playlist
> playlist
= track()->playlist();
2472 /* have to grab selected regions first because the uncombine is going
2473 * to change that in the middle of the list traverse
2476 _view
->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list
), &selected_regions
));
2478 playlist
->clear_changes ();
2480 for (Playlist::RegionList::iterator i
= selected_regions
.begin(); i
!= selected_regions
.end(); ++i
) {
2481 playlist
->uncombine (*i
);
2484 _session
->add_command (new StatefulDiffCommand (playlist
));
2488 RouteTimeAxisView::state_id() const
2490 return string_compose ("rtav %1", _route
->id().to_s());