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
= 0;
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 string::size_type slash
= connection_name
.find("/");
887 if (slash
!= string::npos
) {
888 ardour_track_name
= connection_name
.substr(0, slash
+ 1);
892 if (connection_name
.find(ardour_track_name
) == 0) {
893 ++ardour_connection_count
;
895 } else if (connection_name
.find("system:") == 0) {
897 // "system:capture_123" -> "123"
898 system_port
= connection_name
.substr(15);
900 // "system:playback_123" -> "123"
901 system_port
= connection_name
.substr(16);
904 if (system_ports
.empty()) {
905 system_ports
+= system_port
;
907 system_ports
+= "/" + system_port
;
910 ++system_connection_count
;
912 if (other_connection_type
.empty()) {
913 // "jamin:in 1" -> "jamin:"
914 other_connection_type
= connection_name
.substr(0, connection_name
.find(":") + 1);
917 if (connection_name
.find(other_connection_type
) == 0) {
918 ++other_connection_count
;
922 ++total_connection_count
;
923 ++io_connection_count
;
927 if (io_connection_count
!= 1) {
928 each_io_has_one_connection
= false;
932 if (total_connection_count
== 0) {
933 tooltip
<< endl
<< _("Disconnected");
936 tooltip_cstr
= new char[tooltip
.str().size() + 1];
937 strcpy(tooltip_cstr
, tooltip
.str().c_str());
940 ARDOUR_UI::instance()->set_tip (&input_button
, tooltip_cstr
, "");
942 ARDOUR_UI::instance()->set_tip (&output_button
, tooltip_cstr
, "");
945 if (each_io_has_one_connection
) {
946 if ((total_connection_count
== ardour_connection_count
)) {
947 // all connections are to the same track in ardour
948 // "ardour:Master/" -> "Master"
949 string::size_type slash
= ardour_track_name
.find("/");
950 if (slash
!= string::npos
) {
951 label
<< ardour_track_name
.substr(7, slash
- 7);
955 else if (total_connection_count
== system_connection_count
) {
956 // all connections are to system ports
957 label
<< system_ports
;
960 else if (total_connection_count
== other_connection_count
) {
961 // all connections are to the same external program eg jamin
962 // "jamin:" -> "jamin"
963 label
<< other_connection_type
.substr(0, other_connection_type
.size() - 1);
969 if (total_connection_count
== 0) {
974 label
<< "*" << total_connection_count
<< "*";
980 label_string
= label
.str().substr(0, 6);
983 label_string
= label
.str().substr(0, 3);
987 label_cstr
= new char[label_string
.size() + 1];
988 strcpy(label_cstr
, label_string
.c_str());
991 input_label
.set_text (label_cstr
);
993 output_label
.set_text (label_cstr
);
998 MixerStrip::update_input_display ()
1000 update_io_button (_route
, _width
, true);
1001 panners
.setup_pan ();
1005 MixerStrip::update_output_display ()
1007 update_io_button (_route
, _width
, false);
1008 gpm
.setup_meters ();
1009 panners
.setup_pan ();
1013 MixerStrip::fast_update ()
1015 gpm
.update_meters ();
1019 MixerStrip::diskstream_changed ()
1021 Gtkmm2ext::UI::instance()->call_slot (mem_fun(*this, &MixerStrip::update_diskstream_display
));
1025 MixerStrip::input_changed (IOChange change
, void *src
)
1027 Gtkmm2ext::UI::instance()->call_slot (mem_fun(*this, &MixerStrip::update_input_display
));
1028 set_width(_width
, this);
1032 MixerStrip::output_changed (IOChange change
, void *src
)
1034 Gtkmm2ext::UI::instance()->call_slot (mem_fun(*this, &MixerStrip::update_output_display
));
1035 set_width(_width
, this);
1040 MixerStrip::comment_editor_done_editing()
1042 string str
= comment_area
->get_buffer()->get_text();
1043 if (_route
->comment() != str
) {
1044 _route
->set_comment (str
, this);
1049 if (! str
.empty()) {
1050 comment_button
.modify_bg (STATE_NORMAL
, color());
1051 ((Gtk::Label
*)comment_button
.get_child())->set_text (_("*Comments*"));
1053 comment_button
.unset_bg (STATE_NORMAL
);
1054 ((Gtk::Label
*)comment_button
.get_child())->set_text (_("Comments"));
1059 if (! str
.empty()) {
1060 comment_button
.modify_bg (STATE_NORMAL
, color());
1061 ((Gtk::Label
*)comment_button
.get_child())->set_text (_("*Cmt*"));
1063 comment_button
.unset_bg (STATE_NORMAL
);
1064 ((Gtk::Label
*)comment_button
.get_child())->set_text (_("Cmt"));
1069 ARDOUR_UI::instance()->tooltips().set_tip (comment_button
,
1070 str
.empty() ? _("Click to Add/Edit Comments") : str
);
1076 MixerStrip::comment_button_clicked ()
1078 if (comment_window
== 0) {
1079 setup_comment_editor ();
1082 int x
, y
, cw_width
, cw_height
;
1084 if (comment_window
->is_visible()) {
1085 comment_window
->hide ();
1089 comment_window
->get_size (cw_width
, cw_height
);
1090 comment_window
->get_position(x
, y
);
1091 comment_window
->move(x
, y
- (cw_height
/ 2) - 45);
1093 half the dialog height minus the comments button height
1094 with some window decoration fudge thrown in.
1097 comment_window
->show();
1098 comment_window
->present();
1102 MixerStrip::setup_comment_editor ()
1105 title
= _route
->name();
1106 title
+= _(": comment editor");
1108 comment_window
= new ArdourDialog (title
, false);
1109 comment_window
->set_position (Gtk::WIN_POS_MOUSE
);
1110 comment_window
->set_skip_taskbar_hint (true);
1111 comment_window
->signal_hide().connect (mem_fun(*this, &MixerStrip::comment_editor_done_editing
));
1113 comment_area
= manage (new TextView());
1114 comment_area
->set_name ("MixerTrackCommentArea");
1115 comment_area
->set_size_request (110, 178);
1116 comment_area
->set_wrap_mode (WRAP_WORD
);
1117 comment_area
->set_editable (true);
1118 comment_area
->get_buffer()->set_text (_route
->comment());
1119 comment_area
->show ();
1121 comment_window
->get_vbox()->pack_start (*comment_area
);
1122 comment_window
->get_action_area()->hide();
1126 MixerStrip::comment_changed (void *src
)
1128 ENSURE_GUI_THREAD(bind (mem_fun(*this, &MixerStrip::comment_changed
), src
));
1131 ignore_comment_edit
= true;
1133 comment_area
->get_buffer()->set_text (_route
->comment());
1135 ignore_comment_edit
= false;
1140 MixerStrip::set_mix_group (RouteGroup
*rg
)
1142 _route
->set_mix_group (rg
, this);
1146 MixerStrip::add_mix_group_to_menu (RouteGroup
*rg
, RadioMenuItem::Group
* group
)
1148 using namespace Menu_Helpers
;
1150 MenuList
& items
= group_menu
->items();
1152 items
.push_back (RadioMenuElem (*group
, rg
->name(), bind (mem_fun(*this, &MixerStrip::set_mix_group
), rg
)));
1154 if (_route
->mix_group() == rg
) {
1155 static_cast<RadioMenuItem
*>(&items
.back())->set_active ();
1160 MixerStrip::select_mix_group (GdkEventButton
*ev
)
1162 using namespace Menu_Helpers
;
1164 if (group_menu
== 0) {
1165 group_menu
= new Menu
;
1167 group_menu
->set_name ("ArdourContextMenu");
1168 MenuList
& items
= group_menu
->items();
1169 RadioMenuItem::Group group
;
1171 switch (ev
->button
) {
1175 items
.push_back (RadioMenuElem (group
, _("No group"), bind (mem_fun(*this, &MixerStrip::set_mix_group
), (RouteGroup
*) 0)));
1177 _session
.foreach_mix_group (bind (mem_fun (*this, &MixerStrip::add_mix_group_to_menu
), &group
));
1179 group_menu
->popup (1, ev
->time
);
1190 MixerStrip::mix_group_changed (void *ignored
)
1192 ENSURE_GUI_THREAD(bind (mem_fun(*this, &MixerStrip::mix_group_changed
), ignored
));
1194 RouteGroup
*rg
= _route
->mix_group();
1197 group_label
.set_text (PBD::short_version (rg
->name(), 5));
1201 group_label
.set_text (_("Grp"));
1204 group_label
.set_text (_("~G"));
1212 MixerStrip::route_gui_changed (string what_changed
, void* ignored
)
1214 ENSURE_GUI_THREAD(bind (mem_fun(*this, &MixerStrip::route_gui_changed
), what_changed
, ignored
));
1216 if (what_changed
== "color") {
1217 if (set_color_from_route () == 0) {
1218 show_route_color ();
1224 MixerStrip::show_route_color ()
1226 name_button
.modify_bg (STATE_NORMAL
, color());
1227 top_event_box
.modify_bg (STATE_NORMAL
, color());
1228 route_active_changed ();
1232 MixerStrip::show_passthru_color ()
1234 route_active_changed ();
1238 MixerStrip::build_route_ops_menu ()
1240 using namespace Menu_Helpers
;
1241 route_ops_menu
= new Menu
;
1242 route_ops_menu
->set_name ("ArdourContextMenu");
1244 MenuList
& items
= route_ops_menu
->items();
1246 items
.push_back (MenuElem (_("Save As Template"), mem_fun(*this, &RouteUI::save_as_template
)));
1247 items
.push_back (MenuElem (_("Rename"), mem_fun(*this, &RouteUI::route_rename
)));
1248 rename_menu_item
= &items
.back();
1249 items
.push_back (SeparatorElem());
1250 items
.push_back (CheckMenuElem (_("Active"), mem_fun (*this, &RouteUI::toggle_route_active
)));
1251 route_active_menu_item
= dynamic_cast<CheckMenuItem
*> (&items
.back());
1252 route_active_menu_item
->set_active (_route
->active());
1253 items
.push_back (SeparatorElem());
1254 items
.push_back (CheckMenuElem (_("Invert Polarity"), mem_fun (*this, &RouteUI::toggle_polarity
)));
1255 polarity_menu_item
= dynamic_cast<CheckMenuItem
*> (&items
.back());
1256 polarity_menu_item
->set_active (_route
->phase_invert());
1257 items
.push_back (CheckMenuElem (_("Protect against denormals"), mem_fun (*this, &RouteUI::toggle_denormal_protection
)));
1258 denormal_menu_item
= dynamic_cast<CheckMenuItem
*> (&items
.back());
1259 denormal_menu_item
->set_active (_route
->denormal_protection());
1261 if (!Profile
->get_sae()) {
1262 build_remote_control_menu ();
1263 items
.push_back (SeparatorElem());
1264 items
.push_back (MenuElem (_("Remote Control ID"), *remote_control_menu
));
1267 items
.push_back (SeparatorElem());
1268 items
.push_back (MenuElem (_("Remove"), mem_fun(*this, &RouteUI::remove_this_route
)));
1272 MixerStrip::name_button_button_press (GdkEventButton
* ev
)
1274 if (ev
->button
== 1 || ev
->button
== 3) {
1275 list_route_operations ();
1276 /* do not allow rename if the track is record-enabled */
1277 rename_menu_item
->set_sensitive (!_route
->record_enabled());
1278 route_ops_menu
->popup (1, ev
->time
);
1284 MixerStrip::list_route_operations ()
1286 if (route_ops_menu
== 0) {
1287 build_route_ops_menu ();
1290 refresh_remote_control_menu();
1295 MixerStrip::speed_adjustment_changed ()
1297 /* since there is a usable speed adjustment, there has to be a diskstream */
1298 if (!ignore_speed_adjustment
) {
1299 get_diskstream()->set_speed (speed_adjustment
.get_value());
1304 MixerStrip::speed_changed ()
1306 Gtkmm2ext::UI::instance()->call_slot (mem_fun(*this, &MixerStrip::update_speed_display
));
1310 MixerStrip::update_speed_display ()
1314 val
= get_diskstream()->speed();
1317 speed_spinner
.set_name ("MixerStripSpeedBaseNotOne");
1319 speed_spinner
.set_name ("MixerStripSpeedBase");
1322 if (speed_adjustment
.get_value() != val
) {
1323 ignore_speed_adjustment
= true;
1324 speed_adjustment
.set_value (val
);
1325 ignore_speed_adjustment
= false;
1331 MixerStrip::set_selected (bool yn
)
1333 AxisView::set_selected (yn
);
1335 global_frame
.set_shadow_type (Gtk::SHADOW_ETCHED_OUT
);
1336 global_frame
.set_name ("MixerStripSelectedFrame");
1338 global_frame
.set_shadow_type (Gtk::SHADOW_IN
);
1339 global_frame
.set_name ("MixerStripFrame");
1341 global_frame
.queue_draw ();
1345 MixerStrip::name_changed (void *src
)
1349 RouteUI::name_changed (src
);
1352 name_label
.set_text (PBD::short_version (_route
->name(), 5));
1355 if (_route
->phase_invert()) {
1356 name_label
.set_text (X_("Ø ") + name_label
.get_text());
1361 MixerStrip::width_clicked ()
1365 set_width (Narrow
, this);
1368 set_width (Wide
, this);
1374 MixerStrip::hide_clicked ()
1376 // LAME fix to reset the button status for when it is redisplayed (part 1)
1377 hide_button
.set_sensitive(false);
1380 Hiding(); /* EMIT_SIGNAL */
1382 _mixer
.hide_strip (this);
1386 hide_button
.set_sensitive(true);
1390 MixerStrip::set_embedded (bool yn
)
1396 MixerStrip::map_frozen ()
1398 ENSURE_GUI_THREAD (mem_fun(*this, &MixerStrip::map_frozen
));
1400 boost::shared_ptr
<AudioTrack
> at
= audio_track();
1403 switch (at
->freeze_state()) {
1404 case AudioTrack::Frozen
:
1405 pre_redirect_box
.set_sensitive (false);
1406 post_redirect_box
.set_sensitive (false);
1407 speed_spinner
.set_sensitive (false);
1410 pre_redirect_box
.set_sensitive (true);
1411 post_redirect_box
.set_sensitive (true);
1412 speed_spinner
.set_sensitive (true);
1413 // XXX need some way, maybe, to retoggle redirect editors
1418 hide_redirect_editors ();
1422 MixerStrip::hide_redirect_editors ()
1424 _route
->foreach_redirect (this, &MixerStrip::hide_redirect_editor
);
1428 MixerStrip::hide_redirect_editor (boost::shared_ptr
<Redirect
> redirect
)
1430 void* gui
= redirect
->get_gui ();
1433 static_cast<Gtk::Widget
*>(gui
)->hide ();
1438 MixerStrip::route_active_changed ()
1440 RouteUI::route_active_changed ();
1442 if (is_audio_track()) {
1443 if (_route
->active()) {
1444 set_name ("AudioTrackStripBase");
1445 gpm
.set_meter_strip_name ("AudioTrackMetrics");
1447 set_name ("AudioTrackStripBaseInactive");
1448 gpm
.set_meter_strip_name ("AudioTrackMetricsInactive");
1450 gpm
.set_fader_name ("AudioTrackFader");
1451 } else { // FIXME: assumed audio bus
1452 if (_route
->active()) {
1453 set_name ("AudioBusStripBase");
1454 gpm
.set_meter_strip_name ("AudioBusMetrics");
1456 set_name ("AudioBusStripBaseInactive");
1457 gpm
.set_meter_strip_name ("AudioBusMetricsInactive");
1459 gpm
.set_fader_name ("AudioBusFader");
1464 MixerStrip::mix_group() const
1466 return _route
->mix_group();
1470 MixerStrip::engine_stopped ()
1475 MixerStrip::engine_running ()
1480 MixerStrip::meter_changed (void *src
)
1483 ENSURE_GUI_THREAD (bind (mem_fun(*this, &MixerStrip::meter_changed
), src
));
1485 switch (_route
->meter_point()) {
1487 meter_point_label
.set_text (_("input"));
1491 meter_point_label
.set_text (_("pre"));
1494 case MeterPostFader
:
1495 meter_point_label
.set_text (_("post"));
1499 gpm
.setup_meters ();
1500 // reset peak when meter point changes
1501 gpm
.reset_peak_display();
1502 set_width (_width
, this);