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
;
313 MixerStrip::set_route (boost::shared_ptr
<Route
> rt
)
315 if (rec_enable_button
->get_parent()) {
316 button_table
.remove (*rec_enable_button
);
319 #ifdef VARISPEED_IN_MIXER_STRIP
320 if (speed_frame
->get_parent()) {
321 button_table
.remove (*speed_frame
);
325 RouteUI::set_route (rt
);
327 if (input_selector
) {
328 delete input_selector
;
332 if (output_selector
) {
333 delete output_selector
;
339 pre_redirect_box
.set_route (rt
);
340 post_redirect_box
.set_route (rt
);
342 if (set_color_from_route()) {
343 set_color (unique_random_color());
346 if (_mixer_owned
&& (route()->master() || route()->control())) {
348 if (scrollbar_height
== 0) {
349 HScrollbar scrollbar
;
350 Gtk::Requisition
requisition(scrollbar
.size_request ());
351 scrollbar_height
= requisition
.height
;
354 EventBox
* spacer
= manage (new EventBox
);
355 spacer
->set_size_request (-1, scrollbar_height
);
356 global_vpacker
.pack_start (*spacer
, false, false);
359 if (is_audio_track()) {
361 boost::shared_ptr
<AudioTrack
> at
= audio_track();
363 connections
.push_back (at
->FreezeChange
.connect (mem_fun(*this, &MixerStrip::map_frozen
)));
365 #ifdef VARISPEED_IN_MIXER_STRIP
366 speed_adjustment
.signal_value_changed().connect (mem_fun(*this, &MixerStrip::speed_adjustment_changed
));
368 speed_frame
.set_name ("BaseFrame");
369 speed_frame
.set_shadow_type (Gtk::SHADOW_IN
);
370 speed_frame
.add (speed_spinner
);
372 speed_spinner
.set_print_func (speed_printer
, 0);
374 ARDOUR_UI::instance()->tooltips().set_tip (speed_spinner
, _("Varispeed"));
376 button_table
.attach (speed_frame
, 0, 2, 5, 6);
377 #endif /* VARISPEED_IN_MIXER_STRIP */
379 button_table
.attach (*rec_enable_button
, 0, 2, 2, 3);
380 rec_enable_button
->set_sensitive (_session
.writable());
381 rec_enable_button
->show();
384 if (_route
->phase_invert()) {
385 name_label
.set_text (X_("Ø ") + name_label
.get_text());
387 name_label
.set_text (_route
->name());
390 switch (_route
->meter_point()) {
392 meter_point_label
.set_text (_("input"));
396 meter_point_label
.set_text (_("pre"));
400 meter_point_label
.set_text (_("post"));
404 delete route_ops_menu
;
407 ARDOUR_UI::instance()->tooltips().set_tip (comment_button
, _route
->comment().empty() ?
408 _("Click to Add/Edit Comments"):
411 connections
.push_back (_route
->meter_change
.connect (mem_fun(*this, &MixerStrip::meter_changed
)));
412 connections
.push_back (_route
->input_changed
.connect (mem_fun(*this, &MixerStrip::input_changed
)));
413 connections
.push_back (_route
->output_changed
.connect (mem_fun(*this, &MixerStrip::output_changed
)));
414 connections
.push_back (_route
->mute_changed
.connect (mem_fun(*this, &RouteUI::mute_changed
)));
415 connections
.push_back (_route
->solo_changed
.connect (mem_fun(*this, &RouteUI::solo_changed
)));
416 connections
.push_back (_route
->solo_safe_changed
.connect (mem_fun(*this, &RouteUI::solo_changed
)));
417 connections
.push_back (_route
->mix_group_changed
.connect (mem_fun(*this, &MixerStrip::mix_group_changed
)));
418 connections
.push_back (_route
->panner().Changed
.connect (mem_fun(*this, &MixerStrip::connect_to_pan
)));
420 if (is_audio_track()) {
421 connections
.push_back (audio_track()->DiskstreamChanged
.connect (mem_fun(*this, &MixerStrip::diskstream_changed
)));
422 connections
.push_back (get_diskstream()->SpeedChanged
.connect (mem_fun(*this, &MixerStrip::speed_changed
)));
425 connections
.push_back (_route
->name_changed
.connect (mem_fun(*this, &RouteUI::name_changed
)));
426 connections
.push_back (_route
->comment_changed
.connect (mem_fun(*this, &MixerStrip::comment_changed
)));
427 connections
.push_back (_route
->gui_changed
.connect (mem_fun(*this, &MixerStrip::route_gui_changed
)));
429 set_stuff_from_route ();
431 /* now force an update of all the various elements */
433 pre_redirect_box
.update();
434 post_redirect_box
.update();
439 mix_group_changed (0);
443 panners
.setup_pan ();
445 if (is_audio_track()) {
449 update_diskstream_display ();
450 update_input_display ();
451 update_output_display ();
455 MixerStrip::set_stuff_from_route ()
461 /* if width is not set, it will be set by the MixerUI or editor */
463 if ((prop
= xml_node
->property ("strip_width")) != 0) {
464 set_width (Width (string_2_enum (prop
->value(), _width
)), this);
467 if ((prop
= xml_node
->property ("shown_mixer")) != 0) {
468 if (prop
->value() == "no") {
469 _marked_for_display
= false;
471 _marked_for_display
= true;
474 /* backwards compatibility */
475 _marked_for_display
= true;
480 MixerStrip::set_width (Width w
, void* owner
)
482 /* always set the gpm width again, things may be hidden */
485 panners
.set_width (w
);
486 pre_redirect_box
.set_width (w
);
487 post_redirect_box
.set_width (w
);
489 _width_owner
= owner
;
495 if (_width_owner
== this) {
496 xml_node
->add_property ("strip_width", enum_2_string (_width
));
502 if (rec_enable_button
) {
503 ((Gtk::Label
*)rec_enable_button
->get_child())->set_text (_("Record"));
505 ((Gtk::Label
*)mute_button
->get_child())->set_text (_("Mute"));
506 ((Gtk::Label
*)solo_button
->get_child())->set_text (_("Solo"));
508 if (_route
->comment() == "") {
509 comment_button
.unset_bg (STATE_NORMAL
);
510 ((Gtk::Label
*)comment_button
.get_child())->set_text (_("Comments"));
512 comment_button
.modify_bg (STATE_NORMAL
, color());
513 ((Gtk::Label
*)comment_button
.get_child())->set_text (_("*Comments*"));
516 ((Gtk::Label
*)gpm
.gain_automation_style_button
.get_child())->set_text (gpm
.astyle_string(_route
->gain_automation_curve().automation_style()));
517 ((Gtk::Label
*)gpm
.gain_automation_state_button
.get_child())->set_text (gpm
.astate_string(_route
->gain_automation_curve().automation_state()));
518 ((Gtk::Label
*)panners
.pan_automation_style_button
.get_child())->set_text (panners
.astyle_string(_route
->panner().automation_style()));
519 ((Gtk::Label
*)panners
.pan_automation_state_button
.get_child())->set_text (panners
.astate_string(_route
->panner().automation_state()));
520 Gtkmm2ext::set_size_request_to_display_given_text (name_button
, "long", 2, 2);
521 set_size_request (-1, -1);
525 if (rec_enable_button
) {
526 ((Gtk::Label
*)rec_enable_button
->get_child())->set_text (_("Rec"));
528 ((Gtk::Label
*)mute_button
->get_child())->set_text (_("M"));
529 ((Gtk::Label
*)solo_button
->get_child())->set_text (_("S"));
531 if (_route
->comment() == "") {
532 comment_button
.unset_bg (STATE_NORMAL
);
533 ((Gtk::Label
*)comment_button
.get_child())->set_text (_("Cmt"));
535 comment_button
.modify_bg (STATE_NORMAL
, color());
536 ((Gtk::Label
*)comment_button
.get_child())->set_text (_("*Cmt*"));
539 ((Gtk::Label
*)gpm
.gain_automation_style_button
.get_child())->set_text (gpm
.short_astyle_string(_route
->gain_automation_curve().automation_style()));
540 ((Gtk::Label
*)gpm
.gain_automation_state_button
.get_child())->set_text (gpm
.short_astate_string(_route
->gain_automation_curve().automation_state()));
541 ((Gtk::Label
*)panners
.pan_automation_style_button
.get_child())->set_text (panners
.short_astyle_string(_route
->panner().automation_style()));
542 ((Gtk::Label
*)panners
.pan_automation_state_button
.get_child())->set_text (panners
.short_astate_string(_route
->panner().automation_state()));
543 Gtkmm2ext::set_size_request_to_display_given_text (name_button
, "longest label", 2, 2);
544 set_size_request (max (50, gpm
.get_gm_width()), -1);
547 update_input_display ();
548 update_output_display ();
549 mix_group_changed (0);
557 MixerStrip::set_packed (bool yn
)
564 xml_node
->add_property ("shown_mixer", "yes");
566 xml_node
->add_property ("shown_mixer", "no");
572 MixerStrip::output_press (GdkEventButton
*ev
)
574 using namespace Menu_Helpers
;
575 if (!_session
.engine().connected()) {
576 MessageDialog
msg (_("Not connected to JACK - no I/O changes are possible"));
581 MenuList
& citems
= output_menu
.items();
582 switch (ev
->button
) {
585 output_menu
.set_name ("ArdourContextMenu");
588 citems
.push_back (MenuElem (_("Edit"), mem_fun(*this, &MixerStrip::edit_output_configuration
)));
589 citems
.push_back (SeparatorElem());
590 citems
.push_back (MenuElem (_("Disconnect"), mem_fun (*(static_cast<RouteUI
*>(this)), &RouteUI::disconnect_output
)));
591 citems
.push_back (SeparatorElem());
593 _session
.foreach_connection (this, &MixerStrip::add_connection_to_output_menu
);
595 output_menu
.popup (1, ev
->time
);
605 MixerStrip::edit_output_configuration ()
607 if (output_selector
== 0) {
608 output_selector
= new IOSelectorWindow (_session
, _route
, false);
611 if (output_selector
->is_visible()) {
612 output_selector
->get_toplevel()->get_window()->raise();
614 output_selector
->show_all ();
619 MixerStrip::edit_input_configuration ()
621 if (input_selector
== 0) {
622 input_selector
= new IOSelectorWindow (_session
, _route
, true);
625 if (input_selector
->is_visible()) {
626 input_selector
->get_toplevel()->get_window()->raise();
628 input_selector
->show_all ();
633 MixerStrip::input_press (GdkEventButton
*ev
)
635 using namespace Menu_Helpers
;
637 MenuList
& citems
= input_menu
.items();
638 input_menu
.set_name ("ArdourContextMenu");
641 if (!_session
.engine().connected()) {
642 MessageDialog
msg (_("Not connected to JACK - no I/O changes are possible"));
647 switch (ev
->button
) {
650 citems
.push_back (MenuElem (_("Edit"), mem_fun(*this, &MixerStrip::edit_input_configuration
)));
651 citems
.push_back (SeparatorElem());
652 citems
.push_back (MenuElem (_("Disconnect"), mem_fun (*(static_cast<RouteUI
*>(this)), &RouteUI::disconnect_input
)));
653 citems
.push_back (SeparatorElem());
655 _session
.foreach_connection (this, &MixerStrip::add_connection_to_input_menu
);
657 input_menu
.popup (1, ev
->time
);
667 MixerStrip::connection_input_chosen (ARDOUR::Connection
*c
)
669 if (!ignore_toggle
) {
672 _route
->use_input_connection (*c
, this);
675 catch (AudioEngine::PortRegistrationFailure
& err
) {
676 error
<< _("could not register new ports required for that connection")
683 MixerStrip::connection_output_chosen (ARDOUR::Connection
*c
)
685 if (!ignore_toggle
) {
688 _route
->use_output_connection (*c
, this);
691 catch (AudioEngine::PortRegistrationFailure
& err
) {
692 error
<< _("could not register new ports required for that connection")
699 MixerStrip::add_connection_to_input_menu (ARDOUR::Connection
* c
)
701 using namespace Menu_Helpers
;
703 if (dynamic_cast<InputConnection
*> (c
) == 0) {
707 MenuList
& citems
= input_menu
.items();
709 if (c
->nports() == _route
->n_inputs()) {
711 citems
.push_back (CheckMenuElem (c
->name(), bind (mem_fun(*this, &MixerStrip::connection_input_chosen
), c
)));
713 ARDOUR::Connection
*current
= _route
->input_connection();
716 ignore_toggle
= true;
717 dynamic_cast<CheckMenuItem
*> (&citems
.back())->set_active (true);
718 ignore_toggle
= false;
724 MixerStrip::add_connection_to_output_menu (ARDOUR::Connection
* c
)
726 using namespace Menu_Helpers
;
728 if (dynamic_cast<OutputConnection
*> (c
) == 0) {
732 if (c
->nports() == _route
->n_outputs()) {
734 MenuList
& citems
= output_menu
.items();
735 citems
.push_back (CheckMenuElem (c
->name(), bind (mem_fun(*this, &MixerStrip::connection_output_chosen
), c
)));
737 ARDOUR::Connection
*current
= _route
->output_connection();
740 ignore_toggle
= true;
741 dynamic_cast<CheckMenuItem
*> (&citems
.back())->set_active (true);
742 ignore_toggle
= false;
748 MixerStrip::update_diskstream_display ()
751 update_input_display ();
753 if (is_audio_track()) {
755 if (input_selector
) {
756 input_selector
->hide_all ();
763 show_passthru_color ();
768 MixerStrip::connect_to_pan ()
770 ENSURE_GUI_THREAD(mem_fun(*this, &MixerStrip::connect_to_pan
));
772 panstate_connection
.disconnect ();
773 panstyle_connection
.disconnect ();
775 if (!_route
->panner().empty()) {
776 StreamPanner
* sp
= _route
->panner().front();
778 panstate_connection
= sp
->automation().automation_state_changed
.connect (mem_fun(panners
, &PannerUI::pan_automation_state_changed
));
779 panstyle_connection
= sp
->automation().automation_style_changed
.connect (mem_fun(panners
, &PannerUI::pan_automation_style_changed
));
782 panners
.pan_changed (this);
786 * Output port labelling
787 * =====================
789 * Case 1: Each output has one connection, all connections are to system:playback_%i
790 * out 1 -> system:playback_1
791 * out 2 -> system:playback_2
792 * out 3 -> system:playback_3
795 * Case 2: Each output has one connection, all connections are to ardour:track_x/in 1
796 * out 1 -> ardour:track_x/in 1
797 * out 2 -> ardour:track_x/in 2
798 * Display as: track_x
800 * Case 3: Each output has one connection, all connections are to Jack client "program x"
801 * out 1 -> program x:foo
802 * out 2 -> program x:foo
803 * Display as: program x
805 * Case 4: No connections (Disconnected)
808 * Default case (unusual routing):
809 * Display as: *number of connections*
813 * .-----------------------------------------------.
815 * | out 1 -> ardour:master/in 1, jamin:input/in 1 |
816 * | out 2 -> ardour:master/in 2, jamin:input/in 2 |
817 * '-----------------------------------------------'
818 * .-----------------------------------------------.
821 * '-----------------------------------------------'
825 MixerStrip::update_io_button (boost::shared_ptr
<ARDOUR::Route
> route
, Width width
, bool for_input
)
830 const char **connections
;
832 uint32_t connection_index
;
833 uint32_t total_connection_count
= 0;
834 uint32_t io_connection_count
= 0;
835 uint32_t ardour_connection_count
= 0;
836 uint32_t system_connection_count
= 0;
837 uint32_t other_connection_count
= 0;
843 bool have_label
= false;
844 bool each_io_has_one_connection
= true;
846 string connection_name
;
847 string ardour_track_name
;
848 string other_connection_type
;
852 ostringstream tooltip
;
855 tooltip
<< route
->name();
858 io_count
= route
->n_inputs();
860 io_count
= route
->n_outputs();
863 for (io_index
= 0; io_index
< io_count
; ++io_index
) {
865 port
= route
->input(io_index
);
867 port
= route
->output(io_index
);
870 connections
= port
->get_connections();
871 io_connection_count
= 0;
874 for (connection_index
= 0; connections
[connection_index
]; ++connection_index
) {
875 connection_name
= connections
[connection_index
];
877 if (connection_index
== 0) {
878 tooltip
<< endl
<< port
->name().substr(port
->name().find("/") + 1) << " -> " << connection_name
;
880 tooltip
<< ", " << connection_name
;
883 if (connection_name
.find("ardour:") == 0) {
884 if (ardour_track_name
.empty()) {
885 // "ardour:Master/in 1" -> "ardour:Master/"
886 ardour_track_name
= connection_name
.substr(0, connection_name
.find("/") + 1);
889 if (connection_name
.find(ardour_track_name
) == 0) {
890 ++ardour_connection_count
;
892 } else if (connection_name
.find("system:") == 0) {
894 // "system:capture_123" -> "123"
895 system_port
= connection_name
.substr(15);
897 // "system:playback_123" -> "123"
898 system_port
= connection_name
.substr(16);
901 if (system_ports
.empty()) {
902 system_ports
+= system_port
;
904 system_ports
+= "/" + system_port
;
907 ++system_connection_count
;
909 if (other_connection_type
.empty()) {
910 // "jamin:in 1" -> "jamin:"
911 other_connection_type
= connection_name
.substr(0, connection_name
.find(":") + 1);
914 if (connection_name
.find(other_connection_type
) == 0) {
915 ++other_connection_count
;
919 ++total_connection_count
;
920 ++io_connection_count
;
924 if (io_connection_count
!= 1) {
925 each_io_has_one_connection
= false;
929 if (total_connection_count
== 0) {
930 tooltip
<< endl
<< _("Disconnected");
933 tooltip_cstr
= new char[tooltip
.str().size() + 1];
934 strcpy(tooltip_cstr
, tooltip
.str().c_str());
937 ARDOUR_UI::instance()->set_tip (&input_button
, tooltip_cstr
, "");
939 ARDOUR_UI::instance()->set_tip (&output_button
, tooltip_cstr
, "");
942 if (each_io_has_one_connection
) {
943 if ((total_connection_count
== ardour_connection_count
)) {
944 // all connections are to the same track in ardour
945 // "ardour:Master/" -> "Master"
946 label
<< ardour_track_name
.substr(7, ardour_track_name
.find("/") - 7);
949 else if (total_connection_count
== system_connection_count
) {
950 // all connections are to system ports
951 label
<< system_ports
;
954 else if (total_connection_count
== other_connection_count
) {
955 // all connections are to the same external program eg jamin
956 // "jamin:" -> "jamin"
957 label
<< other_connection_type
.substr(0, other_connection_type
.size() - 1);
963 if (total_connection_count
== 0) {
968 label
<< "*" << total_connection_count
<< "*";
974 label_string
= label
.str().substr(0, 6);
977 label_string
= label
.str().substr(0, 3);
981 label_cstr
= new char[label_string
.size() + 1];
982 strcpy(label_cstr
, label_string
.c_str());
985 input_label
.set_text (label_cstr
);
987 output_label
.set_text (label_cstr
);
992 MixerStrip::update_input_display ()
994 update_io_button (_route
, _width
, true);
995 panners
.setup_pan ();
999 MixerStrip::update_output_display ()
1001 update_io_button (_route
, _width
, false);
1002 gpm
.setup_meters ();
1003 panners
.setup_pan ();
1007 MixerStrip::fast_update ()
1009 gpm
.update_meters ();
1013 MixerStrip::diskstream_changed ()
1015 Gtkmm2ext::UI::instance()->call_slot (mem_fun(*this, &MixerStrip::update_diskstream_display
));
1019 MixerStrip::input_changed (IOChange change
, void *src
)
1021 Gtkmm2ext::UI::instance()->call_slot (mem_fun(*this, &MixerStrip::update_input_display
));
1022 set_width(_width
, this);
1026 MixerStrip::output_changed (IOChange change
, void *src
)
1028 Gtkmm2ext::UI::instance()->call_slot (mem_fun(*this, &MixerStrip::update_output_display
));
1029 set_width(_width
, this);
1034 MixerStrip::comment_editor_done_editing()
1036 string str
= comment_area
->get_buffer()->get_text();
1037 if (_route
->comment() != str
) {
1038 _route
->set_comment (str
, this);
1043 if (! str
.empty()) {
1044 comment_button
.modify_bg (STATE_NORMAL
, color());
1045 ((Gtk::Label
*)comment_button
.get_child())->set_text (_("*Comments*"));
1047 comment_button
.unset_bg (STATE_NORMAL
);
1048 ((Gtk::Label
*)comment_button
.get_child())->set_text (_("Comments"));
1053 if (! str
.empty()) {
1054 comment_button
.modify_bg (STATE_NORMAL
, color());
1055 ((Gtk::Label
*)comment_button
.get_child())->set_text (_("*Cmt*"));
1057 comment_button
.unset_bg (STATE_NORMAL
);
1058 ((Gtk::Label
*)comment_button
.get_child())->set_text (_("Cmt"));
1063 ARDOUR_UI::instance()->tooltips().set_tip (comment_button
,
1064 str
.empty() ? _("Click to Add/Edit Comments") : str
);
1070 MixerStrip::comment_button_clicked ()
1072 if (comment_window
== 0) {
1073 setup_comment_editor ();
1076 int x
, y
, cw_width
, cw_height
;
1078 if (comment_window
->is_visible()) {
1079 comment_window
->hide ();
1083 comment_window
->get_size (cw_width
, cw_height
);
1084 comment_window
->get_position(x
, y
);
1085 comment_window
->move(x
, y
- (cw_height
/ 2) - 45);
1087 half the dialog height minus the comments button height
1088 with some window decoration fudge thrown in.
1091 comment_window
->show();
1092 comment_window
->present();
1096 MixerStrip::setup_comment_editor ()
1099 title
= _route
->name();
1100 title
+= _(": comment editor");
1102 comment_window
= new ArdourDialog (title
, false);
1103 comment_window
->set_position (Gtk::WIN_POS_MOUSE
);
1104 comment_window
->set_skip_taskbar_hint (true);
1105 comment_window
->signal_hide().connect (mem_fun(*this, &MixerStrip::comment_editor_done_editing
));
1107 comment_area
= manage (new TextView());
1108 comment_area
->set_name ("MixerTrackCommentArea");
1109 comment_area
->set_size_request (110, 178);
1110 comment_area
->set_wrap_mode (WRAP_WORD
);
1111 comment_area
->set_editable (true);
1112 comment_area
->get_buffer()->set_text (_route
->comment());
1113 comment_area
->show ();
1115 comment_window
->get_vbox()->pack_start (*comment_area
);
1116 comment_window
->get_action_area()->hide();
1120 MixerStrip::comment_changed (void *src
)
1122 ENSURE_GUI_THREAD(bind (mem_fun(*this, &MixerStrip::comment_changed
), src
));
1125 ignore_comment_edit
= true;
1127 comment_area
->get_buffer()->set_text (_route
->comment());
1129 ignore_comment_edit
= false;
1134 MixerStrip::set_mix_group (RouteGroup
*rg
)
1136 _route
->set_mix_group (rg
, this);
1140 MixerStrip::add_mix_group_to_menu (RouteGroup
*rg
, RadioMenuItem::Group
* group
)
1142 using namespace Menu_Helpers
;
1144 MenuList
& items
= group_menu
->items();
1146 items
.push_back (RadioMenuElem (*group
, rg
->name(), bind (mem_fun(*this, &MixerStrip::set_mix_group
), rg
)));
1148 if (_route
->mix_group() == rg
) {
1149 static_cast<RadioMenuItem
*>(&items
.back())->set_active ();
1154 MixerStrip::select_mix_group (GdkEventButton
*ev
)
1156 using namespace Menu_Helpers
;
1158 if (group_menu
== 0) {
1159 group_menu
= new Menu
;
1161 group_menu
->set_name ("ArdourContextMenu");
1162 MenuList
& items
= group_menu
->items();
1163 RadioMenuItem::Group group
;
1165 switch (ev
->button
) {
1169 items
.push_back (RadioMenuElem (group
, _("No group"), bind (mem_fun(*this, &MixerStrip::set_mix_group
), (RouteGroup
*) 0)));
1171 _session
.foreach_mix_group (bind (mem_fun (*this, &MixerStrip::add_mix_group_to_menu
), &group
));
1173 group_menu
->popup (1, ev
->time
);
1184 MixerStrip::mix_group_changed (void *ignored
)
1186 ENSURE_GUI_THREAD(bind (mem_fun(*this, &MixerStrip::mix_group_changed
), ignored
));
1188 RouteGroup
*rg
= _route
->mix_group();
1191 group_label
.set_text (PBD::short_version (rg
->name(), 5));
1195 group_label
.set_text (_("Grp"));
1198 group_label
.set_text (_("~G"));
1206 MixerStrip::route_gui_changed (string what_changed
, void* ignored
)
1208 ENSURE_GUI_THREAD(bind (mem_fun(*this, &MixerStrip::route_gui_changed
), what_changed
, ignored
));
1210 if (what_changed
== "color") {
1211 if (set_color_from_route () == 0) {
1212 show_route_color ();
1218 MixerStrip::show_route_color ()
1220 name_button
.modify_bg (STATE_NORMAL
, color());
1221 top_event_box
.modify_bg (STATE_NORMAL
, color());
1222 route_active_changed ();
1226 MixerStrip::show_passthru_color ()
1228 route_active_changed ();
1232 MixerStrip::build_route_ops_menu ()
1234 using namespace Menu_Helpers
;
1235 route_ops_menu
= new Menu
;
1236 route_ops_menu
->set_name ("ArdourContextMenu");
1238 MenuList
& items
= route_ops_menu
->items();
1240 items
.push_back (MenuElem (_("Save As Template"), mem_fun(*this, &RouteUI::save_as_template
)));
1241 items
.push_back (MenuElem (_("Rename"), mem_fun(*this, &RouteUI::route_rename
)));
1242 rename_menu_item
= &items
.back();
1243 items
.push_back (SeparatorElem());
1244 items
.push_back (CheckMenuElem (_("Active"), mem_fun (*this, &RouteUI::toggle_route_active
)));
1245 route_active_menu_item
= dynamic_cast<CheckMenuItem
*> (&items
.back());
1246 route_active_menu_item
->set_active (_route
->active());
1247 items
.push_back (SeparatorElem());
1248 items
.push_back (CheckMenuElem (_("Invert Polarity"), mem_fun (*this, &RouteUI::toggle_polarity
)));
1249 polarity_menu_item
= dynamic_cast<CheckMenuItem
*> (&items
.back());
1250 polarity_menu_item
->set_active (_route
->phase_invert());
1251 items
.push_back (CheckMenuElem (_("Protect against denormals"), mem_fun (*this, &RouteUI::toggle_denormal_protection
)));
1252 denormal_menu_item
= dynamic_cast<CheckMenuItem
*> (&items
.back());
1253 denormal_menu_item
->set_active (_route
->denormal_protection());
1255 if (!Profile
->get_sae()) {
1256 build_remote_control_menu ();
1257 items
.push_back (SeparatorElem());
1258 items
.push_back (MenuElem (_("Remote Control ID"), *remote_control_menu
));
1261 items
.push_back (SeparatorElem());
1262 items
.push_back (MenuElem (_("Remove"), mem_fun(*this, &RouteUI::remove_this_route
)));
1266 MixerStrip::name_button_button_press (GdkEventButton
* ev
)
1268 if (ev
->button
== 1 || ev
->button
== 3) {
1269 list_route_operations ();
1270 /* do not allow rename if the track is record-enabled */
1271 rename_menu_item
->set_sensitive (!_route
->record_enabled());
1272 route_ops_menu
->popup (1, ev
->time
);
1278 MixerStrip::list_route_operations ()
1280 if (route_ops_menu
== 0) {
1281 build_route_ops_menu ();
1284 refresh_remote_control_menu();
1289 MixerStrip::speed_adjustment_changed ()
1291 /* since there is a usable speed adjustment, there has to be a diskstream */
1292 if (!ignore_speed_adjustment
) {
1293 get_diskstream()->set_speed (speed_adjustment
.get_value());
1298 MixerStrip::speed_changed ()
1300 Gtkmm2ext::UI::instance()->call_slot (mem_fun(*this, &MixerStrip::update_speed_display
));
1304 MixerStrip::update_speed_display ()
1308 val
= get_diskstream()->speed();
1311 speed_spinner
.set_name ("MixerStripSpeedBaseNotOne");
1313 speed_spinner
.set_name ("MixerStripSpeedBase");
1316 if (speed_adjustment
.get_value() != val
) {
1317 ignore_speed_adjustment
= true;
1318 speed_adjustment
.set_value (val
);
1319 ignore_speed_adjustment
= false;
1325 MixerStrip::set_selected (bool yn
)
1327 AxisView::set_selected (yn
);
1329 global_frame
.set_shadow_type (Gtk::SHADOW_ETCHED_OUT
);
1330 global_frame
.set_name ("MixerStripSelectedFrame");
1332 global_frame
.set_shadow_type (Gtk::SHADOW_IN
);
1333 global_frame
.set_name ("MixerStripFrame");
1335 global_frame
.queue_draw ();
1339 MixerStrip::name_changed (void *src
)
1343 RouteUI::name_changed (src
);
1346 name_label
.set_text (PBD::short_version (_route
->name(), 5));
1349 if (_route
->phase_invert()) {
1350 name_label
.set_text (X_("Ø ") + name_label
.get_text());
1355 MixerStrip::width_clicked ()
1359 set_width (Narrow
, this);
1362 set_width (Wide
, this);
1368 MixerStrip::hide_clicked ()
1370 // LAME fix to reset the button status for when it is redisplayed (part 1)
1371 hide_button
.set_sensitive(false);
1374 Hiding(); /* EMIT_SIGNAL */
1376 _mixer
.hide_strip (this);
1380 hide_button
.set_sensitive(true);
1384 MixerStrip::set_embedded (bool yn
)
1390 MixerStrip::map_frozen ()
1392 ENSURE_GUI_THREAD (mem_fun(*this, &MixerStrip::map_frozen
));
1394 boost::shared_ptr
<AudioTrack
> at
= audio_track();
1397 switch (at
->freeze_state()) {
1398 case AudioTrack::Frozen
:
1399 pre_redirect_box
.set_sensitive (false);
1400 post_redirect_box
.set_sensitive (false);
1401 speed_spinner
.set_sensitive (false);
1404 pre_redirect_box
.set_sensitive (true);
1405 post_redirect_box
.set_sensitive (true);
1406 speed_spinner
.set_sensitive (true);
1407 // XXX need some way, maybe, to retoggle redirect editors
1412 hide_redirect_editors ();
1416 MixerStrip::hide_redirect_editors ()
1418 _route
->foreach_redirect (this, &MixerStrip::hide_redirect_editor
);
1422 MixerStrip::hide_redirect_editor (boost::shared_ptr
<Redirect
> redirect
)
1424 void* gui
= redirect
->get_gui ();
1427 static_cast<Gtk::Widget
*>(gui
)->hide ();
1432 MixerStrip::route_active_changed ()
1434 RouteUI::route_active_changed ();
1436 if (is_audio_track()) {
1437 if (_route
->active()) {
1438 set_name ("AudioTrackStripBase");
1439 gpm
.set_meter_strip_name ("AudioTrackMetrics");
1441 set_name ("AudioTrackStripBaseInactive");
1442 gpm
.set_meter_strip_name ("AudioTrackMetricsInactive");
1444 gpm
.set_fader_name ("AudioTrackFader");
1445 } else { // FIXME: assumed audio bus
1446 if (_route
->active()) {
1447 set_name ("AudioBusStripBase");
1448 gpm
.set_meter_strip_name ("AudioBusMetrics");
1450 set_name ("AudioBusStripBaseInactive");
1451 gpm
.set_meter_strip_name ("AudioBusMetricsInactive");
1453 gpm
.set_fader_name ("AudioBusFader");
1458 MixerStrip::mix_group() const
1460 return _route
->mix_group();
1464 MixerStrip::engine_stopped ()
1469 MixerStrip::engine_running ()
1474 MixerStrip::meter_changed (void *src
)
1477 ENSURE_GUI_THREAD (bind (mem_fun(*this, &MixerStrip::meter_changed
), src
));
1479 switch (_route
->meter_point()) {
1481 meter_point_label
.set_text (_("input"));
1485 meter_point_label
.set_text (_("pre"));
1488 case MeterPostFader
:
1489 meter_point_label
.set_text (_("post"));
1493 gpm
.setup_meters ();
1494 // reset peak when meter point changes
1495 gpm
.reset_peak_display();
1496 set_width (_width
, this);