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/route_group.h"
57 #include "ardour/session.h"
58 #include "ardour/session_playlist.h"
59 #include "ardour/debug.h"
60 #include "ardour/utils.h"
61 #include "evoral/Parameter.hpp"
63 #include "ardour_ui.h"
65 #include "global_signals.h"
66 #include "route_time_axis.h"
67 #include "automation_time_axis.h"
68 #include "canvas_impl.h"
69 #include "crossfade_view.h"
71 #include "gui_thread.h"
73 #include "playlist_selector.h"
74 #include "point_selection.h"
76 #include "public_editor.h"
77 #include "region_view.h"
78 #include "rgb_macros.h"
79 #include "selection.h"
80 #include "simplerect.h"
81 #include "streamview.h"
83 #include "route_group_menu.h"
85 #include "ardour/track.h"
89 using namespace ARDOUR
;
91 using namespace Gtkmm2ext
;
93 using namespace Editing
;
96 Glib::RefPtr
<Gdk::Pixbuf
> RouteTimeAxisView::slider
;
99 RouteTimeAxisView::setup_slider_pix ()
101 if ((slider
= ::get_icon ("fader_belt_h")) == 0) {
102 throw failed_constructor ();
106 RouteTimeAxisView::RouteTimeAxisView (PublicEditor
& ed
, Session
* sess
, boost::shared_ptr
<Route
> rt
, Canvas
& canvas
)
109 , TimeAxisView(sess
,ed
,(TimeAxisView
*) 0, canvas
)
110 , parent_canvas (canvas
)
111 , button_table (3, 3)
112 , route_group_button (_("g"))
113 , playlist_button (_("p"))
114 , automation_button (_("a"))
115 , gm (sess
, slider
, true, 115)
117 gm
.set_controls (_route
, _route
->shared_peak_meter(), _route
->amp());
118 gm
.get_level_meter().set_no_show_all();
119 gm
.get_level_meter().setup_meters(50);
122 playlist_action_menu
= 0;
123 automation_action_menu
= 0;
124 plugins_submenu_item
= 0;
128 if (!_route
->is_hidden()) {
129 _marked_for_display
= true;
133 update_solo_display ();
135 timestretch_rect
= 0;
138 ignore_toggle
= false;
140 route_group_button
.set_name ("TrackGroupButton");
141 playlist_button
.set_name ("TrackPlaylistButton");
142 automation_button
.set_name ("TrackAutomationButton");
144 route_group_button
.unset_flags (Gtk::CAN_FOCUS
);
145 playlist_button
.unset_flags (Gtk::CAN_FOCUS
);
146 automation_button
.unset_flags (Gtk::CAN_FOCUS
);
148 route_group_button
.signal_button_release_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click
), false);
149 playlist_button
.signal_clicked().connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click
));
150 automation_button
.signal_clicked().connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click
));
156 rec_enable_button
->remove ();
158 switch (track()->mode()) {
160 case ARDOUR::NonLayered
:
161 rec_enable_button
->add (*(manage (new Image (::get_icon (X_("record_normal_red"))))));
163 case ARDOUR::Destructive
:
164 rec_enable_button
->add (*(manage (new Image (::get_icon (X_("record_tape_red"))))));
167 rec_enable_button
->show_all ();
169 controls_table
.attach (*rec_enable_button
, 5, 6, 0, 1, Gtk::FILL
|Gtk::EXPAND
, Gtk::FILL
|Gtk::EXPAND
, 0, 0);
171 if (is_midi_track()) {
172 ARDOUR_UI::instance()->set_tip(*rec_enable_button
, _("Record (Right-click for Step Edit)"));
174 ARDOUR_UI::instance()->set_tip(*rec_enable_button
, _("Record"));
177 rec_enable_button
->set_sensitive (_session
->writable());
180 controls_hbox
.pack_start(gm
.get_level_meter(), false, false);
181 _route
->meter_change
.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed
, this), gui_context());
182 _route
->input()->changed
.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::io_changed
, this, _1
, _2
), gui_context());
183 _route
->output()->changed
.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::io_changed
, this, _1
, _2
), gui_context());
185 controls_table
.attach (*mute_button
, 6, 7, 0, 1, Gtk::FILL
|Gtk::EXPAND
, Gtk::FILL
|Gtk::EXPAND
, 0, 0);
187 if (!_route
->is_master()) {
188 controls_table
.attach (*solo_button
, 7, 8, 0, 1, Gtk::FILL
|Gtk::EXPAND
, Gtk::FILL
|Gtk::EXPAND
, 0, 0);
191 controls_table
.attach (route_group_button
, 7, 8, 1, 2, Gtk::FILL
|Gtk::EXPAND
, Gtk::FILL
|Gtk::EXPAND
, 0, 0);
192 controls_table
.attach (gm
.get_gain_slider(), 0, 5, 1, 2, Gtk::SHRINK
, Gtk::SHRINK
, 0, 0);
194 ARDOUR_UI::instance()->set_tip(*solo_button
,_("Solo"));
195 ARDOUR_UI::instance()->set_tip(*mute_button
,_("Mute"));
196 ARDOUR_UI::instance()->set_tip(route_group_button
, _("Route Group"));
197 ARDOUR_UI::instance()->set_tip(playlist_button
,_("Playlist"));
198 ARDOUR_UI::instance()->set_tip(automation_button
, _("Automation"));
202 controls_table
.attach (automation_button
, 6, 7, 1, 2, Gtk::FILL
|Gtk::EXPAND
, Gtk::FILL
|Gtk::EXPAND
);
204 if (is_track() && track()->mode() == ARDOUR::Normal
) {
205 controls_table
.attach (playlist_button
, 5, 6, 1, 2, Gtk::FILL
|Gtk::EXPAND
, Gtk::FILL
|Gtk::EXPAND
);
210 _route
->processors_changed
.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::processors_changed
, this, _1
), gui_context());
211 _route
->PropertyChanged
.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::route_property_changed
, this, _1
), gui_context());
215 track()->FreezeChange
.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen
, this), gui_context());
216 track()->SpeedChanged
.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed
, this), gui_context());
218 /* pick up the correct freeze state */
223 _editor
.ZoomChanged
.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_unit
));
224 _editor
.HorizontalPositionChanged
.connect (sigc::mem_fun (*this, &RouteTimeAxisView::horizontal_position_changed
));
225 ColorsChanged
.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler
));
227 PropertyList
* plist
= new PropertyList();
229 plist
->add (ARDOUR::Properties::edit
, true);
230 plist
->add (ARDOUR::Properties::mute
, true);
231 plist
->add (ARDOUR::Properties::solo
, true);
233 route_group_menu
= new RouteGroupMenu (_session
, plist
);
235 gm
.get_gain_slider().signal_scroll_event().connect(sigc::mem_fun(*this, &RouteTimeAxisView::controls_ebox_scroll
), false);
236 gm
.get_gain_slider().set_name ("TrackGainFader");
242 RouteTimeAxisView::~RouteTimeAxisView ()
244 CatchDeletion (this);
246 for (list
<ProcessorAutomationInfo
*>::iterator i
= processor_automation
.begin(); i
!= processor_automation
.end(); ++i
) {
250 delete playlist_action_menu
;
251 playlist_action_menu
= 0;
256 _automation_tracks
.clear ();
258 delete route_group_menu
;
262 RouteTimeAxisView::post_construct ()
264 /* map current state of the route */
266 update_diskstream_display ();
268 _subplugin_menu_map
.clear ();
269 subplugin_menu
.items().clear ();
270 _route
->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu
));
271 _route
->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves
));
272 reset_processor_automation_curves ();
276 RouteTimeAxisView::route_group_click (GdkEventButton
*ev
)
278 if (Keyboard::modifier_state_equals (ev
->state
, Keyboard::PrimaryModifier
)) {
279 if (_route
->route_group()) {
280 _route
->route_group()->remove (_route
);
286 r
.push_back (route ());
288 route_group_menu
->build (r
);
289 route_group_menu
->menu()->popup (ev
->button
, ev
->time
);
295 RouteTimeAxisView::playlist_changed ()
301 RouteTimeAxisView::label_view ()
303 string x
= _route
->name();
305 if (x
!= name_entry
.get_text()) {
306 name_entry
.set_text (x
);
309 if (x
!= name_label
.get_text()) {
310 name_label
.set_text (x
);
313 ARDOUR_UI::instance()->set_tip (name_entry
, x
);
317 RouteTimeAxisView::route_property_changed (const PropertyChange
& what_changed
)
319 if (what_changed
.contains (ARDOUR::Properties::name
)) {
325 RouteTimeAxisView::take_name_changed (void *src
)
333 RouteTimeAxisView::playlist_click ()
335 build_playlist_menu ();
336 conditionally_add_to_selection ();
337 playlist_action_menu
->popup (1, gtk_get_current_event_time());
341 RouteTimeAxisView::automation_click ()
343 conditionally_add_to_selection ();
344 build_automation_action_menu (false);
345 automation_action_menu
->popup (1, gtk_get_current_event_time());
349 RouteTimeAxisView::set_state (const XMLNode
& node
, int version
)
351 TimeAxisView::set_state (node
, version
);
353 XMLNodeList kids
= node
.children();
354 XMLNodeConstIterator iter
;
355 const XMLProperty
* prop
;
357 if (_view
&& (prop
= node
.property ("layer-display"))) {
358 set_layer_display (LayerDisplay (string_2_enum (prop
->value(), _view
->layer_display ())));
361 for (iter
= kids
.begin(); iter
!= kids
.end(); ++iter
) {
362 if ((*iter
)->name() == AutomationTimeAxisView::state_node_name
) {
363 if ((prop
= (*iter
)->property ("automation-id")) != 0) {
365 Evoral::Parameter param
= ARDOUR::EventTypeMap::instance().new_parameter(prop
->value());
366 bool show
= ((prop
= (*iter
)->property ("shown")) != 0) && string_is_affirmative (prop
->value());
367 create_automation_child(param
, show
);
369 warning
<< "Automation child has no ID" << endmsg
;
378 RouteTimeAxisView::build_automation_action_menu (bool for_selection
)
380 using namespace Menu_Helpers
;
382 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
383 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
386 detach_menu (subplugin_menu
);
388 _main_automation_menu_map
.clear ();
389 delete automation_action_menu
;
390 automation_action_menu
= new Menu
;
392 MenuList
& items
= automation_action_menu
->items();
394 automation_action_menu
->set_name ("ArdourContextMenu");
396 items
.push_back (MenuElem (_("Show All Automation"),
397 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation
), for_selection
)));
399 items
.push_back (MenuElem (_("Show Existing Automation"),
400 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation
), for_selection
)));
402 items
.push_back (MenuElem (_("Hide All Automation"),
403 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation
), for_selection
)));
405 items
.push_back (SeparatorElem ());
407 /* Attach the plugin submenu. It may have previously been used elsewhere,
408 so it was detached above */
410 items
.push_back (MenuElem (_("Plugins"), subplugin_menu
));
411 items
.back().set_sensitive (!subplugin_menu
.items().empty() && (!for_selection
|| _editor
.get_selection().tracks
.size() == 1));;
415 RouteTimeAxisView::build_display_menu ()
417 using namespace Menu_Helpers
;
421 TimeAxisView::build_display_menu ();
423 /* now fill it with our stuff */
425 MenuList
& items
= display_menu
->items();
426 display_menu
->set_name ("ArdourContextMenu");
428 items
.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color
)));
431 detach_menu (*_size_menu
);
434 items
.push_back (MenuElem (_("Height"), *_size_menu
));
436 items
.push_back (SeparatorElem());
438 if (!Profile
->get_sae()) {
439 items
.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog
)));
440 items
.back().set_sensitive (_editor
.get_selection().tracks
.size() <= 1);
441 items
.push_back (SeparatorElem());
444 // Hook for derived classes to add type specific stuff
445 append_extra_display_menu_items ();
449 Menu
* layers_menu
= manage (new Menu
);
450 MenuList
&layers_items
= layers_menu
->items();
451 layers_menu
->set_name("ArdourContextMenu");
453 RadioMenuItem::Group layers_group
;
455 /* Find out how many overlaid/stacked tracks we have in the selection */
459 TrackSelection
const & s
= _editor
.get_selection().tracks
;
460 for (TrackSelection::const_iterator i
= s
.begin(); i
!= s
.end(); ++i
) {
461 StreamView
* v
= (*i
)->view ();
466 switch (v
->layer_display ()) {
476 /* We're not connecting to signal_toggled() here; in the case where these two items are
477 set to be in the `inconsistent' state, it seems that one or other will end up active
478 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
479 select the active one, no toggled signal is emitted so nothing happens.
482 layers_items
.push_back (RadioMenuElem (layers_group
, _("Overlaid")));
483 RadioMenuItem
* i
= dynamic_cast<RadioMenuItem
*> (&layers_items
.back ());
484 i
->set_active (overlaid
!= 0 && stacked
== 0);
485 i
->set_inconsistent (overlaid
!= 0 && stacked
!= 0);
486 i
->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display
), Overlaid
, true));
488 layers_items
.push_back (
489 RadioMenuElem (layers_group
, _("Stacked"),
490 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display
), Stacked
, true))
493 i
= dynamic_cast<RadioMenuItem
*> (&layers_items
.back ());
494 i
->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display
), Stacked
, true));
495 i
->set_active (overlaid
== 0 && stacked
!= 0);
496 i
->set_inconsistent (overlaid
!= 0 && stacked
!= 0);
498 items
.push_back (MenuElem (_("Layers"), *layers_menu
));
500 if (!Profile
->get_sae()) {
502 Menu
* alignment_menu
= manage (new Menu
);
503 MenuList
& alignment_items
= alignment_menu
->items();
504 alignment_menu
->set_name ("ArdourContextMenu");
506 RadioMenuItem::Group align_group
;
508 /* Same verbose hacks as for the layering options above */
514 boost::shared_ptr
<Track
> first_track
;
516 TrackSelection
const & s
= _editor
.get_selection().tracks
;
517 for (TrackSelection::const_iterator i
= s
.begin(); i
!= s
.end(); ++i
) {
518 RouteTimeAxisView
* r
= dynamic_cast<RouteTimeAxisView
*> (*i
);
519 if (!r
|| !r
->is_track ()) {
524 first_track
= r
->track();
527 switch (r
->track()->alignment_choice()) {
531 switch (r
->track()->alignment_style()) {
532 case ExistingMaterial
:
540 case UseExistingMaterial
:
556 inconsistent
= false;
565 if (!inconsistent
&& first_track
) {
567 alignment_items
.push_back (RadioMenuElem (align_group
, _("Automatic (based on I/O connections)")));
568 i
= dynamic_cast<RadioMenuItem
*> (&alignment_items
.back());
569 i
->set_active (automatic
!= 0 && existing
== 0 && capture
== 0);
570 i
->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice
), i
, Automatic
, true));
572 switch (first_track
->alignment_choice()) {
574 switch (first_track
->alignment_style()) {
575 case ExistingMaterial
:
576 alignment_items
.push_back (MenuElem (_("(Currently: Existing Material)")));
579 alignment_items
.push_back (MenuElem (_("(Currently: Capture Time)")));
587 alignment_items
.push_back (RadioMenuElem (align_group
, _("Align With Existing Material")));
588 i
= dynamic_cast<RadioMenuItem
*> (&alignment_items
.back());
589 i
->set_active (existing
!= 0 && capture
== 0 && automatic
== 0);
590 i
->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice
), i
, UseExistingMaterial
, true));
592 alignment_items
.push_back (RadioMenuElem (align_group
, _("Align With Capture Time")));
593 i
= dynamic_cast<RadioMenuItem
*> (&alignment_items
.back());
594 i
->set_active (existing
== 0 && capture
!= 0 && automatic
== 0);
595 i
->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice
), i
, UseCaptureTime
, true));
597 items
.push_back (MenuElem (_("Alignment"), *alignment_menu
));
603 Menu
* mode_menu
= manage (new Menu
);
604 MenuList
& mode_items
= mode_menu
->items ();
605 mode_menu
->set_name ("ArdourContextMenu");
607 RadioMenuItem::Group mode_group
;
613 for (TrackSelection::const_iterator i
= s
.begin(); i
!= s
.end(); ++i
) {
614 RouteTimeAxisView
* r
= dynamic_cast<RouteTimeAxisView
*> (*i
);
615 if (!r
|| !r
->is_track ()) {
619 switch (r
->track()->mode()) {
632 mode_items
.push_back (RadioMenuElem (mode_group
, _("Normal 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::Normal
, true));
635 i
->set_active (normal
!= 0 && tape
== 0 && non_layered
== 0);
636 i
->set_inconsistent (normal
!= 0 && (tape
!= 0 || non_layered
!= 0));
638 mode_items
.push_back (RadioMenuElem (mode_group
, _("Tape Mode")));
639 i
= dynamic_cast<RadioMenuItem
*> (&mode_items
.back ());
640 i
->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode
), ARDOUR::Destructive
, true));
641 i
->set_active (normal
== 0 && tape
!= 0 && non_layered
== 0);
642 i
->set_inconsistent (tape
!= 0 && (normal
!= 0 || non_layered
!= 0));
644 mode_items
.push_back (RadioMenuElem (mode_group
, _("Non-Layered Mode")));
645 i
= dynamic_cast<RadioMenuItem
*> (&mode_items
.back ());
646 i
->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode
), ARDOUR::NonLayered
, true));
647 i
->set_active (normal
== 0 && tape
== 0 && non_layered
!= 0);
648 i
->set_inconsistent (non_layered
!= 0 && (normal
!= 0 || tape
!= 0));
650 items
.push_back (MenuElem (_("Mode"), *mode_menu
));
653 color_mode_menu
= build_color_mode_menu();
654 if (color_mode_menu
) {
655 items
.push_back (MenuElem (_("Color Mode"), *color_mode_menu
));
658 items
.push_back (SeparatorElem());
660 build_playlist_menu ();
661 items
.push_back (MenuElem (_("Playlist"), *playlist_action_menu
));
662 items
.back().set_sensitive (_editor
.get_selection().tracks
.size() <= 1);
664 route_group_menu
->detach ();
667 for (TrackSelection::iterator i
= _editor
.get_selection().tracks
.begin(); i
!= _editor
.get_selection().tracks
.end(); ++i
) {
668 RouteTimeAxisView
* rtv
= dynamic_cast<RouteTimeAxisView
*> (*i
);
670 r
.push_back (rtv
->route ());
675 r
.push_back (route ());
678 route_group_menu
->build (r
);
679 items
.push_back (MenuElem (_("Route Group"), *route_group_menu
->menu ()));
681 build_automation_action_menu (true);
682 items
.push_back (MenuElem (_("Automation"), *automation_action_menu
));
684 items
.push_back (SeparatorElem());
689 TrackSelection
const & s
= _editor
.get_selection().tracks
;
690 for (TrackSelection::const_iterator i
= s
.begin(); i
!= s
.end(); ++i
) {
691 RouteTimeAxisView
* r
= dynamic_cast<RouteTimeAxisView
*> (*i
);
696 if (r
->route()->active()) {
703 items
.push_back (CheckMenuElem (_("Active")));
704 CheckMenuItem
* i
= dynamic_cast<CheckMenuItem
*> (&items
.back());
705 bool click_sets_active
= true;
706 if (active
> 0 && inactive
== 0) {
707 i
->set_active (true);
708 click_sets_active
= false;
709 } else if (active
> 0 && inactive
> 0) {
710 i
->set_inconsistent (true);
712 i
->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active
), click_sets_active
, true));
714 items
.push_back (SeparatorElem());
715 items
.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor
, &PublicEditor::hide_track_in_display
), this, true)));
716 if (!Profile
->get_sae()) {
717 items
.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route
), true)));
719 items
.push_front (SeparatorElem());
720 items
.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route
), true)));
725 RouteTimeAxisView::set_track_mode (TrackMode mode
, bool apply_to_selection
)
727 if (apply_to_selection
) {
728 _editor
.get_selection().tracks
.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode
, _1
, mode
, false));
733 if (!track()->can_use_mode (mode
, needs_bounce
)) {
739 cerr
<< "would bounce this one\n";
744 track()->set_mode (mode
);
746 rec_enable_button
->remove ();
749 case ARDOUR::NonLayered
:
751 rec_enable_button
->add (*(manage (new Image (::get_icon (X_("record_normal_red"))))));
753 case ARDOUR::Destructive
:
754 rec_enable_button
->add (*(manage (new Image (::get_icon (X_("record_tape_red"))))));
758 rec_enable_button
->show_all ();
763 RouteTimeAxisView::show_timestretch (framepos_t start
, framepos_t end
)
769 TimeAxisView::show_timestretch (start
, end
);
779 /* check that the time selection was made in our route, or our route group.
780 remember that route_group() == 0 implies the route is *not* in a edit group.
783 if (!(ts
.track
== this || (ts
.group
!= 0 && ts
.group
== _route
->route_group()))) {
784 /* this doesn't apply to us */
788 /* ignore it if our edit group is not active */
790 if ((ts
.track
!= this) && _route
->route_group() && !_route
->route_group()->is_active()) {
795 if (timestretch_rect
== 0) {
796 timestretch_rect
= new SimpleRect (*canvas_display ());
797 timestretch_rect
->property_x1() = 0.0;
798 timestretch_rect
->property_y1() = 0.0;
799 timestretch_rect
->property_x2() = 0.0;
800 timestretch_rect
->property_y2() = 0.0;
801 timestretch_rect
->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill
.get();
802 timestretch_rect
->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline
.get();
805 timestretch_rect
->show ();
806 timestretch_rect
->raise_to_top ();
808 x1
= start
/ _editor
.get_current_zoom();
809 x2
= (end
- 1) / _editor
.get_current_zoom();
810 y2
= current_height() - 2;
812 timestretch_rect
->property_x1() = x1
;
813 timestretch_rect
->property_y1() = 1.0;
814 timestretch_rect
->property_x2() = x2
;
815 timestretch_rect
->property_y2() = y2
;
819 RouteTimeAxisView::hide_timestretch ()
821 TimeAxisView::hide_timestretch ();
823 if (timestretch_rect
) {
824 timestretch_rect
->hide ();
829 RouteTimeAxisView::show_selection (TimeSelection
& ts
)
833 /* ignore it if our edit group is not active or if the selection was started
834 in some other track or route group (remember that route_group() == 0 means
835 that the track is not in an route group).
838 if (((ts
.track
!= this && !is_child (ts
.track
)) && _route
->route_group() && !_route
->route_group()->is_active()) ||
839 (!(ts
.track
== this || is_child (ts
.track
) || (ts
.group
!= 0 && ts
.group
== _route
->route_group())))) {
845 TimeAxisView::show_selection (ts
);
849 RouteTimeAxisView::set_height (uint32_t h
)
852 bool height_changed
= (height
== 0) || (h
!= height
);
853 gm
.get_level_meter().setup_meters (gmlen
);
855 TimeAxisView::set_height (h
);
860 _view
->set_height ((double) current_height());
864 snprintf (buf
, sizeof (buf
), "%u", height
);
865 xml_node
->add_property ("height", buf
);
867 if (height
>= preset_height (HeightNormal
)) {
869 controls_table
.set_border_width (2);
873 gm
.get_gain_slider().show();
875 if (!_route
|| _route
->is_monitor()) {
880 if (rec_enable_button
)
881 rec_enable_button
->show();
883 route_group_button
.show();
884 automation_button
.show();
886 if (is_track() && track()->mode() == ARDOUR::Normal
) {
887 playlist_button
.show();
890 } else if (height
>= preset_height (HeightSmaller
)) {
892 controls_table
.set_border_width (2);
896 gm
.get_gain_slider().hide();
898 if (!_route
|| _route
->is_monitor()) {
903 if (rec_enable_button
)
904 rec_enable_button
->show();
906 route_group_button
.hide ();
907 automation_button
.hide ();
909 if (is_track() && track()->mode() == ARDOUR::Normal
) {
910 playlist_button
.hide ();
915 controls_table
.set_border_width (0);
919 if (height_changed
&& !no_redraw
) {
920 /* only emit the signal if the height really changed */
921 _route
->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
926 RouteTimeAxisView::set_color (Gdk::Color
const & c
)
928 RouteUI::set_color (c
);
931 _view
->apply_color (_color
, StreamView::RegionColor
);
936 RouteTimeAxisView::reset_samples_per_unit ()
938 set_samples_per_unit (_editor
.get_current_zoom());
942 RouteTimeAxisView::horizontal_position_changed ()
945 _view
->horizontal_position_changed ();
950 RouteTimeAxisView::set_samples_per_unit (double spu
)
955 speed
= track()->speed();
959 _view
->set_samples_per_unit (spu
* speed
);
962 TimeAxisView::set_samples_per_unit (spu
* speed
);
966 RouteTimeAxisView::set_align_choice (RadioMenuItem
* mitem
, AlignChoice choice
, bool apply_to_selection
)
968 if (!mitem
->get_active()) {
969 /* this is one of the two calls made when these radio menu items change status. this one
970 is for the item that became inactive, and we want to ignore it.
975 if (apply_to_selection
) {
976 _editor
.get_selection().tracks
.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice
, _1
, mitem
, choice
, false));
979 track()->set_align_choice (choice
);
985 RouteTimeAxisView::rename_current_playlist ()
987 ArdourPrompter
prompter (true);
990 boost::shared_ptr
<Track
> tr
= track();
991 if (!tr
|| tr
->destructive()) {
995 boost::shared_ptr
<Playlist
> pl
= tr
->playlist();
1000 prompter
.set_title (_("Rename Playlist"));
1001 prompter
.set_prompt (_("New name for playlist:"));
1002 prompter
.set_initial_text (pl
->name());
1003 prompter
.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT
);
1004 prompter
.set_response_sensitive (Gtk::RESPONSE_ACCEPT
, false);
1006 switch (prompter
.run ()) {
1007 case Gtk::RESPONSE_ACCEPT
:
1008 prompter
.get_result (name
);
1009 if (name
.length()) {
1010 pl
->set_name (name
);
1020 RouteTimeAxisView::resolve_new_group_playlist_name(std::string
&basename
, vector
<boost::shared_ptr
<Playlist
> > const & playlists
)
1022 std::string
ret (basename
);
1024 std::string
const group_string
= "." + route_group()->name() + ".";
1026 // iterate through all playlists
1028 for (vector
<boost::shared_ptr
<Playlist
> >::const_iterator i
= playlists
.begin(); i
!= playlists
.end(); ++i
) {
1029 std::string tmp
= (*i
)->name();
1031 std::string::size_type idx
= tmp
.find(group_string
);
1032 // find those which belong to this group
1033 if (idx
!= string::npos
) {
1034 tmp
= tmp
.substr(idx
+ group_string
.length());
1036 // and find the largest current number
1037 int x
= atoi(tmp
.c_str());
1038 if (x
> maxnumber
) {
1047 snprintf (buf
, sizeof(buf
), "%d", maxnumber
);
1049 ret
= this->name() + "." + route_group()->name () + "." + buf
;
1055 RouteTimeAxisView::use_copy_playlist (bool prompt
, vector
<boost::shared_ptr
<Playlist
> > const & playlists_before_op
)
1059 boost::shared_ptr
<Track
> tr
= track ();
1060 if (!tr
|| tr
->destructive()) {
1064 boost::shared_ptr
<const Playlist
> pl
= tr
->playlist();
1071 if (route_group() && route_group()->is_active()) {
1072 name
= resolve_new_group_playlist_name(name
, playlists_before_op
);
1075 while (_session
->playlists
->by_name(name
)) {
1076 name
= Playlist::bump_name (name
, *_session
);
1079 // TODO: The prompter "new" button should be de-activated if the user
1080 // specifies a playlist name which already exists in the session.
1084 ArdourPrompter
prompter (true);
1086 prompter
.set_title (_("New Copy Playlist"));
1087 prompter
.set_prompt (_("Name for new playlist:"));
1088 prompter
.set_initial_text (name
);
1089 prompter
.add_button (Gtk::Stock::NEW
, Gtk::RESPONSE_ACCEPT
);
1090 prompter
.set_response_sensitive (Gtk::RESPONSE_ACCEPT
, true);
1091 prompter
.show_all ();
1093 switch (prompter
.run ()) {
1094 case Gtk::RESPONSE_ACCEPT
:
1095 prompter
.get_result (name
);
1103 if (name
.length()) {
1104 tr
->use_copy_playlist ();
1105 tr
->playlist()->set_name (name
);
1110 RouteTimeAxisView::use_new_playlist (bool prompt
, vector
<boost::shared_ptr
<Playlist
> > const & playlists_before_op
)
1114 boost::shared_ptr
<Track
> tr
= track ();
1115 if (!tr
|| tr
->destructive()) {
1119 boost::shared_ptr
<const Playlist
> pl
= tr
->playlist();
1126 if (route_group() && route_group()->is_active()) {
1127 name
= resolve_new_group_playlist_name(name
,playlists_before_op
);
1130 while (_session
->playlists
->by_name(name
)) {
1131 name
= Playlist::bump_name (name
, *_session
);
1137 ArdourPrompter
prompter (true);
1139 prompter
.set_title (_("New Playlist"));
1140 prompter
.set_prompt (_("Name for new playlist:"));
1141 prompter
.set_initial_text (name
);
1142 prompter
.add_button (Gtk::Stock::NEW
, Gtk::RESPONSE_ACCEPT
);
1143 prompter
.set_response_sensitive (Gtk::RESPONSE_ACCEPT
, true);
1145 switch (prompter
.run ()) {
1146 case Gtk::RESPONSE_ACCEPT
:
1147 prompter
.get_result (name
);
1155 if (name
.length()) {
1156 tr
->use_new_playlist ();
1157 tr
->playlist()->set_name (name
);
1162 RouteTimeAxisView::clear_playlist ()
1164 boost::shared_ptr
<Track
> tr
= track ();
1165 if (!tr
|| tr
->destructive()) {
1169 boost::shared_ptr
<Playlist
> pl
= tr
->playlist();
1174 _editor
.clear_playlist (pl
);
1178 RouteTimeAxisView::speed_changed ()
1180 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_unit
, this));
1184 RouteTimeAxisView::update_diskstream_display ()
1194 RouteTimeAxisView::selection_click (GdkEventButton
* ev
)
1196 if (Keyboard::modifier_state_equals (ev
->state
, (Keyboard::TertiaryModifier
|Keyboard::PrimaryModifier
))) {
1198 /* special case: select/deselect all tracks */
1199 if (_editor
.get_selection().selected (this)) {
1200 _editor
.get_selection().clear_tracks ();
1202 _editor
.select_all_tracks ();
1208 switch (ArdourKeyboard::selection_type (ev
->state
)) {
1209 case Selection::Toggle
:
1210 _editor
.get_selection().toggle (this);
1213 case Selection::Set
:
1214 _editor
.get_selection().set (this);
1217 case Selection::Extend
:
1218 _editor
.extend_selection_to_track (*this);
1221 case Selection::Add
:
1222 _editor
.get_selection().add (this);
1228 RouteTimeAxisView::set_selected_points (PointSelection
& points
)
1230 for (Children::iterator i
= children
.begin(); i
!= children
.end(); ++i
) {
1231 (*i
)->set_selected_points (points
);
1236 RouteTimeAxisView::set_selected_regionviews (RegionSelection
& regions
)
1239 _view
->set_selected_regionviews (regions
);
1243 /** Add the selectable things that we have to a list.
1244 * @param results List to add things to.
1247 RouteTimeAxisView::get_selectables (framepos_t start
, framepos_t end
, double top
, double bot
, list
<Selectable
*>& results
)
1252 speed
= track()->speed();
1255 framepos_t
const start_adjusted
= session_frame_to_track_frame(start
, speed
);
1256 framepos_t
const end_adjusted
= session_frame_to_track_frame(end
, speed
);
1258 if ((_view
&& ((top
< 0.0 && bot
< 0.0))) || touched (top
, bot
)) {
1259 _view
->get_selectables (start_adjusted
, end_adjusted
, top
, bot
, results
);
1262 /* pick up visible automation tracks */
1264 for (Children::iterator i
= children
.begin(); i
!= children
.end(); ++i
) {
1265 if (!(*i
)->hidden()) {
1266 (*i
)->get_selectables (start_adjusted
, end_adjusted
, top
, bot
, results
);
1272 RouteTimeAxisView::get_inverted_selectables (Selection
& sel
, list
<Selectable
*>& results
)
1275 _view
->get_inverted_selectables (sel
, results
);
1278 for (Children::iterator i
= children
.begin(); i
!= children
.end(); ++i
) {
1279 if (!(*i
)->hidden()) {
1280 (*i
)->get_inverted_selectables (sel
, results
);
1288 RouteTimeAxisView::route_group () const
1290 return _route
->route_group();
1294 RouteTimeAxisView::name() const
1296 return _route
->name();
1299 boost::shared_ptr
<Playlist
>
1300 RouteTimeAxisView::playlist () const
1302 boost::shared_ptr
<Track
> tr
;
1304 if ((tr
= track()) != 0) {
1305 return tr
->playlist();
1307 return boost::shared_ptr
<Playlist
> ();
1312 RouteTimeAxisView::name_entry_changed ()
1316 x
= name_entry
.get_text ();
1318 if (x
== _route
->name()) {
1322 strip_whitespace_edges(x
);
1324 if (x
.length() == 0) {
1325 name_entry
.set_text (_route
->name());
1329 if (!_session
->route_name_unique (x
)) {
1330 ARDOUR_UI::instance()->popup_error (_("A track already exists with that name"));
1331 name_entry
.set_text (_route
->name());
1332 } else if (_session
->route_name_internal (x
)) {
1333 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1335 name_entry
.set_text (_route
->name());
1337 _route
->set_name (x
);
1341 boost::shared_ptr
<Region
>
1342 RouteTimeAxisView::find_next_region (framepos_t pos
, RegionPoint point
, int32_t dir
)
1344 boost::shared_ptr
<Playlist
> pl
= playlist ();
1347 return pl
->find_next_region (pos
, point
, dir
);
1350 return boost::shared_ptr
<Region
> ();
1354 RouteTimeAxisView::find_next_region_boundary (framepos_t pos
, int32_t dir
)
1356 boost::shared_ptr
<Playlist
> pl
= playlist ();
1359 return pl
->find_next_region_boundary (pos
, dir
);
1366 RouteTimeAxisView::cut_copy_clear (Selection
& selection
, CutCopyOp op
)
1368 boost::shared_ptr
<Playlist
> what_we_got
;
1369 boost::shared_ptr
<Track
> tr
= track ();
1370 boost::shared_ptr
<Playlist
> playlist
;
1373 /* route is a bus, not a track */
1377 playlist
= tr
->playlist();
1379 TimeSelection
time (selection
.time
);
1380 float const speed
= tr
->speed();
1381 if (speed
!= 1.0f
) {
1382 for (TimeSelection::iterator i
= time
.begin(); i
!= time
.end(); ++i
) {
1383 (*i
).start
= session_frame_to_track_frame((*i
).start
, speed
);
1384 (*i
).end
= session_frame_to_track_frame((*i
).end
, speed
);
1388 playlist
->clear_changes ();
1389 playlist
->clear_owned_changes ();
1393 if ((what_we_got
= playlist
->cut (time
)) != 0) {
1394 _editor
.get_cut_buffer().add (what_we_got
);
1396 vector
<Command
*> cmds
;
1397 playlist
->rdiff (cmds
);
1398 _session
->add_commands (cmds
);
1400 _session
->add_command (new StatefulDiffCommand (playlist
));
1404 if ((what_we_got
= playlist
->copy (time
)) != 0) {
1405 _editor
.get_cut_buffer().add (what_we_got
);
1410 if ((what_we_got
= playlist
->cut (time
)) != 0) {
1412 vector
<Command
*> cmds
;
1413 playlist
->rdiff (cmds
);
1414 _session
->add_commands (cmds
);
1415 _session
->add_command (new StatefulDiffCommand (playlist
));
1416 what_we_got
->release ();
1423 RouteTimeAxisView::paste (framepos_t pos
, float times
, Selection
& selection
, size_t nth
)
1429 boost::shared_ptr
<Playlist
> pl
= playlist ();
1430 PlaylistSelection::iterator p
;
1432 for (p
= selection
.playlists
.begin(); p
!= selection
.playlists
.end() && nth
; ++p
, --nth
) {}
1434 if (p
== selection
.playlists
.end()) {
1438 DEBUG_TRACE (DEBUG::CutNPaste
, string_compose ("paste to %1\n", pos
));
1440 if (track()->speed() != 1.0f
) {
1441 pos
= session_frame_to_track_frame (pos
, track()->speed());
1442 DEBUG_TRACE (DEBUG::CutNPaste
, string_compose ("modified paste to %1\n", pos
));
1445 pl
->clear_changes ();
1446 pl
->paste (*p
, pos
, times
);
1447 _session
->add_command (new StatefulDiffCommand (pl
));
1453 struct PlaylistSorter
{
1454 bool operator() (boost::shared_ptr
<Playlist
> a
, boost::shared_ptr
<Playlist
> b
) const {
1455 return a
->sort_id() < b
->sort_id();
1460 RouteTimeAxisView::build_playlist_menu ()
1462 using namespace Menu_Helpers
;
1468 delete playlist_action_menu
;
1469 playlist_action_menu
= new Menu
;
1470 playlist_action_menu
->set_name ("ArdourContextMenu");
1472 MenuList
& playlist_items
= playlist_action_menu
->items();
1473 playlist_action_menu
->set_name ("ArdourContextMenu");
1474 playlist_items
.clear();
1476 vector
<boost::shared_ptr
<Playlist
> > playlists
, playlists_tr
;
1477 boost::shared_ptr
<Track
> tr
= track();
1478 RadioMenuItem::Group playlist_group
;
1480 _session
->playlists
->get (playlists
);
1482 /* find the playlists for this diskstream */
1483 for (vector
<boost::shared_ptr
<Playlist
> >::iterator i
= playlists
.begin(); i
!= playlists
.end(); ++i
) {
1484 if (((*i
)->get_orig_diskstream_id() == tr
->diskstream_id()) || (tr
->playlist()->id() == (*i
)->id())) {
1485 playlists_tr
.push_back(*i
);
1489 /* sort the playlists */
1491 sort (playlists_tr
.begin(), playlists_tr
.end(), cmp
);
1493 /* add the playlists to the menu */
1494 for (vector
<boost::shared_ptr
<Playlist
> >::iterator i
= playlists_tr
.begin(); i
!= playlists_tr
.end(); ++i
) {
1495 playlist_items
.push_back (RadioMenuElem (playlist_group
, (*i
)->name()));
1496 RadioMenuItem
*item
= static_cast<RadioMenuItem
*>(&playlist_items
.back());
1497 item
->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist
), item
, boost::weak_ptr
<Playlist
> (*i
)));
1499 if (tr
->playlist()->id() == (*i
)->id()) {
1505 playlist_items
.push_back (SeparatorElem());
1506 playlist_items
.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist
)));
1507 playlist_items
.push_back (SeparatorElem());
1509 if (!route_group() || !route_group()->is_active()) {
1510 playlist_items
.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor
, &PublicEditor::new_playlists
), this)));
1511 playlist_items
.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor
, &PublicEditor::copy_playlists
), this)));
1514 // Use a label which tells the user what is happening
1515 playlist_items
.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor
, &PublicEditor::new_playlists
), this)));
1516 playlist_items
.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor
, &PublicEditor::copy_playlists
), this)));
1520 playlist_items
.push_back (SeparatorElem());
1521 playlist_items
.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor
, &PublicEditor::clear_playlists
), this)));
1522 playlist_items
.push_back (SeparatorElem());
1524 playlist_items
.push_back (MenuElem(_("Select from all..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector
)));
1528 RouteTimeAxisView::use_playlist (RadioMenuItem
*item
, boost::weak_ptr
<Playlist
> wpl
)
1530 assert (is_track());
1532 // exit if we were triggered by deactivating the old playlist
1533 if (!item
->get_active()) {
1537 boost::shared_ptr
<Playlist
> pl (wpl
.lock());
1543 boost::shared_ptr
<AudioPlaylist
> apl
= boost::dynamic_pointer_cast
<AudioPlaylist
> (pl
);
1546 if (track()->playlist() == apl
) {
1547 // exit when use_playlist is called by the creation of the playlist menu
1548 // or the playlist choice is unchanged
1551 track()->use_playlist (apl
);
1553 if (route_group() && route_group()->is_active()) {
1554 std::string group_string
= "."+route_group()->name()+".";
1556 std::string take_name
= apl
->name();
1557 std::string::size_type idx
= take_name
.find(group_string
);
1559 if (idx
== std::string::npos
)
1562 take_name
= take_name
.substr(idx
+ group_string
.length()); // find the bit containing the take number / name
1564 boost::shared_ptr
<RouteList
> rl (route_group()->route_list());
1566 for (RouteList::const_iterator i
= rl
->begin(); i
!= rl
->end(); ++i
) {
1567 if ( (*i
) == this->route()) {
1571 std::string playlist_name
= (*i
)->name()+group_string
+take_name
;
1573 boost::shared_ptr
<Track
> track
= boost::dynamic_pointer_cast
<Track
>(*i
);
1578 boost::shared_ptr
<Playlist
> ipl
= session()->playlists
->by_name(playlist_name
);
1580 // No playlist for this track for this take yet, make it
1581 track
->use_new_playlist();
1582 track
->playlist()->set_name(playlist_name
);
1584 track
->use_playlist(ipl
);
1592 RouteTimeAxisView::show_playlist_selector ()
1594 _editor
.playlist_selector().show_for (this);
1598 RouteTimeAxisView::map_frozen ()
1604 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen
)
1606 switch (track()->freeze_state()) {
1608 playlist_button
.set_sensitive (false);
1609 rec_enable_button
->set_sensitive (false);
1612 playlist_button
.set_sensitive (true);
1613 rec_enable_button
->set_sensitive (true);
1619 RouteTimeAxisView::color_handler ()
1621 //case cTimeStretchOutline:
1622 if (timestretch_rect
) {
1623 timestretch_rect
->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline
.get();
1625 //case cTimeStretchFill:
1626 if (timestretch_rect
) {
1627 timestretch_rect
->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill
.get();
1633 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1634 * Will add track if necessary.
1637 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter
& param
)
1639 boost::shared_ptr
<AutomationTimeAxisView
> track
= automation_child (param
);
1640 Gtk::CheckMenuItem
* menu
= automation_child_menu_item (param
);
1643 /* it doesn't exist yet, so we don't care about the button state: just add it */
1644 create_automation_child (param
, true);
1647 bool yn
= menu
->get_active();
1648 if (track
->set_visibility (menu
->get_active()) && yn
) {
1650 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1651 will have done that for us.
1655 _route
->gui_changed (X_("track_height"), (void *) 0); /* EMIT_SIGNAL */
1662 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param
)
1664 boost::shared_ptr
<AutomationTimeAxisView
> track
= automation_child (param
);
1670 Gtk::CheckMenuItem
* menu
= automation_child_menu_item (param
);
1672 // if Evoral::Parameter::operator< doesn't obey strict weak ordering, we may crash here....
1673 track
->get_state_node()->add_property (X_("shown"), X_("no"));
1675 if (menu
&& !_hidden
) {
1676 ignore_toggle
= true;
1677 menu
->set_active (false);
1678 ignore_toggle
= false;
1681 if (_route
&& !no_redraw
) {
1682 _route
->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1688 RouteTimeAxisView::show_all_automation (bool apply_to_selection
)
1690 if (apply_to_selection
) {
1691 _editor
.get_selection().tracks
.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation
, _1
, false));
1695 /* Show our automation */
1697 for (AutomationTracks::iterator i
= _automation_tracks
.begin(); i
!= _automation_tracks
.end(); ++i
) {
1698 i
->second
->set_marked_for_display (true);
1699 i
->second
->canvas_display()->show();
1700 i
->second
->get_state_node()->add_property ("shown", X_("yes"));
1702 Gtk::CheckMenuItem
* menu
= automation_child_menu_item (i
->first
);
1705 menu
->set_active(true);
1710 /* Show processor automation */
1712 for (list
<ProcessorAutomationInfo
*>::iterator i
= processor_automation
.begin(); i
!= processor_automation
.end(); ++i
) {
1713 for (vector
<ProcessorAutomationNode
*>::iterator ii
= (*i
)->lines
.begin(); ii
!= (*i
)->lines
.end(); ++ii
) {
1714 if ((*ii
)->view
== 0) {
1715 add_processor_automation_curve ((*i
)->processor
, (*ii
)->what
);
1718 (*ii
)->menu_item
->set_active (true);
1726 _route
->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1731 RouteTimeAxisView::show_existing_automation (bool apply_to_selection
)
1733 if (apply_to_selection
) {
1734 _editor
.get_selection().tracks
.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation
, _1
, false));
1738 /* Show our automation */
1740 for (AutomationTracks::iterator i
= _automation_tracks
.begin(); i
!= _automation_tracks
.end(); ++i
) {
1741 if (i
->second
->has_automation()) {
1742 i
->second
->set_marked_for_display (true);
1743 i
->second
->canvas_display()->show();
1744 i
->second
->get_state_node()->add_property ("shown", X_("yes"));
1746 Gtk::CheckMenuItem
* menu
= automation_child_menu_item (i
->first
);
1748 menu
->set_active(true);
1754 /* Show processor automation */
1756 for (list
<ProcessorAutomationInfo
*>::iterator i
= processor_automation
.begin(); i
!= processor_automation
.end(); ++i
) {
1757 for (vector
<ProcessorAutomationNode
*>::iterator ii
= (*i
)->lines
.begin(); ii
!= (*i
)->lines
.end(); ++ii
) {
1758 if ((*ii
)->view
!= 0 && (*i
)->processor
->control((*ii
)->what
)->list()->size() > 0) {
1759 (*ii
)->menu_item
->set_active (true);
1766 _route
->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1771 RouteTimeAxisView::hide_all_automation (bool apply_to_selection
)
1773 if (apply_to_selection
) {
1774 _editor
.get_selection().tracks
.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation
, _1
, false));
1778 /* Hide our automation */
1780 for (AutomationTracks::iterator i
= _automation_tracks
.begin(); i
!= _automation_tracks
.end(); ++i
) {
1781 i
->second
->set_marked_for_display (false);
1783 i
->second
->get_state_node()->add_property ("shown", X_("no"));
1785 Gtk::CheckMenuItem
* menu
= automation_child_menu_item (i
->first
);
1788 menu
->set_active (false);
1792 /* Hide processor automation */
1794 for (list
<ProcessorAutomationInfo
*>::iterator i
= processor_automation
.begin(); i
!= processor_automation
.end(); ++i
) {
1795 for (vector
<ProcessorAutomationNode
*>::iterator ii
= (*i
)->lines
.begin(); ii
!= (*i
)->lines
.end(); ++ii
) {
1796 (*ii
)->menu_item
->set_active (false);
1801 _route
->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1807 RouteTimeAxisView::region_view_added (RegionView
* rv
)
1809 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1810 for (Children::iterator i
= children
.begin(); i
!= children
.end(); ++i
) {
1811 boost::shared_ptr
<AutomationTimeAxisView
> atv
;
1813 if ((atv
= boost::dynamic_pointer_cast
<AutomationTimeAxisView
> (*i
)) != 0) {
1818 for (UnderlayMirrorList::iterator i
= _underlay_mirrors
.begin(); i
!= _underlay_mirrors
.end(); ++i
) {
1819 (*i
)->add_ghost(rv
);
1823 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1825 for (vector
<ProcessorAutomationNode
*>::iterator i
= lines
.begin(); i
!= lines
.end(); ++i
) {
1831 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1833 parent
.remove_processor_automation_node (this);
1837 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode
* pan
)
1840 remove_child (pan
->view
);
1844 RouteTimeAxisView::ProcessorAutomationNode
*
1845 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr
<Processor
> processor
, Evoral::Parameter what
)
1847 for (list
<ProcessorAutomationInfo
*>::iterator i
= processor_automation
.begin(); i
!= processor_automation
.end(); ++i
) {
1849 if ((*i
)->processor
== processor
) {
1851 for (vector
<ProcessorAutomationNode
*>::iterator ii
= (*i
)->lines
.begin(); ii
!= (*i
)->lines
.end(); ++ii
) {
1852 if ((*ii
)->what
== what
) {
1863 legalize_for_xml_node (string str
)
1865 string::size_type pos
;
1866 string legal_chars
= "abcdefghijklmnopqrtsuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_=:";
1872 while ((pos
= legal
.find_first_not_of (legal_chars
, pos
)) != string::npos
) {
1873 legal
.replace (pos
, 1, "_");
1882 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr
<Processor
> processor
, Evoral::Parameter what
)
1885 ProcessorAutomationNode
* pan
;
1887 if ((pan
= find_processor_automation_node (processor
, what
)) == 0) {
1888 /* session state may never have been saved with new plugin */
1889 error
<< _("programming error: ")
1890 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
1891 processor
->name(), what
.type(), (int) what
.channel(), what
.id() )
1901 name
= processor
->describe_parameter (what
);
1903 /* create a string that is a legal XML node name that can be used to refer to this redirect+port combination */
1907 char state_name
[256];
1908 snprintf (state_name
, sizeof (state_name
), "%s-%" PRIu32
, legalize_for_xml_node (processor
->name()).c_str(), what
.id());
1910 boost::shared_ptr
<AutomationControl
> control
1911 = boost::dynamic_pointer_cast
<AutomationControl
>(processor
->control(what
, true));
1913 pan
->view
= boost::shared_ptr
<AutomationTimeAxisView
>(
1914 new AutomationTimeAxisView (_session
, _route
, processor
, control
, control
->parameter (),
1915 _editor
, *this, false, parent_canvas
, name
, state_name
));
1917 pan
->view
->Hiding
.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden
), pan
, processor
));
1919 if (!pan
->view
->marked_for_display()) {
1922 pan
->menu_item
->set_active (true);
1925 add_child (pan
->view
);
1928 _view
->foreach_regionview (sigc::mem_fun(*pan
->view
.get(), &TimeAxisView::add_ghost
));
1931 processor
->mark_automation_visible (what
, true);
1935 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode
* pan
, boost::shared_ptr
<Processor
> i
)
1938 pan
->menu_item
->set_active (false);
1941 i
->mark_automation_visible (pan
->what
, false);
1944 _route
->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1949 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr
<Processor
> p
)
1951 boost::shared_ptr
<Processor
> processor (p
.lock ());
1956 set
<Evoral::Parameter
> s
;
1957 boost::shared_ptr
<AutomationLine
> al
;
1959 processor
->what_has_visible_data (s
);
1961 for (set
<Evoral::Parameter
>::iterator i
= s
.begin(); i
!= s
.end(); ++i
) {
1963 if ((al
= find_processor_automation_curve (processor
, *i
)) != 0) {
1966 add_processor_automation_curve (processor
, (*i
));
1972 RouteTimeAxisView::add_automation_child (Evoral::Parameter param
, boost::shared_ptr
<AutomationTimeAxisView
> track
, bool show
)
1974 using namespace Menu_Helpers
;
1981 track
->Hiding
.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden
), param
));
1983 bool hideit
= (!show
);
1985 if ((node
= track
->get_state_node()) != 0) {
1986 if ((prop
= node
->property ("shown")) != 0) {
1987 if (string_is_affirmative (prop
->value())) {
1993 _automation_tracks
[param
] = track
;
1995 track
->set_visibility (!hideit
);
1998 _route
->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
2001 if (!EventTypeMap::instance().is_midi_parameter(param
)) {
2002 /* MIDI-related parameters are always in the menu, there's no
2003 reason to rebuild the menu just because we added a automation
2004 lane for one of them. But if we add a non-MIDI automation
2005 lane, then we need to invalidate the display menu.
2007 delete display_menu
;
2013 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr
<Processor
> p
)
2015 boost::shared_ptr
<Processor
> processor (p
.lock ());
2017 if (!processor
|| !processor
->display_to_user ()) {
2021 using namespace Menu_Helpers
;
2022 ProcessorAutomationInfo
*rai
;
2023 list
<ProcessorAutomationInfo
*>::iterator x
;
2025 const std::set
<Evoral::Parameter
>& automatable
= processor
->what_can_be_automated ();
2026 std::set
<Evoral::Parameter
> has_visible_automation
;
2028 processor
->what_has_visible_data(has_visible_automation
);
2030 if (automatable
.empty()) {
2034 for (x
= processor_automation
.begin(); x
!= processor_automation
.end(); ++x
) {
2035 if ((*x
)->processor
== processor
) {
2040 if (x
== processor_automation
.end()) {
2042 rai
= new ProcessorAutomationInfo (processor
);
2043 processor_automation
.push_back (rai
);
2051 /* any older menu was deleted at the top of processors_changed()
2052 when we cleared the subplugin menu.
2055 rai
->menu
= manage (new Menu
);
2056 MenuList
& items
= rai
->menu
->items();
2057 rai
->menu
->set_name ("ArdourContextMenu");
2061 for (std::set
<Evoral::Parameter
>::const_iterator i
= automatable
.begin(); i
!= automatable
.end(); ++i
) {
2063 ProcessorAutomationNode
* pan
;
2064 CheckMenuItem
* mitem
;
2066 string name
= processor
->describe_parameter (*i
);
2068 items
.push_back (CheckMenuElem (name
));
2069 mitem
= dynamic_cast<CheckMenuItem
*> (&items
.back());
2071 _subplugin_menu_map
[*i
] = mitem
;
2073 if (has_visible_automation
.find((*i
)) != has_visible_automation
.end()) {
2074 mitem
->set_active(true);
2077 if ((pan
= find_processor_automation_node (processor
, *i
)) == 0) {
2081 pan
= new ProcessorAutomationNode (*i
, mitem
, *this);
2083 rai
->lines
.push_back (pan
);
2087 pan
->menu_item
= mitem
;
2091 mitem
->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled
), rai
, pan
));
2094 /* add the menu for this processor, because the subplugin
2095 menu is always cleared at the top of processors_changed().
2096 this is the result of some poor design in gtkmm and/or
2100 subplugin_menu
.items().push_back (MenuElem (processor
->name(), *rai
->menu
));
2105 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo
* rai
,
2106 RouteTimeAxisView::ProcessorAutomationNode
* pan
)
2108 bool showit
= pan
->menu_item
->get_active();
2109 bool redraw
= false;
2111 if (pan
->view
== 0 && showit
) {
2112 add_processor_automation_curve (rai
->processor
, pan
->what
);
2116 if (pan
->view
&& showit
!= pan
->view
->marked_for_display()) {
2119 pan
->view
->set_marked_for_display (true);
2120 pan
->view
->canvas_display()->show();
2121 pan
->view
->canvas_background()->show();
2123 rai
->processor
->mark_automation_visible (pan
->what
, true);
2124 pan
->view
->set_marked_for_display (false);
2132 if (redraw
&& !no_redraw
) {
2133 _route
->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
2139 RouteTimeAxisView::processors_changed (RouteProcessorChange c
)
2141 if (c
.type
== RouteProcessorChange::MeterPointChange
) {
2142 /* nothing to do if only the meter point has changed */
2146 using namespace Menu_Helpers
;
2148 for (list
<ProcessorAutomationInfo
*>::iterator i
= processor_automation
.begin(); i
!= processor_automation
.end(); ++i
) {
2149 (*i
)->valid
= false;
2152 _subplugin_menu_map
.clear ();
2153 subplugin_menu
.items().clear ();
2155 _route
->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu
));
2156 _route
->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves
));
2158 bool deleted_processor_automation
= false;
2160 for (list
<ProcessorAutomationInfo
*>::iterator i
= processor_automation
.begin(); i
!= processor_automation
.end(); ) {
2162 list
<ProcessorAutomationInfo
*>::iterator tmp
;
2170 processor_automation
.erase (i
);
2171 deleted_processor_automation
= true;
2178 if (deleted_processor_automation
&& !no_redraw
) {
2179 _route
->gui_changed ("track_height", this);
2183 boost::shared_ptr
<AutomationLine
>
2184 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr
<Processor
> processor
, Evoral::Parameter what
)
2186 ProcessorAutomationNode
* pan
;
2188 if ((pan
= find_processor_automation_node (processor
, what
)) != 0) {
2194 return boost::shared_ptr
<AutomationLine
>();
2198 RouteTimeAxisView::reset_processor_automation_curves ()
2200 for (ProcessorAutomationCurves::iterator i
= processor_automation_curves
.begin(); i
!= processor_automation_curves
.end(); ++i
) {
2206 RouteTimeAxisView::update_rec_display ()
2208 RouteUI::update_rec_display ();
2209 name_entry
.set_sensitive (!_route
->record_enabled());
2213 RouteTimeAxisView::set_layer_display (LayerDisplay d
, bool apply_to_selection
)
2215 if (apply_to_selection
) {
2216 _editor
.get_selection().tracks
.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display
, _1
, d
, false));
2220 _view
->set_layer_display (d
);
2224 xml_node
->add_property (N_("layer-display"), enum_2_string (d
));
2229 RouteTimeAxisView::layer_display () const
2232 return _view
->layer_display ();
2235 /* we don't know, since we don't have a _view, so just return something */
2241 boost::shared_ptr
<AutomationTimeAxisView
>
2242 RouteTimeAxisView::automation_child(Evoral::Parameter param
)
2244 AutomationTracks::iterator i
= _automation_tracks
.find(param
);
2245 if (i
!= _automation_tracks
.end()) {
2248 return boost::shared_ptr
<AutomationTimeAxisView
>();
2253 RouteTimeAxisView::fast_update ()
2255 gm
.get_level_meter().update_meters ();
2259 RouteTimeAxisView::hide_meter ()
2262 gm
.get_level_meter().hide_meters ();
2266 RouteTimeAxisView::show_meter ()
2272 RouteTimeAxisView::reset_meter ()
2274 if (Config
->get_show_track_meters()) {
2275 gm
.get_level_meter().setup_meters (height
-5);
2282 RouteTimeAxisView::clear_meter ()
2284 gm
.get_level_meter().clear_meters ();
2288 RouteTimeAxisView::meter_changed ()
2290 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed
)
2295 RouteTimeAxisView::io_changed (IOChange
/*change*/, void */
*src*/
)
2301 RouteTimeAxisView::build_underlay_menu(Gtk::Menu
* parent_menu
)
2303 using namespace Menu_Helpers
;
2305 if (!_underlay_streams
.empty()) {
2306 MenuList
& parent_items
= parent_menu
->items();
2307 Menu
* gs_menu
= manage (new Menu
);
2308 gs_menu
->set_name ("ArdourContextMenu");
2309 MenuList
& gs_items
= gs_menu
->items();
2311 parent_items
.push_back (MenuElem (_("Underlays"), *gs_menu
));
2313 for(UnderlayList::iterator it
= _underlay_streams
.begin(); it
!= _underlay_streams
.end(); ++it
) {
2314 gs_items
.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it
)->trackview().name()),
2315 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay
), *it
)));
2321 RouteTimeAxisView::set_underlay_state()
2323 if (!underlay_xml_node
) {
2327 XMLNodeList nlist
= underlay_xml_node
->children();
2328 XMLNodeConstIterator niter
;
2329 XMLNode
*child_node
;
2331 for (niter
= nlist
.begin(); niter
!= nlist
.end(); ++niter
) {
2332 child_node
= *niter
;
2334 if (child_node
->name() != "Underlay") {
2338 XMLProperty
* prop
= child_node
->property ("id");
2340 PBD::ID
id (prop
->value());
2342 RouteTimeAxisView
* v
= _editor
.get_route_view_by_route_id (id
);
2345 add_underlay(v
->view(), false);
2354 RouteTimeAxisView::add_underlay (StreamView
* v
, bool update_xml
)
2360 RouteTimeAxisView
& other
= v
->trackview();
2362 if (find(_underlay_streams
.begin(), _underlay_streams
.end(), v
) == _underlay_streams
.end()) {
2363 if (find(other
._underlay_mirrors
.begin(), other
._underlay_mirrors
.end(), this) != other
._underlay_mirrors
.end()) {
2364 fatal
<< _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg
;
2368 _underlay_streams
.push_back(v
);
2369 other
._underlay_mirrors
.push_back(this);
2371 v
->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost
));
2374 if (!underlay_xml_node
) {
2376 underlay_xml_node
= xml_node
->add_child("Underlays");
2379 XMLNode
* node
= underlay_xml_node
->add_child("Underlay");
2380 XMLProperty
* prop
= node
->add_property("id");
2381 prop
->set_value(v
->trackview().route()->id().to_s());
2387 RouteTimeAxisView::remove_underlay (StreamView
* v
)
2393 UnderlayList::iterator it
= find(_underlay_streams
.begin(), _underlay_streams
.end(), v
);
2394 RouteTimeAxisView
& other
= v
->trackview();
2396 if (it
!= _underlay_streams
.end()) {
2397 UnderlayMirrorList::iterator gm
= find(other
._underlay_mirrors
.begin(), other
._underlay_mirrors
.end(), this);
2399 if (gm
== other
._underlay_mirrors
.end()) {
2400 fatal
<< _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg
;
2404 v
->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost
));
2406 _underlay_streams
.erase(it
);
2407 other
._underlay_mirrors
.erase(gm
);
2409 if (underlay_xml_node
) {
2410 underlay_xml_node
->remove_nodes_and_delete("id", v
->trackview().route()->id().to_s());
2416 RouteTimeAxisView::set_button_names ()
2418 rec_enable_button_label
.set_text (_("r"));
2420 if (_route
&& _route
->solo_safe()) {
2421 solo_button_label
.set_text (X_("!"));
2423 if (Config
->get_solo_control_is_listen_control()) {
2424 switch (Config
->get_listen_position()) {
2425 case AfterFaderListen
:
2426 solo_button_label
.set_text (_("A"));
2428 case PreFaderListen
:
2429 solo_button_label
.set_text (_("P"));
2433 solo_button_label
.set_text (_("s"));
2436 mute_button_label
.set_text (_("m"));
2440 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param
)
2442 ParameterMenuMap::iterator i
= _main_automation_menu_map
.find (param
);
2443 if (i
!= _main_automation_menu_map
.end()) {
2447 i
= _subplugin_menu_map
.find (param
);
2448 if (i
!= _subplugin_menu_map
.end()) {
2456 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter
& param
, bool show
)
2458 boost::shared_ptr
<AutomationControl
> c
= _route
->gain_control();
2460 error
<< "Route has no gain automation, unable to add automation track view." << endmsg
;
2464 gain_track
.reset (new AutomationTimeAxisView (_session
,
2465 _route
, _route
->amp(), c
, param
,
2470 _route
->amp()->describe_parameter(param
)));
2473 _view
->foreach_regionview (sigc::mem_fun (*gain_track
.get(), &TimeAxisView::add_ghost
));
2476 add_automation_child (Evoral::Parameter(GainAutomation
), gain_track
, show
);