2 Copyright (C) 2000-2002 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.
23 #include <sigc++/bind.h>
25 #include <pbd/convert.h>
26 #include <pbd/enumwriter.h>
28 #include <gtkmm2ext/gtk_ui.h>
29 #include <gtkmm2ext/utils.h>
30 #include <gtkmm2ext/choice.h>
31 #include <gtkmm2ext/stop_signal.h>
32 #include <gtkmm2ext/doi.h>
33 #include <gtkmm2ext/slider_controller.h>
34 #include <gtkmm2ext/bindable_button.h>
36 #include <ardour/ardour.h>
37 #include <ardour/session.h>
38 #include <ardour/audioengine.h>
39 #include <ardour/route.h>
40 #include <ardour/audio_track.h>
41 #include <ardour/audio_diskstream.h>
42 #include <ardour/panner.h>
43 #include <ardour/send.h>
44 #include <ardour/insert.h>
45 #include <ardour/profile.h>
46 #include <ardour/ladspa_plugin.h>
47 #include <ardour/connection.h>
48 #include <ardour/session_connection.h>
50 #include "ardour_ui.h"
51 #include "ardour_dialog.h"
52 #include "mixer_strip.h"
55 #include "public_editor.h"
57 #include "io_selector.h"
59 #include "gui_thread.h"
64 using namespace ARDOUR
;
67 using namespace Gtkmm2ext
;
70 int MixerStrip::scrollbar_height
= 0;
72 #ifdef VARISPEED_IN_MIXER_STRIP
74 speed_printer (char buf
[32], Gtk::Adjustment
& adj
, void* arg
)
76 float val
= adj
.get_value ();
81 snprintf (buf
, 32, "%.3f", val
);
86 MixerStrip::MixerStrip (Mixer_UI
& mx
, Session
& sess
, bool in_mixer
)
88 RouteUI (sess
, _("Mute"), _("Solo"), _("Record")),
90 _mixer_owned (in_mixer
),
91 pre_redirect_box (PreFader
, sess
, mx
.plugin_selector(), mx
.selection(), in_mixer
),
92 post_redirect_box (PostFader
, sess
, mx
.plugin_selector(), mx
.selection(), in_mixer
),
96 middle_button_table (1, 2),
97 bottom_button_table (1, 2),
98 meter_point_label (_("pre")),
99 comment_button (_("Comments")),
100 speed_adjustment (1.0, 0.001, 4.0, 0.001, 0.1),
101 speed_spinner (&speed_adjustment
, "MixerStripSpeedBase", true)
107 /* the editor mixer strip: don't destroy it every time
108 the underlying route goes away.
111 self_destruct
= false;
115 MixerStrip::MixerStrip (Mixer_UI
& mx
, Session
& sess
, boost::shared_ptr
<Route
> rt
, bool in_mixer
)
117 RouteUI (sess
, _("Mute"), _("Solo"), _("Record")),
119 _mixer_owned (in_mixer
),
120 pre_redirect_box (PreFader
, sess
, mx
.plugin_selector(), mx
.selection(), in_mixer
),
121 post_redirect_box (PostFader
, sess
, mx
.plugin_selector(), mx
.selection(), in_mixer
),
125 middle_button_table (1, 2),
126 bottom_button_table (1, 2),
127 meter_point_label (_("pre")),
128 comment_button (_("Comments")),
129 speed_adjustment (1.0, 0.001, 4.0, 0.001, 0.1),
130 speed_spinner (&speed_adjustment
, "MixerStripSpeedBase", true)
143 _marked_for_display
= false;
145 rename_menu_item
= 0;
146 ignore_comment_edit
= false;
147 ignore_toggle
= false;
148 ignore_speed_adjustment
= false;
153 width_button
.add (*(manage (new Gtk::Image (::get_icon("strip_width")))));
154 hide_button
.add (*(manage (new Gtk::Image (::get_icon("hide")))));
156 input_label
.set_text (_("Input"));
157 ARDOUR_UI::instance()->set_tip (&input_button
, _("Click to choose inputs"), "");
158 input_button
.add (input_label
);
159 input_button
.set_name ("MixerIOButton");
160 input_label
.set_name ("MixerIOButtonLabel");
162 output_label
.set_text (_("Output"));
163 ARDOUR_UI::instance()->set_tip (&output_button
, _("Click to choose outputs"), "");
164 output_button
.add (output_label
);
165 output_button
.set_name ("MixerIOButton");
166 output_label
.set_name ("MixerIOButtonLabel");
168 ARDOUR_UI::instance()->set_tip (&meter_point_button
, _("Select metering point"), "");
169 meter_point_button
.add (meter_point_label
);
170 meter_point_button
.set_name ("MixerStripMeterPreButton");
171 meter_point_label
.set_name ("MixerStripMeterPreButton");
173 /* TRANSLATORS: this string should be longest of the strings
174 used to describe meter points. In english, it's "input".
176 set_size_request_to_display_given_text (meter_point_button
, _("tupni"), 5, 5);
178 bottom_button_table
.attach (meter_point_button
, 1, 2, 0, 1);
180 meter_point_button
.signal_button_press_event().connect (mem_fun (gpm
, &GainMeter::meter_press
), false);
181 /* XXX what is this meant to do? */
182 //meter_point_button.signal_button_release_event().connect (mem_fun (gpm, &GainMeter::meter_release), false);
184 hide_button
.set_events (hide_button
.get_events() & ~(Gdk::ENTER_NOTIFY_MASK
|Gdk::LEAVE_NOTIFY_MASK
));
186 mute_button
->set_name ("MixerMuteButton");
187 solo_button
->set_name ("MixerSoloButton");
189 button_table
.set_homogeneous (true);
190 button_table
.set_spacings (0);
192 button_table
.attach (name_button
, 0, 2, 0, 1);
193 button_table
.attach (input_button
, 0, 2, 1, 2);
195 middle_button_table
.set_homogeneous (true);
196 middle_button_table
.set_spacings (0);
197 middle_button_table
.attach (*mute_button
, 0, 1, 0, 1);
198 middle_button_table
.attach (*solo_button
, 1, 2, 0, 1);
200 bottom_button_table
.set_col_spacings (0);
201 bottom_button_table
.set_homogeneous (true);
202 bottom_button_table
.attach (group_button
, 0, 1, 0, 1);
204 name_button
.add (name_label
);
205 name_button
.set_name ("MixerNameButton");
206 Gtkmm2ext::set_size_request_to_display_given_text (name_button
, "longest label", 2, 2);
207 name_label
.set_name ("MixerNameButtonLabel");
209 ARDOUR_UI::instance()->set_tip (&group_button
, _("Mix group"), "");
210 group_button
.add (group_label
);
211 group_button
.set_name ("MixerGroupButton");
212 Gtkmm2ext::set_size_request_to_display_given_text (group_button
, "Group", 2, 2);
214 group_label
.set_name ("MixerGroupButtonLabel");
216 comment_button
.set_name ("MixerCommentButton");
218 comment_button
.signal_clicked().connect (mem_fun(*this, &MixerStrip::comment_button_clicked
));
220 global_vpacker
.set_border_width (0);
221 global_vpacker
.set_spacing (0);
223 VBox
*whvbox
= manage (new VBox
);
225 width_button
.set_name ("MixerWidthButton");
226 hide_button
.set_name ("MixerHideButton");
227 top_event_box
.set_name ("MixerTopEventBox");
229 width_button
.signal_clicked().connect (mem_fun(*this, &MixerStrip::width_clicked
));
230 hide_button
.signal_clicked().connect (mem_fun(*this, &MixerStrip::hide_clicked
));
232 width_hide_box
.pack_start (width_button
, false, true);
233 width_hide_box
.pack_start (top_event_box
, true, true);
234 width_hide_box
.pack_end (hide_button
, false, true);
235 Gtk::Alignment
*gain_meter_alignment
= Gtk::manage(new Gtk::Alignment());
236 gain_meter_alignment
->set_padding(0, 4, 0, 0);
237 gain_meter_alignment
->add(gpm
);
239 whvbox
->pack_start (width_hide_box
, true, true);
241 global_vpacker
.pack_start (*whvbox
, Gtk::PACK_SHRINK
);
242 global_vpacker
.pack_start (button_table
,Gtk::PACK_SHRINK
);
243 global_vpacker
.pack_start (pre_redirect_box
, true, true);
244 global_vpacker
.pack_start (middle_button_table
,Gtk::PACK_SHRINK
);
245 global_vpacker
.pack_start (*gain_meter_alignment
,Gtk::PACK_SHRINK
);
246 global_vpacker
.pack_start (bottom_button_table
,Gtk::PACK_SHRINK
);
247 global_vpacker
.pack_start (post_redirect_box
, true, true);
248 global_vpacker
.pack_start (panners
, Gtk::PACK_SHRINK
);
249 global_vpacker
.pack_start (output_button
, Gtk::PACK_SHRINK
);
250 global_vpacker
.pack_start (comment_button
, Gtk::PACK_SHRINK
);
252 global_frame
.add (global_vpacker
);
253 global_frame
.set_shadow_type (Gtk::SHADOW_IN
);
254 global_frame
.set_name ("BaseFrame");
258 /* force setting of visible selected status */
261 set_selected (false);
266 _session
.engine().Stopped
.connect (mem_fun(*this, &MixerStrip::engine_stopped
));
267 _session
.engine().Running
.connect (mem_fun(*this, &MixerStrip::engine_running
));
269 input_button
.signal_button_press_event().connect (mem_fun(*this, &MixerStrip::input_press
), false);
270 output_button
.signal_button_press_event().connect (mem_fun(*this, &MixerStrip::output_press
), false);
272 solo_button
->signal_button_press_event().connect (mem_fun(*this, &RouteUI::solo_press
), false);
273 solo_button
->signal_button_release_event().connect (mem_fun(*this, &RouteUI::solo_release
), false);
274 mute_button
->signal_button_press_event().connect (mem_fun(*this, &RouteUI::mute_press
), false);
275 mute_button
->signal_button_release_event().connect (mem_fun(*this, &RouteUI::mute_release
), false);
277 /* we don't need this if its not an audio track, but we don't know that yet and it doesn't
281 rec_enable_button
->set_name ("MixerRecordEnableButton");
282 rec_enable_button
->signal_button_press_event().connect (mem_fun(*this, &RouteUI::rec_enable_press
), false);
283 rec_enable_button
->signal_button_release_event().connect (mem_fun(*this, &RouteUI::rec_enable_release
));
285 name_button
.signal_button_press_event().connect (mem_fun(*this, &MixerStrip::name_button_button_press
), false);
286 group_button
.signal_button_press_event().connect (mem_fun(*this, &MixerStrip::select_mix_group
), false);
290 /* start off as a passthru strip. we'll correct this, if necessary,
291 in update_diskstream_display().
294 set_name ("AudioTrackStripBase");
296 add_events (Gdk::BUTTON_RELEASE_MASK
);
299 MixerStrip::~MixerStrip ()
301 GoingAway(); /* EMIT_SIGNAL */
303 if (input_selector
) {
304 delete input_selector
;
307 if (output_selector
) {
308 delete output_selector
;
312 delete comment_window
;
317 MixerStrip::set_route (boost::shared_ptr
<Route
> rt
)
319 if (rec_enable_button
->get_parent()) {
320 button_table
.remove (*rec_enable_button
);
323 #ifdef VARISPEED_IN_MIXER_STRIP
324 if (speed_frame
->get_parent()) {
325 button_table
.remove (*speed_frame
);
329 RouteUI::set_route (rt
);
331 if (input_selector
) {
332 delete input_selector
;
336 if (output_selector
) {
337 delete output_selector
;
343 pre_redirect_box
.set_route (rt
);
344 post_redirect_box
.set_route (rt
);
346 if (set_color_from_route()) {
347 set_color (unique_random_color());
350 if (_mixer_owned
&& (route()->master() || route()->control())) {
352 if (scrollbar_height
== 0) {
353 HScrollbar scrollbar
;
354 Gtk::Requisition
requisition(scrollbar
.size_request ());
355 scrollbar_height
= requisition
.height
;
358 EventBox
* spacer
= manage (new EventBox
);
359 spacer
->set_size_request (-1, scrollbar_height
);
360 global_vpacker
.pack_start (*spacer
, false, false);
363 if (is_audio_track()) {
365 boost::shared_ptr
<AudioTrack
> at
= audio_track();
367 connections
.push_back (at
->FreezeChange
.connect (mem_fun(*this, &MixerStrip::map_frozen
)));
369 #ifdef VARISPEED_IN_MIXER_STRIP
370 speed_adjustment
.signal_value_changed().connect (mem_fun(*this, &MixerStrip::speed_adjustment_changed
));
372 speed_frame
.set_name ("BaseFrame");
373 speed_frame
.set_shadow_type (Gtk::SHADOW_IN
);
374 speed_frame
.add (speed_spinner
);
376 speed_spinner
.set_print_func (speed_printer
, 0);
378 ARDOUR_UI::instance()->tooltips().set_tip (speed_spinner
, _("Varispeed"));
380 button_table
.attach (speed_frame
, 0, 2, 5, 6);
381 #endif /* VARISPEED_IN_MIXER_STRIP */
383 button_table
.attach (*rec_enable_button
, 0, 2, 2, 3);
384 rec_enable_button
->set_sensitive (_session
.writable());
385 rec_enable_button
->show();
388 if (_route
->phase_invert()) {
389 name_label
.set_text (X_("Ø ") + name_label
.get_text());
391 name_label
.set_text (_route
->name());
394 switch (_route
->meter_point()) {
396 meter_point_label
.set_text (_("input"));
400 meter_point_label
.set_text (_("pre"));
404 meter_point_label
.set_text (_("post"));
408 delete route_ops_menu
;
411 ARDOUR_UI::instance()->tooltips().set_tip (comment_button
, _route
->comment().empty() ?
412 _("Click to Add/Edit Comments"):
415 connections
.push_back (_route
->meter_change
.connect (mem_fun(*this, &MixerStrip::meter_changed
)));
416 connections
.push_back (_route
->input_changed
.connect (mem_fun(*this, &MixerStrip::input_changed
)));
417 connections
.push_back (_route
->output_changed
.connect (mem_fun(*this, &MixerStrip::output_changed
)));
418 connections
.push_back (_route
->mute_changed
.connect (mem_fun(*this, &RouteUI::mute_changed
)));
419 connections
.push_back (_route
->solo_changed
.connect (mem_fun(*this, &RouteUI::solo_changed
)));
420 connections
.push_back (_route
->solo_safe_changed
.connect (mem_fun(*this, &RouteUI::solo_changed
)));
421 connections
.push_back (_route
->mix_group_changed
.connect (mem_fun(*this, &MixerStrip::mix_group_changed
)));
422 connections
.push_back (_route
->panner().Changed
.connect (mem_fun(*this, &MixerStrip::connect_to_pan
)));
424 if (is_audio_track()) {
425 connections
.push_back (audio_track()->DiskstreamChanged
.connect (mem_fun(*this, &MixerStrip::diskstream_changed
)));
426 connections
.push_back (get_diskstream()->SpeedChanged
.connect (mem_fun(*this, &MixerStrip::speed_changed
)));
429 connections
.push_back (_route
->name_changed
.connect (mem_fun(*this, &RouteUI::name_changed
)));
430 connections
.push_back (_route
->comment_changed
.connect (mem_fun(*this, &MixerStrip::comment_changed
)));
431 connections
.push_back (_route
->gui_changed
.connect (mem_fun(*this, &MixerStrip::route_gui_changed
)));
433 set_stuff_from_route ();
435 /* now force an update of all the various elements */
437 pre_redirect_box
.update();
438 post_redirect_box
.update();
443 mix_group_changed (0);
447 panners
.setup_pan ();
449 if (is_audio_track()) {
453 update_diskstream_display ();
454 update_input_display ();
455 update_output_display ();
459 MixerStrip::set_stuff_from_route ()
465 /* if width is not set, it will be set by the MixerUI or editor */
467 if ((prop
= xml_node
->property ("strip_width")) != 0) {
468 set_width (Width (string_2_enum (prop
->value(), _width
)), this);
471 if ((prop
= xml_node
->property ("shown_mixer")) != 0) {
472 if (prop
->value() == "no") {
473 _marked_for_display
= false;
475 _marked_for_display
= true;
478 /* backwards compatibility */
479 _marked_for_display
= true;
484 MixerStrip::set_width (Width w
, void* owner
)
486 /* always set the gpm width again, things may be hidden */
489 panners
.set_width (w
);
490 pre_redirect_box
.set_width (w
);
491 post_redirect_box
.set_width (w
);
493 _width_owner
= owner
;
499 if (_width_owner
== this) {
500 xml_node
->add_property ("strip_width", enum_2_string (_width
));
506 if (rec_enable_button
) {
507 ((Gtk::Label
*)rec_enable_button
->get_child())->set_text (_("Record"));
509 ((Gtk::Label
*)mute_button
->get_child())->set_text (_("Mute"));
510 ((Gtk::Label
*)solo_button
->get_child())->set_text (_("Solo"));
512 if (_route
->comment() == "") {
513 comment_button
.unset_bg (STATE_NORMAL
);
514 ((Gtk::Label
*)comment_button
.get_child())->set_text (_("Comments"));
516 comment_button
.modify_bg (STATE_NORMAL
, color());
517 ((Gtk::Label
*)comment_button
.get_child())->set_text (_("*Comments*"));
520 ((Gtk::Label
*)gpm
.gain_automation_style_button
.get_child())->set_text (gpm
.astyle_string(_route
->gain_automation_curve().automation_style()));
521 ((Gtk::Label
*)gpm
.gain_automation_state_button
.get_child())->set_text (gpm
.astate_string(_route
->gain_automation_curve().automation_state()));
522 ((Gtk::Label
*)panners
.pan_automation_style_button
.get_child())->set_text (panners
.astyle_string(_route
->panner().automation_style()));
523 ((Gtk::Label
*)panners
.pan_automation_state_button
.get_child())->set_text (panners
.astate_string(_route
->panner().automation_state()));
524 Gtkmm2ext::set_size_request_to_display_given_text (name_button
, "long", 2, 2);
525 set_size_request (-1, -1);
529 if (rec_enable_button
) {
530 ((Gtk::Label
*)rec_enable_button
->get_child())->set_text (_("Rec"));
532 ((Gtk::Label
*)mute_button
->get_child())->set_text (_("M"));
533 ((Gtk::Label
*)solo_button
->get_child())->set_text (_("S"));
535 if (_route
->comment() == "") {
536 comment_button
.unset_bg (STATE_NORMAL
);
537 ((Gtk::Label
*)comment_button
.get_child())->set_text (_("Cmt"));
539 comment_button
.modify_bg (STATE_NORMAL
, color());
540 ((Gtk::Label
*)comment_button
.get_child())->set_text (_("*Cmt*"));
543 ((Gtk::Label
*)gpm
.gain_automation_style_button
.get_child())->set_text (gpm
.short_astyle_string(_route
->gain_automation_curve().automation_style()));
544 ((Gtk::Label
*)gpm
.gain_automation_state_button
.get_child())->set_text (gpm
.short_astate_string(_route
->gain_automation_curve().automation_state()));
545 ((Gtk::Label
*)panners
.pan_automation_style_button
.get_child())->set_text (panners
.short_astyle_string(_route
->panner().automation_style()));
546 ((Gtk::Label
*)panners
.pan_automation_state_button
.get_child())->set_text (panners
.short_astate_string(_route
->panner().automation_state()));
547 Gtkmm2ext::set_size_request_to_display_given_text (name_button
, "longest label", 2, 2);
548 set_size_request (max (50, gpm
.get_gm_width()), -1);
551 update_input_display ();
552 update_output_display ();
553 mix_group_changed (0);
561 MixerStrip::set_packed (bool yn
)
568 xml_node
->add_property ("shown_mixer", "yes");
570 xml_node
->add_property ("shown_mixer", "no");
576 MixerStrip::output_press (GdkEventButton
*ev
)
578 using namespace Menu_Helpers
;
579 if (!_session
.engine().connected()) {
580 MessageDialog
msg (_("Not connected to JACK - no I/O changes are possible"));
585 MenuList
& citems
= output_menu
.items();
586 switch (ev
->button
) {
589 output_menu
.set_name ("ArdourContextMenu");
592 citems
.push_back (MenuElem (_("Edit"), mem_fun(*this, &MixerStrip::edit_output_configuration
)));
593 citems
.push_back (SeparatorElem());
594 citems
.push_back (MenuElem (_("Disconnect"), mem_fun (*(static_cast<RouteUI
*>(this)), &RouteUI::disconnect_output
)));
595 citems
.push_back (SeparatorElem());
597 _session
.foreach_connection (this, &MixerStrip::add_connection_to_output_menu
);
599 output_menu
.popup (1, ev
->time
);
609 MixerStrip::edit_output_configuration ()
611 if (output_selector
== 0) {
612 output_selector
= new IOSelectorWindow (_session
, _route
, false);
615 if (output_selector
->is_visible()) {
616 output_selector
->get_toplevel()->get_window()->raise();
618 output_selector
->show_all ();
623 MixerStrip::edit_input_configuration ()
625 if (input_selector
== 0) {
626 input_selector
= new IOSelectorWindow (_session
, _route
, true);
629 if (input_selector
->is_visible()) {
630 input_selector
->get_toplevel()->get_window()->raise();
632 input_selector
->show_all ();
637 MixerStrip::input_press (GdkEventButton
*ev
)
639 using namespace Menu_Helpers
;
641 MenuList
& citems
= input_menu
.items();
642 input_menu
.set_name ("ArdourContextMenu");
645 if (!_session
.engine().connected()) {
646 MessageDialog
msg (_("Not connected to JACK - no I/O changes are possible"));
651 switch (ev
->button
) {
654 citems
.push_back (MenuElem (_("Edit"), mem_fun(*this, &MixerStrip::edit_input_configuration
)));
655 citems
.push_back (SeparatorElem());
656 citems
.push_back (MenuElem (_("Disconnect"), mem_fun (*(static_cast<RouteUI
*>(this)), &RouteUI::disconnect_input
)));
657 citems
.push_back (SeparatorElem());
659 _session
.foreach_connection (this, &MixerStrip::add_connection_to_input_menu
);
661 input_menu
.popup (1, ev
->time
);
671 MixerStrip::connection_input_chosen (ARDOUR::Connection
*c
)
673 if (!ignore_toggle
) {
676 _route
->use_input_connection (*c
, this);
679 catch (AudioEngine::PortRegistrationFailure
& err
) {
680 error
<< _("could not register new ports required for that connection")
687 MixerStrip::connection_output_chosen (ARDOUR::Connection
*c
)
689 if (!ignore_toggle
) {
692 _route
->use_output_connection (*c
, this);
695 catch (AudioEngine::PortRegistrationFailure
& err
) {
696 error
<< _("could not register new ports required for that connection")
703 MixerStrip::add_connection_to_input_menu (ARDOUR::Connection
* c
)
705 using namespace Menu_Helpers
;
707 if (dynamic_cast<InputConnection
*> (c
) == 0) {
711 MenuList
& citems
= input_menu
.items();
713 if (c
->nports() == _route
->n_inputs()) {
715 citems
.push_back (CheckMenuElem (c
->name(), bind (mem_fun(*this, &MixerStrip::connection_input_chosen
), c
)));
717 ARDOUR::Connection
*current
= _route
->input_connection();
720 ignore_toggle
= true;
721 dynamic_cast<CheckMenuItem
*> (&citems
.back())->set_active (true);
722 ignore_toggle
= false;
728 MixerStrip::add_connection_to_output_menu (ARDOUR::Connection
* c
)
730 using namespace Menu_Helpers
;
732 if (dynamic_cast<OutputConnection
*> (c
) == 0) {
736 if (c
->nports() == _route
->n_outputs()) {
738 MenuList
& citems
= output_menu
.items();
739 citems
.push_back (CheckMenuElem (c
->name(), bind (mem_fun(*this, &MixerStrip::connection_output_chosen
), c
)));
741 ARDOUR::Connection
*current
= _route
->output_connection();
744 ignore_toggle
= true;
745 dynamic_cast<CheckMenuItem
*> (&citems
.back())->set_active (true);
746 ignore_toggle
= false;
752 MixerStrip::update_diskstream_display ()
755 update_input_display ();
757 if (is_audio_track()) {
759 if (input_selector
) {
760 input_selector
->hide_all ();
767 show_passthru_color ();
772 MixerStrip::connect_to_pan ()
774 ENSURE_GUI_THREAD(mem_fun(*this, &MixerStrip::connect_to_pan
));
776 panstate_connection
.disconnect ();
777 panstyle_connection
.disconnect ();
779 if (!_route
->panner().empty()) {
780 StreamPanner
* sp
= _route
->panner().front();
782 panstate_connection
= sp
->automation().automation_state_changed
.connect (mem_fun(panners
, &PannerUI::pan_automation_state_changed
));
783 panstyle_connection
= sp
->automation().automation_style_changed
.connect (mem_fun(panners
, &PannerUI::pan_automation_style_changed
));
786 panners
.pan_changed (this);
790 * Output port labelling
791 * =====================
793 * Case 1: Each output has one connection, all connections are to system:playback_%i
794 * out 1 -> system:playback_1
795 * out 2 -> system:playback_2
796 * out 3 -> system:playback_3
799 * Case 2: Each output has one connection, all connections are to ardour:track_x/in 1
800 * out 1 -> ardour:track_x/in 1
801 * out 2 -> ardour:track_x/in 2
802 * Display as: track_x
804 * Case 3: Each output has one connection, all connections are to Jack client "program x"
805 * out 1 -> program x:foo
806 * out 2 -> program x:foo
807 * Display as: program x
809 * Case 4: No connections (Disconnected)
812 * Default case (unusual routing):
813 * Display as: *number of connections*
817 * .-----------------------------------------------.
819 * | out 1 -> ardour:master/in 1, jamin:input/in 1 |
820 * | out 2 -> ardour:master/in 2, jamin:input/in 2 |
821 * '-----------------------------------------------'
822 * .-----------------------------------------------.
825 * '-----------------------------------------------'
829 MixerStrip::update_io_button (boost::shared_ptr
<ARDOUR::Route
> route
, Width width
, bool for_input
)
834 const char **connections
;
836 uint32_t connection_index
= 0;
837 uint32_t total_connection_count
= 0;
838 uint32_t io_connection_count
= 0;
839 uint32_t ardour_connection_count
= 0;
840 uint32_t system_connection_count
= 0;
841 uint32_t other_connection_count
= 0;
847 bool have_label
= false;
848 bool each_io_has_one_connection
= true;
850 string connection_name
;
851 string ardour_track_name
;
852 string other_connection_type
;
856 ostringstream tooltip
;
859 tooltip
<< route
->name();
862 io_count
= route
->n_inputs();
864 io_count
= route
->n_outputs();
867 for (io_index
= 0; io_index
< io_count
; ++io_index
) {
869 port
= route
->input(io_index
);
871 port
= route
->output(io_index
);
874 connections
= port
->get_connections();
875 io_connection_count
= 0;
878 for (connection_index
= 0; connections
[connection_index
]; ++connection_index
) {
879 connection_name
= connections
[connection_index
];
881 if (connection_index
== 0) {
882 tooltip
<< endl
<< port
->name().substr(port
->name().find("/") + 1) << " -> " << connection_name
;
884 tooltip
<< ", " << connection_name
;
887 if (connection_name
.find("ardour:") == 0) {
888 if (ardour_track_name
.empty()) {
889 // "ardour:Master/in 1" -> "ardour:Master/"
890 string::size_type slash
= connection_name
.find("/");
891 if (slash
!= string::npos
) {
892 ardour_track_name
= connection_name
.substr(0, slash
+ 1);
896 if (connection_name
.find(ardour_track_name
) == 0) {
897 ++ardour_connection_count
;
899 } else if (connection_name
.find("system:") == 0) {
901 // "system:capture_123" -> "123"
902 system_port
= connection_name
.substr(15);
904 // "system:playback_123" -> "123"
905 system_port
= connection_name
.substr(16);
908 if (system_ports
.empty()) {
909 system_ports
+= system_port
;
911 system_ports
+= "/" + system_port
;
914 ++system_connection_count
;
916 if (other_connection_type
.empty()) {
917 // "jamin:in 1" -> "jamin:"
918 other_connection_type
= connection_name
.substr(0, connection_name
.find(":") + 1);
921 if (connection_name
.find(other_connection_type
) == 0) {
922 ++other_connection_count
;
926 ++total_connection_count
;
927 ++io_connection_count
;
931 if (io_connection_count
!= 1) {
932 each_io_has_one_connection
= false;
936 if (total_connection_count
== 0) {
937 tooltip
<< endl
<< _("Disconnected");
940 tooltip_cstr
= new char[tooltip
.str().size() + 1];
941 strcpy(tooltip_cstr
, tooltip
.str().c_str());
944 ARDOUR_UI::instance()->set_tip (&input_button
, tooltip_cstr
, "");
946 ARDOUR_UI::instance()->set_tip (&output_button
, tooltip_cstr
, "");
949 if (each_io_has_one_connection
) {
950 if ((total_connection_count
== ardour_connection_count
)) {
951 // all connections are to the same track in ardour
952 // "ardour:Master/" -> "Master"
953 string::size_type slash
= ardour_track_name
.find("/");
954 if (slash
!= string::npos
) {
955 label
<< ardour_track_name
.substr(7, slash
- 7);
959 else if (total_connection_count
== system_connection_count
) {
960 // all connections are to system ports
961 label
<< system_ports
;
964 else if (total_connection_count
== other_connection_count
) {
965 // all connections are to the same external program eg jamin
966 // "jamin:" -> "jamin"
967 label
<< other_connection_type
.substr(0, other_connection_type
.size() - 1);
973 if (total_connection_count
== 0) {
978 label
<< "*" << total_connection_count
<< "*";
984 label_string
= label
.str().substr(0, 6);
987 label_string
= label
.str().substr(0, 3);
991 label_cstr
= new char[label_string
.size() + 1];
992 strcpy(label_cstr
, label_string
.c_str());
995 input_label
.set_text (label_cstr
);
997 output_label
.set_text (label_cstr
);
1002 MixerStrip::update_input_display ()
1004 update_io_button (_route
, _width
, true);
1005 panners
.setup_pan ();
1009 MixerStrip::update_output_display ()
1011 update_io_button (_route
, _width
, false);
1012 gpm
.setup_meters ();
1013 panners
.setup_pan ();
1017 MixerStrip::fast_update ()
1019 gpm
.update_meters ();
1023 MixerStrip::diskstream_changed ()
1025 Gtkmm2ext::UI::instance()->call_slot (mem_fun(*this, &MixerStrip::update_diskstream_display
));
1029 MixerStrip::input_changed (IOChange change
, void *src
)
1031 Gtkmm2ext::UI::instance()->call_slot (mem_fun(*this, &MixerStrip::update_input_display
));
1032 set_width(_width
, this);
1036 MixerStrip::output_changed (IOChange change
, void *src
)
1038 Gtkmm2ext::UI::instance()->call_slot (mem_fun(*this, &MixerStrip::update_output_display
));
1039 set_width(_width
, this);
1044 MixerStrip::comment_editor_done_editing()
1046 string str
= comment_area
->get_buffer()->get_text();
1047 if (_route
->comment() != str
) {
1048 _route
->set_comment (str
, this);
1053 if (! str
.empty()) {
1054 comment_button
.modify_bg (STATE_NORMAL
, color());
1055 ((Gtk::Label
*)comment_button
.get_child())->set_text (_("*Comments*"));
1057 comment_button
.unset_bg (STATE_NORMAL
);
1058 ((Gtk::Label
*)comment_button
.get_child())->set_text (_("Comments"));
1063 if (! str
.empty()) {
1064 comment_button
.modify_bg (STATE_NORMAL
, color());
1065 ((Gtk::Label
*)comment_button
.get_child())->set_text (_("*Cmt*"));
1067 comment_button
.unset_bg (STATE_NORMAL
);
1068 ((Gtk::Label
*)comment_button
.get_child())->set_text (_("Cmt"));
1073 ARDOUR_UI::instance()->tooltips().set_tip (comment_button
,
1074 str
.empty() ? _("Click to Add/Edit Comments") : str
);
1080 MixerStrip::comment_button_clicked ()
1082 if (comment_window
== 0) {
1083 setup_comment_editor ();
1086 int x
, y
, cw_width
, cw_height
;
1088 if (comment_window
->is_visible()) {
1089 comment_window
->hide ();
1093 comment_window
->get_size (cw_width
, cw_height
);
1094 comment_window
->get_position(x
, y
);
1095 comment_window
->move(x
, y
- (cw_height
/ 2) - 45);
1097 half the dialog height minus the comments button height
1098 with some window decoration fudge thrown in.
1101 comment_window
->show();
1102 comment_window
->present();
1106 MixerStrip::setup_comment_editor ()
1109 title
= _route
->name();
1110 title
+= _(": comment editor");
1112 comment_window
= new ArdourDialog (title
, false);
1113 comment_window
->set_position (Gtk::WIN_POS_MOUSE
);
1114 comment_window
->set_skip_taskbar_hint (true);
1115 comment_window
->signal_hide().connect (mem_fun(*this, &MixerStrip::comment_editor_done_editing
));
1117 comment_area
= manage (new TextView());
1118 comment_area
->set_name ("MixerTrackCommentArea");
1119 comment_area
->set_size_request (110, 178);
1120 comment_area
->set_wrap_mode (WRAP_WORD
);
1121 comment_area
->set_editable (true);
1122 comment_area
->get_buffer()->set_text (_route
->comment());
1123 comment_area
->show ();
1125 comment_window
->get_vbox()->pack_start (*comment_area
);
1126 comment_window
->get_action_area()->hide();
1130 MixerStrip::comment_changed (void *src
)
1132 ENSURE_GUI_THREAD(bind (mem_fun(*this, &MixerStrip::comment_changed
), src
));
1135 ignore_comment_edit
= true;
1137 comment_area
->get_buffer()->set_text (_route
->comment());
1139 ignore_comment_edit
= false;
1144 MixerStrip::set_mix_group (RouteGroup
*rg
)
1146 _route
->set_mix_group (rg
, this);
1150 MixerStrip::add_mix_group_to_menu (RouteGroup
*rg
, RadioMenuItem::Group
* group
)
1152 using namespace Menu_Helpers
;
1154 MenuList
& items
= group_menu
->items();
1156 items
.push_back (RadioMenuElem (*group
, rg
->name(), bind (mem_fun(*this, &MixerStrip::set_mix_group
), rg
)));
1158 if (_route
->mix_group() == rg
) {
1159 static_cast<RadioMenuItem
*>(&items
.back())->set_active ();
1164 MixerStrip::select_mix_group (GdkEventButton
*ev
)
1166 using namespace Menu_Helpers
;
1168 if (group_menu
== 0) {
1169 group_menu
= new Menu
;
1171 group_menu
->set_name ("ArdourContextMenu");
1172 MenuList
& items
= group_menu
->items();
1173 RadioMenuItem::Group group
;
1175 switch (ev
->button
) {
1179 items
.push_back (RadioMenuElem (group
, _("No group"), bind (mem_fun(*this, &MixerStrip::set_mix_group
), (RouteGroup
*) 0)));
1181 _session
.foreach_mix_group (bind (mem_fun (*this, &MixerStrip::add_mix_group_to_menu
), &group
));
1183 group_menu
->popup (1, ev
->time
);
1194 MixerStrip::mix_group_changed (void *ignored
)
1196 ENSURE_GUI_THREAD(bind (mem_fun(*this, &MixerStrip::mix_group_changed
), ignored
));
1198 RouteGroup
*rg
= _route
->mix_group();
1201 group_label
.set_text (PBD::short_version (rg
->name(), 5));
1205 group_label
.set_text (_("Grp"));
1208 group_label
.set_text (_("~G"));
1216 MixerStrip::route_gui_changed (string what_changed
, void* ignored
)
1218 ENSURE_GUI_THREAD(bind (mem_fun(*this, &MixerStrip::route_gui_changed
), what_changed
, ignored
));
1220 if (what_changed
== "color") {
1221 if (set_color_from_route () == 0) {
1222 show_route_color ();
1228 MixerStrip::show_route_color ()
1230 name_button
.modify_bg (STATE_NORMAL
, color());
1231 top_event_box
.modify_bg (STATE_NORMAL
, color());
1232 route_active_changed ();
1236 MixerStrip::show_passthru_color ()
1238 route_active_changed ();
1242 MixerStrip::build_route_ops_menu ()
1244 using namespace Menu_Helpers
;
1245 route_ops_menu
= new Menu
;
1246 route_ops_menu
->set_name ("ArdourContextMenu");
1248 MenuList
& items
= route_ops_menu
->items();
1250 items
.push_back (MenuElem (_("Save As Template"), mem_fun(*this, &RouteUI::save_as_template
)));
1251 items
.push_back (MenuElem (_("Rename"), mem_fun(*this, &RouteUI::route_rename
)));
1252 rename_menu_item
= &items
.back();
1253 items
.push_back (SeparatorElem());
1254 items
.push_back (CheckMenuElem (_("Active"), mem_fun (*this, &RouteUI::toggle_route_active
)));
1255 route_active_menu_item
= dynamic_cast<CheckMenuItem
*> (&items
.back());
1256 route_active_menu_item
->set_active (_route
->active());
1257 items
.push_back (SeparatorElem());
1258 items
.push_back (CheckMenuElem (_("Invert Polarity"), mem_fun (*this, &RouteUI::toggle_polarity
)));
1259 polarity_menu_item
= dynamic_cast<CheckMenuItem
*> (&items
.back());
1260 polarity_menu_item
->set_active (_route
->phase_invert());
1261 items
.push_back (CheckMenuElem (_("Protect against denormals"), mem_fun (*this, &RouteUI::toggle_denormal_protection
)));
1262 denormal_menu_item
= dynamic_cast<CheckMenuItem
*> (&items
.back());
1263 denormal_menu_item
->set_active (_route
->denormal_protection());
1265 if (!Profile
->get_sae()) {
1266 build_remote_control_menu ();
1267 items
.push_back (SeparatorElem());
1268 items
.push_back (MenuElem (_("Remote Control ID"), *remote_control_menu
));
1271 items
.push_back (SeparatorElem());
1272 items
.push_back (MenuElem (_("Remove"), mem_fun(*this, &RouteUI::remove_this_route
)));
1276 MixerStrip::name_button_button_press (GdkEventButton
* ev
)
1278 if (ev
->button
== 1 || ev
->button
== 3) {
1279 list_route_operations ();
1280 /* do not allow rename if the track is record-enabled */
1281 rename_menu_item
->set_sensitive (!_route
->record_enabled());
1282 route_ops_menu
->popup (1, ev
->time
);
1288 MixerStrip::list_route_operations ()
1290 if (route_ops_menu
== 0) {
1291 build_route_ops_menu ();
1294 refresh_remote_control_menu();
1299 MixerStrip::speed_adjustment_changed ()
1301 /* since there is a usable speed adjustment, there has to be a diskstream */
1302 if (!ignore_speed_adjustment
) {
1303 get_diskstream()->set_speed (speed_adjustment
.get_value());
1308 MixerStrip::speed_changed ()
1310 Gtkmm2ext::UI::instance()->call_slot (mem_fun(*this, &MixerStrip::update_speed_display
));
1314 MixerStrip::update_speed_display ()
1318 val
= get_diskstream()->speed();
1321 speed_spinner
.set_name ("MixerStripSpeedBaseNotOne");
1323 speed_spinner
.set_name ("MixerStripSpeedBase");
1326 if (speed_adjustment
.get_value() != val
) {
1327 ignore_speed_adjustment
= true;
1328 speed_adjustment
.set_value (val
);
1329 ignore_speed_adjustment
= false;
1335 MixerStrip::set_selected (bool yn
)
1337 AxisView::set_selected (yn
);
1339 global_frame
.set_shadow_type (Gtk::SHADOW_ETCHED_OUT
);
1340 global_frame
.set_name ("MixerStripSelectedFrame");
1342 global_frame
.set_shadow_type (Gtk::SHADOW_IN
);
1343 global_frame
.set_name ("MixerStripFrame");
1345 global_frame
.queue_draw ();
1349 MixerStrip::name_changed (void *src
)
1353 RouteUI::name_changed (src
);
1356 name_label
.set_text (PBD::short_version (_route
->name(), 5));
1359 if (_route
->phase_invert()) {
1360 name_label
.set_text (X_("Ø ") + name_label
.get_text());
1365 MixerStrip::width_clicked ()
1369 set_width (Narrow
, this);
1372 set_width (Wide
, this);
1378 MixerStrip::hide_clicked ()
1380 // LAME fix to reset the button status for when it is redisplayed (part 1)
1381 hide_button
.set_sensitive(false);
1384 Hiding(); /* EMIT_SIGNAL */
1386 _mixer
.hide_strip (this);
1390 hide_button
.set_sensitive(true);
1394 MixerStrip::set_embedded (bool yn
)
1400 MixerStrip::map_frozen ()
1402 ENSURE_GUI_THREAD (mem_fun(*this, &MixerStrip::map_frozen
));
1404 boost::shared_ptr
<AudioTrack
> at
= audio_track();
1407 switch (at
->freeze_state()) {
1408 case AudioTrack::Frozen
:
1409 pre_redirect_box
.set_sensitive (false);
1410 post_redirect_box
.set_sensitive (false);
1411 speed_spinner
.set_sensitive (false);
1414 pre_redirect_box
.set_sensitive (true);
1415 post_redirect_box
.set_sensitive (true);
1416 speed_spinner
.set_sensitive (true);
1417 // XXX need some way, maybe, to retoggle redirect editors
1422 hide_redirect_editors ();
1426 MixerStrip::hide_redirect_editors ()
1428 _route
->foreach_redirect (this, &MixerStrip::hide_redirect_editor
);
1432 MixerStrip::hide_redirect_editor (boost::shared_ptr
<Redirect
> redirect
)
1434 void* gui
= redirect
->get_gui ();
1437 static_cast<Gtk::Widget
*>(gui
)->hide ();
1442 MixerStrip::route_active_changed ()
1444 RouteUI::route_active_changed ();
1446 if (is_audio_track()) {
1447 if (_route
->active()) {
1448 set_name ("AudioTrackStripBase");
1449 gpm
.set_meter_strip_name ("AudioTrackMetrics");
1451 set_name ("AudioTrackStripBaseInactive");
1452 gpm
.set_meter_strip_name ("AudioTrackMetricsInactive");
1454 gpm
.set_fader_name ("AudioTrackFader");
1455 } else { // FIXME: assumed audio bus
1456 if (_route
->active()) {
1457 set_name ("AudioBusStripBase");
1458 gpm
.set_meter_strip_name ("AudioBusMetrics");
1460 set_name ("AudioBusStripBaseInactive");
1461 gpm
.set_meter_strip_name ("AudioBusMetricsInactive");
1463 gpm
.set_fader_name ("AudioBusFader");
1468 MixerStrip::mix_group() const
1470 return _route
->mix_group();
1474 MixerStrip::engine_stopped ()
1479 MixerStrip::engine_running ()
1484 MixerStrip::meter_changed (void *src
)
1487 ENSURE_GUI_THREAD (bind (mem_fun(*this, &MixerStrip::meter_changed
), src
));
1489 switch (_route
->meter_point()) {
1491 meter_point_label
.set_text (_("input"));
1495 meter_point_label
.set_text (_("pre"));
1498 case MeterPostFader
:
1499 meter_point_label
.set_text (_("post"));
1503 gpm
.setup_meters ();
1504 // reset peak when meter point changes
1505 gpm
.reset_peak_display();
1506 set_width (_width
, this);