fix lack of return value in tell_about_jack_death(), and fixed up indentation for...
[ardour2.git] / gtk2_ardour / mixer_ui.cc
blob6f4fa592b5fded719edb3258abf7d6763844267c
1 /*
2 Copyright (C) 2000-2004 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.
20 #include <algorithm>
21 #include <sigc++/bind.h>
23 #include <gtkmm/accelmap.h>
25 #include <pbd/convert.h>
26 #include <pbd/stacktrace.h>
27 #include <glibmm/thread.h>
29 #include <gtkmm2ext/gtk_ui.h>
30 #include <gtkmm2ext/utils.h>
31 #include <gtkmm2ext/stop_signal.h>
32 #include <gtkmm2ext/window_title.h>
34 #include <ardour/session.h>
35 #include <ardour/audio_track.h>
36 #include <ardour/session_route.h>
37 #include <ardour/audio_diskstream.h>
38 #include <ardour/plugin_manager.h>
40 #include "keyboard.h"
41 #include "mixer_ui.h"
42 #include "mixer_strip.h"
43 #include "plugin_selector.h"
44 #include "ardour_ui.h"
45 #include "prompter.h"
46 #include "utils.h"
47 #include "actions.h"
48 #include "gui_thread.h"
50 #include "i18n.h"
52 using namespace ARDOUR;
53 using namespace PBD;
54 using namespace Gtk;
55 using namespace Glib;
56 using namespace Gtkmm2ext;
57 using namespace sigc;
58 using namespace std;
60 using PBD::atoi;
62 Mixer_UI::Mixer_UI ()
63 : Window (Gtk::WINDOW_TOPLEVEL)
65 session = 0;
66 _strip_width = Config->get_default_narrow_ms() ? Narrow : Wide;
67 track_menu = 0;
68 mix_group_context_menu = 0;
69 no_track_list_redisplay = false;
70 in_group_row_change = false;
71 _visible = false;
72 strip_redisplay_does_not_reset_order_keys = false;
73 strip_redisplay_does_not_sync_order_keys = false;
75 Route::SyncOrderKeys.connect (mem_fun (*this, &Mixer_UI::sync_order_keys));
77 scroller_base.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
78 scroller_base.set_name ("MixerWindow");
79 scroller_base.signal_button_release_event().connect (mem_fun(*this, &Mixer_UI::strip_scroller_button_release));
80 // add as last item of strip packer
81 strip_packer.pack_end (scroller_base, true, true);
83 scroller.add (strip_packer);
84 scroller.set_policy (Gtk::POLICY_ALWAYS, Gtk::POLICY_AUTOMATIC);
86 track_model = ListStore::create (track_columns);
87 track_display.set_model (track_model);
88 track_display.append_column (_("Strips"), track_columns.text);
89 track_display.append_column (_("Show"), track_columns.visible);
90 track_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0));
91 track_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
92 track_display.get_column (0)->set_expand(true);
93 track_display.get_column (1)->set_expand(false);
94 track_display.set_name (X_("MixerTrackDisplayList"));
95 track_display.get_selection()->set_mode (Gtk::SELECTION_NONE);
96 track_display.set_reorderable (true);
97 track_display.set_headers_visible (true);
99 track_model->signal_row_deleted().connect (mem_fun (*this, &Mixer_UI::track_list_delete));
100 track_model->signal_row_changed().connect (mem_fun (*this, &Mixer_UI::track_list_change));
101 track_model->signal_rows_reordered().connect (mem_fun (*this, &Mixer_UI::track_list_reorder));
103 CellRendererToggle* track_list_visible_cell = dynamic_cast<CellRendererToggle*>(track_display.get_column_cell_renderer (1));
104 track_list_visible_cell->property_activatable() = true;
105 track_list_visible_cell->property_radio() = false;
107 track_display.signal_button_press_event().connect (mem_fun (*this, &Mixer_UI::track_display_button_press), false);
109 track_display_scroller.add (track_display);
110 track_display_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
112 group_model = ListStore::create (group_columns);
113 group_display.set_model (group_model);
114 group_display.append_column (_("Group"), group_columns.text);
115 group_display.append_column (_("Active"), group_columns.active);
116 group_display.append_column (_("Show"), group_columns.visible);
117 group_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0));
118 group_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
119 group_display.get_column (2)->set_data (X_("colnum"), GUINT_TO_POINTER(2));
120 group_display.get_column (0)->set_expand(true);
121 group_display.get_column (1)->set_expand(false);
122 group_display.get_column (2)->set_expand(false);
123 group_display.set_name ("MixerGroupList");
124 group_display.get_selection()->set_mode (Gtk::SELECTION_SINGLE);
125 group_display.set_reorderable (true);
126 group_display.set_headers_visible (true);
127 group_display.set_rules_hint (true);
129 /* name is directly editable */
131 CellRendererText* name_cell = dynamic_cast<CellRendererText*>(group_display.get_column_cell_renderer (0));
132 name_cell->property_editable() = true;
133 name_cell->signal_edited().connect (mem_fun (*this, &Mixer_UI::mix_group_name_edit));
135 /* use checkbox for the active column */
137 CellRendererToggle* active_cell = dynamic_cast<CellRendererToggle*>(group_display.get_column_cell_renderer (1));
138 active_cell->property_activatable() = true;
139 active_cell->property_radio() = false;
141 /* use checkbox for the visible column */
143 active_cell = dynamic_cast<CellRendererToggle*>(group_display.get_column_cell_renderer (2));
144 active_cell->property_activatable() = true;
145 active_cell->property_radio() = false;
147 group_model->signal_row_changed().connect (mem_fun (*this, &Mixer_UI::mix_group_row_change));
149 group_display.signal_button_press_event().connect (mem_fun (*this, &Mixer_UI::group_display_button_press), false);
151 group_display_scroller.add (group_display);
152 group_display_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
154 HBox* mix_group_display_button_box = manage (new HBox());
156 Button* mix_group_add_button = manage (new Button ());
157 Button* mix_group_remove_button = manage (new Button ());
159 Widget* w;
161 w = manage (new Image (Stock::ADD, ICON_SIZE_BUTTON));
162 w->show();
163 mix_group_add_button->add (*w);
165 w = manage (new Image (Stock::REMOVE, ICON_SIZE_BUTTON));
166 w->show();
167 mix_group_remove_button->add (*w);
169 mix_group_display_button_box->set_homogeneous (true);
171 mix_group_add_button->signal_clicked().connect (mem_fun (*this, &Mixer_UI::new_mix_group));
172 mix_group_remove_button->signal_clicked().connect (mem_fun (*this, &Mixer_UI::remove_selected_mix_group));
174 mix_group_display_button_box->add (*mix_group_remove_button);
175 mix_group_display_button_box->add (*mix_group_add_button);
177 group_display_vbox.pack_start (group_display_scroller, true, true);
178 group_display_vbox.pack_start (*mix_group_display_button_box, false, false);
180 track_display_frame.set_name("BaseFrame");
181 track_display_frame.set_shadow_type (Gtk::SHADOW_IN);
182 track_display_frame.add(track_display_scroller);
184 group_display_frame.set_name ("BaseFrame");
185 group_display_frame.set_shadow_type (Gtk::SHADOW_IN);
186 group_display_frame.add (group_display_vbox);
188 rhs_pane1.pack1 (track_display_frame);
189 rhs_pane1.pack2 (group_display_frame);
191 list_vpacker.pack_start (rhs_pane1, true, true);
193 global_hpacker.pack_start (scroller, true, true);
194 #ifdef GTKOSX
195 /* current gtk-quartz has dirty updates on borders like this one */
196 global_hpacker.pack_start (out_packer, false, false, 0);
197 #else
198 global_hpacker.pack_start (out_packer, false, false, 12);
199 #endif
200 list_hpane.add1(list_vpacker);
201 list_hpane.add2(global_hpacker);
203 rhs_pane1.signal_size_allocate().connect (bind (mem_fun(*this, &Mixer_UI::pane_allocation_handler),
204 static_cast<Gtk::Paned*> (&rhs_pane1)));
205 list_hpane.signal_size_allocate().connect (bind (mem_fun(*this, &Mixer_UI::pane_allocation_handler),
206 static_cast<Gtk::Paned*> (&list_hpane)));
208 global_vpacker.pack_start (list_hpane, true, true);
210 add (global_vpacker);
211 set_name ("MixerWindow");
213 WindowTitle title(Glib::get_application_name());
214 title += _("Mixer");
215 set_title (title.get_string());
217 set_wmclass (X_("ardour_mixer"), "Ardour");
219 add_accel_group (ActionManager::ui_manager->get_accel_group());
221 signal_delete_event().connect (mem_fun (*this, &Mixer_UI::hide_window));
222 add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
224 _plugin_selector = new PluginSelector (PluginManager::the_manager());
226 signal_configure_event().connect (mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::configure_handler));
228 _selection.RoutesChanged.connect (mem_fun(*this, &Mixer_UI::follow_strip_selection));
230 auto_rebinding = FALSE;
233 Mixer_UI::~Mixer_UI ()
237 void
238 Mixer_UI::ensure_float (Window& win)
240 win.set_transient_for (*this);
243 void
244 Mixer_UI::show_window ()
246 show_all ();
247 if (!_visible) {
248 set_window_pos_and_size ();
250 /* now reset each strips width so the right widgets are shown */
251 MixerStrip* ms;
253 TreeModel::Children rows = track_model->children();
254 TreeModel::Children::iterator ri;
256 for (ri = rows.begin(); ri != rows.end(); ++ri) {
257 ms = (*ri)[track_columns.strip];
258 ms->set_width (ms->get_width(), ms->width_owner());
261 _visible = true;
264 bool
265 Mixer_UI::hide_window (GdkEventAny *ev)
267 get_window_pos_and_size ();
269 _visible = false;
270 return just_hide_it(ev, static_cast<Gtk::Window *>(this));
274 void
275 Mixer_UI::add_strip (Session::RouteList& routes)
277 ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::add_strip), routes));
279 MixerStrip* strip;
281 no_track_list_redisplay = true;
282 strip_redisplay_does_not_sync_order_keys = true;
284 for (Session::RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
285 boost::shared_ptr<Route> route = (*x);
287 if (route->hidden()) {
288 return;
291 strip = new MixerStrip (*this, *session, route);
292 strips.push_back (strip);
294 Config->get_default_narrow_ms() ? _strip_width = Narrow : _strip_width = Wide;
296 if (strip->width_owner() != strip) {
297 strip->set_width (_strip_width, this);
300 show_strip (strip);
302 TreeModel::Row row = *(track_model->append());
303 row[track_columns.text] = route->name();
304 row[track_columns.visible] = strip->marked_for_display();
305 row[track_columns.route] = route;
306 row[track_columns.strip] = strip;
308 if (route->order_key (N_("signal")) == -1) {
309 route->set_order_key (N_("signal"), track_model->children().size()-1);
312 route->name_changed.connect (bind (mem_fun(*this, &Mixer_UI::strip_name_changed), strip));
314 strip->GoingAway.connect (bind (mem_fun(*this, &Mixer_UI::remove_strip), strip));
315 #ifdef GTKOSX
316 strip->WidthChanged.connect (mem_fun(*this, &Mixer_UI::queue_draw_all_strips));
317 #endif
318 strip->signal_button_release_event().connect (bind (mem_fun(*this, &Mixer_UI::strip_button_release_event), strip));
321 no_track_list_redisplay = false;
323 redisplay_track_list ();
325 strip_redisplay_does_not_sync_order_keys = false;
328 void
329 Mixer_UI::remove_strip (MixerStrip* strip)
331 ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::remove_strip), strip));
333 TreeModel::Children rows = track_model->children();
334 TreeModel::Children::iterator ri;
335 list<MixerStrip *>::iterator i;
337 if ((i = find (strips.begin(), strips.end(), strip)) != strips.end()) {
338 strips.erase (i);
341 strip_redisplay_does_not_sync_order_keys = true;
343 for (ri = rows.begin(); ri != rows.end(); ++ri) {
344 if ((*ri)[track_columns.strip] == strip) {
345 track_model->erase (ri);
346 break;
350 strip_redisplay_does_not_sync_order_keys = false;
353 const char*
354 Mixer_UI::get_order_key()
356 return X_("signal");
357 #if 0
358 if (Config->get_sync_all_route_ordering()) {
359 return X_("editor");
360 } else {
361 return X_("signal");
363 #endif
367 void
368 Mixer_UI::sync_order_keys (const char *src)
370 vector<int> neworder;
371 TreeModel::Children rows = track_model->children();
372 TreeModel::Children::iterator ri;
374 if ((strcmp (src, get_order_key()) == 0) || !session || (session->state_of_the_state() & Session::Loading) || rows.empty()) {
375 return;
378 for (ri = rows.begin(); ri != rows.end(); ++ri) {
379 neworder.push_back (0);
382 bool changed = false;
383 int order;
385 for (order = 0, ri = rows.begin(); ri != rows.end(); ++ri, ++order) {
386 boost::shared_ptr<Route> route = (*ri)[track_columns.route];
387 int old_key = order;
388 int new_key = route->order_key (get_order_key());
390 neworder[new_key] = old_key;
392 if (new_key != old_key) {
393 changed = true;
397 if (changed) {
398 strip_redisplay_does_not_reset_order_keys = true;
399 track_model->reorder (neworder);
400 strip_redisplay_does_not_reset_order_keys = false;
405 void
406 Mixer_UI::follow_strip_selection ()
408 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
409 (*i)->set_selected (_selection.selected ((*i)->route()));
413 bool
414 Mixer_UI::strip_button_release_event (GdkEventButton *ev, MixerStrip *strip)
416 if (ev->button == 1) {
418 /* this allows the user to click on the strip to terminate comment
419 editing. XXX it needs improving so that we don't select the strip
420 at the same time.
423 if (_selection.selected (strip->route())) {
424 _selection.remove (strip->route());
425 } else {
426 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
427 _selection.add (strip->route());
428 } else {
429 _selection.set (strip->route());
434 return true;
437 void
438 Mixer_UI::connect_to_session (Session* sess)
440 session = sess;
442 XMLNode* node = ARDOUR_UI::instance()->mixer_settings();
443 set_state (*node);
445 WindowTitle title(session->name());
446 title += _("Mixer");
447 title += Glib::get_application_name();
449 set_title (title.get_string());
451 initial_track_display ();
453 session->GoingAway.connect (mem_fun(*this, &Mixer_UI::disconnect_from_session));
454 session->RouteAdded.connect (mem_fun(*this, &Mixer_UI::add_strip));
455 session->mix_group_added.connect (mem_fun(*this, &Mixer_UI::add_mix_group));
456 session->mix_group_removed.connect (mem_fun(*this, &Mixer_UI::mix_groups_changed));
458 mix_groups_changed ();
460 _plugin_selector->set_session (session);
462 if (_visible) {
463 show_window();
466 start_updating ();
469 void
470 Mixer_UI::disconnect_from_session ()
472 ENSURE_GUI_THREAD(mem_fun(*this, &Mixer_UI::disconnect_from_session));
474 group_model->clear ();
475 _selection.clear ();
477 WindowTitle title(Glib::get_application_name());
478 title += _("Mixer");
479 set_title (title.get_string());
481 stop_updating ();
484 void
485 Mixer_UI::show_strip (MixerStrip* ms)
487 TreeModel::Children rows = track_model->children();
488 TreeModel::Children::iterator i;
490 for (i = rows.begin(); i != rows.end(); ++i) {
492 MixerStrip* strip = (*i)[track_columns.strip];
493 if (strip == ms) {
494 (*i)[track_columns.visible] = true;
495 break;
500 void
501 Mixer_UI::hide_strip (MixerStrip* ms)
503 TreeModel::Children rows = track_model->children();
504 TreeModel::Children::iterator i;
506 for (i = rows.begin(); i != rows.end(); ++i) {
508 MixerStrip* strip = (*i)[track_columns.strip];
509 if (strip == ms) {
510 (*i)[track_columns.visible] = false;
511 break;
516 gint
517 Mixer_UI::start_updating ()
519 fast_screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (mem_fun(*this, &Mixer_UI::fast_update_strips));
520 return 0;
523 gint
524 Mixer_UI::stop_updating ()
526 fast_screen_update_connection.disconnect();
527 return 0;
530 void
531 Mixer_UI::fast_update_strips ()
533 if (is_mapped () && session) {
534 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
535 (*i)->fast_update ();
540 void
541 Mixer_UI::set_all_strips_visibility (bool yn)
543 TreeModel::Children rows = track_model->children();
544 TreeModel::Children::iterator i;
546 no_track_list_redisplay = true;
548 for (i = rows.begin(); i != rows.end(); ++i) {
550 TreeModel::Row row = (*i);
551 MixerStrip* strip = row[track_columns.strip];
553 if (strip == 0) {
554 continue;
557 if (strip->route()->master() || strip->route()->control()) {
558 continue;
561 (*i)[track_columns.visible] = yn;
564 no_track_list_redisplay = false;
565 redisplay_track_list ();
569 void
570 Mixer_UI::set_all_audio_visibility (int tracks, bool yn)
572 TreeModel::Children rows = track_model->children();
573 TreeModel::Children::iterator i;
575 no_track_list_redisplay = true;
577 for (i = rows.begin(); i != rows.end(); ++i) {
578 TreeModel::Row row = (*i);
579 MixerStrip* strip = row[track_columns.strip];
581 if (strip == 0) {
582 continue;
585 if (strip->route()->master() || strip->route()->control()) {
586 continue;
589 boost::shared_ptr<AudioTrack> at = strip->audio_track();
591 switch (tracks) {
592 case 0:
593 (*i)[track_columns.visible] = yn;
594 break;
596 case 1:
597 if (at) { /* track */
598 (*i)[track_columns.visible] = yn;
600 break;
602 case 2:
603 if (!at) { /* bus */
604 (*i)[track_columns.visible] = yn;
606 break;
610 no_track_list_redisplay = false;
611 redisplay_track_list ();
614 void
615 Mixer_UI::hide_all_routes ()
617 set_all_strips_visibility (false);
620 void
621 Mixer_UI::show_all_routes ()
623 set_all_strips_visibility (true);
626 void
627 Mixer_UI::show_all_audiobus ()
629 set_all_audio_visibility (2, true);
631 void
632 Mixer_UI::hide_all_audiobus ()
634 set_all_audio_visibility (2, false);
637 void
638 Mixer_UI::show_all_audiotracks()
640 set_all_audio_visibility (1, true);
642 void
643 Mixer_UI::hide_all_audiotracks ()
645 set_all_audio_visibility (1, false);
648 void
649 Mixer_UI::track_list_reorder (const TreeModel::Path& path, const TreeModel::iterator& iter, int* new_order)
651 strip_redisplay_does_not_sync_order_keys = true;
652 session->set_remote_control_ids();
653 redisplay_track_list ();
654 strip_redisplay_does_not_sync_order_keys = false;
657 void
658 Mixer_UI::track_list_change (const Gtk::TreeModel::Path& path,const Gtk::TreeModel::iterator& iter)
660 // never reset order keys because of a property change
661 strip_redisplay_does_not_reset_order_keys = true;
662 session->set_remote_control_ids();
663 redisplay_track_list ();
664 strip_redisplay_does_not_reset_order_keys = false;
667 void
668 Mixer_UI::track_list_delete (const Gtk::TreeModel::Path& path)
670 /* this could require an order sync */
671 session->set_remote_control_ids();
672 redisplay_track_list ();
675 void
676 Mixer_UI::redisplay_track_list ()
678 TreeModel::Children rows = track_model->children();
679 TreeModel::Children::iterator i;
680 long order;
682 if (no_track_list_redisplay) {
683 return;
686 for (order = 0, i = rows.begin(); i != rows.end(); ++i, ++order) {
687 MixerStrip* strip = (*i)[track_columns.strip];
689 if (strip == 0) {
690 /* we're in the middle of changing a row, don't worry */
691 continue;
694 bool visible = (*i)[track_columns.visible];
696 boost::shared_ptr<Route> route = (*i)[track_columns.route];
698 if (visible) {
699 strip->set_marked_for_display (true);
701 if (!strip_redisplay_does_not_reset_order_keys) {
702 strip->route()->set_order_key (get_order_key(), order);
705 if (strip->packed()) {
707 if (strip->route()->master() || strip->route()->control()) {
708 out_packer.reorder_child (*strip, -1);
709 } else {
710 strip_packer.reorder_child (*strip, -1); /* put at end */
713 } else {
715 if (strip->route()->master() || strip->route()->control()) {
716 out_packer.pack_start (*strip, false, false);
717 } else {
718 strip_packer.pack_start (*strip, false, false);
720 strip->set_packed (true);
721 strip->show_all ();
724 } else {
726 if (strip->route()->master() || strip->route()->control()) {
727 /* do nothing, these cannot be hidden */
728 } else {
729 if (strip->packed()) {
730 strip_packer.remove (*strip);
731 strip->set_packed (false);
737 if (!strip_redisplay_does_not_reset_order_keys && !strip_redisplay_does_not_sync_order_keys) {
738 session->sync_order_keys (get_order_key());
741 // Rebind all of the midi controls automatically
743 if (auto_rebinding) {
744 auto_rebind_midi_controls ();
748 #ifdef GTKOSX
749 void
750 Mixer_UI::queue_draw_all_strips ()
752 TreeModel::Children rows = track_model->children();
753 TreeModel::Children::iterator i;
754 long order;
756 for (order = 0, i = rows.begin(); i != rows.end(); ++i, ++order) {
757 MixerStrip* strip = (*i)[track_columns.strip];
759 if (strip == 0) {
760 continue;
763 bool visible = (*i)[track_columns.visible];
765 if (visible) {
766 strip->queue_draw();
770 #endif
772 void
773 Mixer_UI::set_auto_rebinding( bool val )
775 if( val == TRUE )
777 auto_rebinding = TRUE;
778 Session::AutoBindingOff();
780 else
782 auto_rebinding = FALSE;
783 Session::AutoBindingOn();
787 void
788 Mixer_UI::toggle_auto_rebinding()
790 if (auto_rebinding)
792 set_auto_rebinding( FALSE );
795 else
797 set_auto_rebinding( TRUE );
800 auto_rebind_midi_controls();
803 void
804 Mixer_UI::auto_rebind_midi_controls ()
806 TreeModel::Children rows = track_model->children();
807 TreeModel::Children::iterator i;
808 int pos;
810 // Create bindings for all visible strips and remove those that are not visible
811 pos = 1; // 0 is reserved for the master strip
812 for (i = rows.begin(); i != rows.end(); ++i) {
813 MixerStrip* strip = (*i)[track_columns.strip];
815 if ( (*i)[track_columns.visible] == true ) { // add bindings for
816 // make the actual binding
817 //cout<<"Auto Binding: Visible Strip Found: "<<strip->name()<<endl;
819 int controlValue = pos;
820 if( strip->route()->master() ) {
821 controlValue = 0;
823 else {
824 pos++;
827 PBD::Controllable::CreateBinding ( strip->solo_button->get_controllable(), controlValue, 0);
828 PBD::Controllable::CreateBinding ( strip->mute_button->get_controllable(), controlValue, 1);
830 if( strip->is_audio_track() ) {
831 PBD::Controllable::CreateBinding ( strip->rec_enable_button->get_controllable(), controlValue, 2);
834 PBD::Controllable::CreateBinding ( &(strip->gpm.get_controllable()), controlValue, 3);
835 PBD::Controllable::CreateBinding ( strip->panners.get_controllable(), controlValue, 4);
838 else { // Remove any existing binding
839 PBD::Controllable::DeleteBinding ( strip->solo_button->get_controllable() );
840 PBD::Controllable::DeleteBinding ( strip->mute_button->get_controllable() );
842 if( strip->is_audio_track() ) {
843 PBD::Controllable::DeleteBinding ( strip->rec_enable_button->get_controllable() );
846 PBD::Controllable::DeleteBinding ( &(strip->gpm.get_controllable()) );
847 PBD::Controllable::DeleteBinding ( strip->panners.get_controllable() ); // This only takes the first panner if there are multiples...
850 } // for
855 struct SignalOrderRouteSorter {
856 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
857 /* use of ">" forces the correct sort order */
858 return a->order_key (Mixer_UI::get_order_key()) < b->order_key (Mixer_UI::get_order_key());
862 void
863 Mixer_UI::initial_track_display ()
865 boost::shared_ptr<Session::RouteList> routes = session->get_routes();
866 Session::RouteList copy (*routes);
867 SignalOrderRouteSorter sorter;
869 copy.sort (sorter);
871 no_track_list_redisplay = true;
873 track_model->clear ();
875 add_strip (copy);
877 no_track_list_redisplay = false;
879 redisplay_track_list ();
882 void
883 Mixer_UI::show_track_list_menu ()
885 if (track_menu == 0) {
886 build_track_menu ();
889 track_menu->popup (1, gtk_get_current_event_time());
892 bool
893 Mixer_UI::track_display_button_press (GdkEventButton* ev)
895 if (Keyboard::is_context_menu_event (ev)) {
896 show_track_list_menu ();
897 return true;
900 TreeIter iter;
901 TreeModel::Path path;
902 TreeViewColumn* column;
903 int cellx;
904 int celly;
906 if (!track_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
907 return false;
910 switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
911 case 0:
912 /* allow normal processing to occur */
913 return false;
915 case 1: /* visibility */
917 if ((iter = track_model->get_iter (path))) {
918 MixerStrip* strip = (*iter)[track_columns.strip];
919 if (strip) {
921 if (!strip->route()->master() && !strip->route()->control()) {
922 bool visible = (*iter)[track_columns.visible];
923 (*iter)[track_columns.visible] = !visible;
925 #ifdef GTKOSX
926 track_display.queue_draw();
927 #endif
930 return true;
932 default:
933 break;
936 return false;
940 void
941 Mixer_UI::build_track_menu ()
943 using namespace Menu_Helpers;
944 using namespace Gtk;
946 track_menu = new Menu;
947 track_menu->set_name ("ArdourContextMenu");
948 MenuList& items = track_menu->items();
950 items.push_back (MenuElem (_("Show All"), mem_fun(*this, &Mixer_UI::show_all_routes)));
951 items.push_back (MenuElem (_("Hide All"), mem_fun(*this, &Mixer_UI::hide_all_routes)));
952 items.push_back (MenuElem (_("Show All Audio Tracks"), mem_fun(*this, &Mixer_UI::show_all_audiotracks)));
953 items.push_back (MenuElem (_("Hide All Audio Tracks"), mem_fun(*this, &Mixer_UI::hide_all_audiotracks)));
954 items.push_back (MenuElem (_("Show All Audio Busses"), mem_fun(*this, &Mixer_UI::show_all_audiobus)));
955 items.push_back (MenuElem (_("Hide All Audio Busses"), mem_fun(*this, &Mixer_UI::hide_all_audiobus)));
959 void
960 Mixer_UI::strip_name_changed (void* src, MixerStrip* mx)
962 ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::strip_name_changed), src, mx));
964 TreeModel::Children rows = track_model->children();
965 TreeModel::Children::iterator i;
967 for (i = rows.begin(); i != rows.end(); ++i) {
968 if ((*i)[track_columns.strip] == mx) {
969 (*i)[track_columns.text] = mx->route()->name();
970 return;
974 error << _("track display list item for renamed strip not found!") << endmsg;
978 void
979 Mixer_UI::build_mix_group_context_menu ()
981 using namespace Gtk::Menu_Helpers;
983 mix_group_context_menu = new Menu;
984 mix_group_context_menu->set_name ("ArdourContextMenu");
985 MenuList& items = mix_group_context_menu->items();
987 items.push_back (MenuElem (_("Activate All"), mem_fun(*this, &Mixer_UI::activate_all_mix_groups)));
988 items.push_back (MenuElem (_("Disable All"), mem_fun(*this, &Mixer_UI::disable_all_mix_groups)));
989 items.push_back (SeparatorElem());
990 items.push_back (MenuElem (_("Show All"), mem_fun(*this, &Mixer_UI::show_all_mix_groups)));
991 items.push_back (MenuElem (_("Hide All"), mem_fun(*this, &Mixer_UI::hide_all_mix_groups)));
992 items.push_back (SeparatorElem());
993 items.push_back (MenuElem (_("Add group"), mem_fun(*this, &Mixer_UI::new_mix_group)));
997 bool
998 Mixer_UI::group_display_button_press (GdkEventButton* ev)
1000 if (Keyboard::is_context_menu_event (ev)) {
1001 if (mix_group_context_menu == 0) {
1002 build_mix_group_context_menu ();
1004 mix_group_context_menu->popup (1, ev->time);
1005 return true;
1009 RouteGroup* group;
1010 TreeIter iter;
1011 TreeModel::Path path;
1012 TreeViewColumn* column;
1013 int cellx;
1014 int celly;
1016 if (!group_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
1017 return false;
1020 switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
1021 case 0:
1022 if (Keyboard::is_edit_event (ev)) {
1023 if ((iter = group_model->get_iter (path))) {
1024 if ((group = (*iter)[group_columns.group]) != 0) {
1025 // edit_mix_group (group);
1026 #ifdef GTKOSX
1027 group_display.queue_draw();
1028 #endif
1029 return true;
1034 break;
1036 case 1:
1037 if ((iter = group_model->get_iter (path))) {
1038 bool active = (*iter)[group_columns.active];
1039 (*iter)[group_columns.active] = !active;
1040 #ifdef GTKOSX
1041 group_display.queue_draw();
1042 #endif
1043 return true;
1045 break;
1047 case 2:
1048 if ((iter = group_model->get_iter (path))) {
1049 bool visible = (*iter)[group_columns.visible];
1050 (*iter)[group_columns.visible] = !visible;
1051 #ifdef GTKOSX
1052 group_display.queue_draw();
1053 #endif
1054 return true;
1056 break;
1058 default:
1059 break;
1062 return false;
1065 void
1066 Mixer_UI::activate_all_mix_groups ()
1068 Gtk::TreeModel::Children children = group_model->children();
1069 for(Gtk::TreeModel::Children::iterator iter = children.begin(); iter != children.end(); ++iter) {
1070 (*iter)[group_columns.active] = true;
1076 void
1077 Mixer_UI::disable_all_mix_groups ()
1079 Gtk::TreeModel::Children children = group_model->children();
1080 for(Gtk::TreeModel::Children::iterator iter = children.begin(); iter != children.end(); ++iter) {
1081 (*iter)[group_columns.active] = false;
1085 void
1086 Mixer_UI::show_all_mix_groups ()
1088 Gtk::TreeModel::Children children = group_model->children();
1089 for(Gtk::TreeModel::Children::iterator iter = children.begin(); iter != children.end(); ++iter) {
1090 (*iter)[group_columns.visible] = true;
1094 void
1095 Mixer_UI::hide_all_mix_groups ()
1097 Gtk::TreeModel::Children children = group_model->children();
1098 for(Gtk::TreeModel::Children::iterator iter = children.begin(); iter != children.end(); ++iter) {
1099 (*iter)[group_columns.visible] = false;
1103 void
1104 Mixer_UI::mix_groups_changed ()
1106 ENSURE_GUI_THREAD (mem_fun (*this, &Mixer_UI::mix_groups_changed));
1108 /* just rebuild the while thing */
1110 group_model->clear ();
1112 session->foreach_mix_group (mem_fun (*this, &Mixer_UI::add_mix_group));
1115 void
1116 Mixer_UI::new_mix_group ()
1118 session->add_mix_group ("");
1121 void
1122 Mixer_UI::remove_selected_mix_group ()
1124 Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
1125 TreeView::Selection::ListHandle_Path rows = selection->get_selected_rows ();
1127 if (rows.empty()) {
1128 return;
1131 TreeView::Selection::ListHandle_Path::iterator i = rows.begin();
1132 TreeIter iter;
1134 /* selection mode is single, so rows.begin() is it */
1136 if ((iter = group_model->get_iter (*i))) {
1138 RouteGroup* rg = (*iter)[group_columns.group];
1140 if (rg) {
1141 session->remove_mix_group (*rg);
1146 void
1147 Mixer_UI::group_flags_changed (void* src, RouteGroup* group)
1149 if (in_group_row_change) {
1150 return;
1153 ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::group_flags_changed), src, group));
1155 /* force an update of any mixer strips that are using this group,
1156 otherwise mix group names don't change in mixer strips
1159 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1160 if ((*i)->mix_group() == group) {
1161 (*i)->mix_group_changed(0);
1165 TreeModel::iterator i;
1166 TreeModel::Children rows = group_model->children();
1167 Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
1169 in_group_row_change = true;
1171 for (i = rows.begin(); i != rows.end(); ++i) {
1172 if ((*i)[group_columns.group] == group) {
1173 (*i)[group_columns.visible] = !group->is_hidden ();
1174 (*i)[group_columns.active] = group->is_active ();
1175 (*i)[group_columns.text] = group->name ();
1176 break;
1180 in_group_row_change = false;
1183 void
1184 Mixer_UI::mix_group_name_edit (const Glib::ustring& path, const Glib::ustring& new_text)
1186 RouteGroup* group;
1187 TreeIter iter;
1189 if ((iter = group_model->get_iter (path))) {
1191 if ((group = (*iter)[group_columns.group]) == 0) {
1192 return;
1195 if (new_text != group->name()) {
1196 group->set_name (new_text);
1201 void
1202 Mixer_UI::mix_group_row_change (const Gtk::TreeModel::Path& path,const Gtk::TreeModel::iterator& iter)
1204 RouteGroup* group;
1206 if (in_group_row_change) {
1207 return;
1210 if ((group = (*iter)[group_columns.group]) == 0) {
1211 return;
1214 if ((*iter)[group_columns.visible]) {
1215 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1216 if ((*i)->mix_group() == group) {
1217 show_strip (*i);
1220 } else {
1221 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1222 if ((*i)->mix_group() == group) {
1223 hide_strip (*i);
1228 bool active = (*iter)[group_columns.active];
1229 group->set_active (active, this);
1231 Glib::ustring name = (*iter)[group_columns.text];
1233 if (name != group->name()) {
1234 group->set_name (name);
1239 void
1240 Mixer_UI::add_mix_group (RouteGroup* group)
1243 ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::add_mix_group), group));
1244 bool focus = false;
1246 in_group_row_change = true;
1248 TreeModel::Row row = *(group_model->append());
1249 row[group_columns.active] = group->is_active();
1251 row[group_columns.visible] = false;
1253 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1254 if ((*i)->mix_group() == group) {
1255 if ((*i)->marked_for_display()) {
1256 row[group_columns.visible] = true;
1258 break;
1262 row[group_columns.group] = group;
1263 if (!group->name().empty()) {
1264 row[group_columns.text] = group->name();
1265 } else {
1266 row[group_columns.text] = _("unnamed");
1267 focus = true;
1270 group->FlagsChanged.connect (bind (mem_fun(*this, &Mixer_UI::group_flags_changed), group));
1272 if (focus) {
1273 TreeViewColumn* col = group_display.get_column (0);
1274 CellRendererText* name_cell = dynamic_cast<CellRendererText*>(group_display.get_column_cell_renderer (0));
1275 group_display.set_cursor (group_model->get_path (row), *col, *name_cell, true);
1278 in_group_row_change = false;
1281 bool
1282 Mixer_UI::strip_scroller_button_release (GdkEventButton* ev)
1284 using namespace Menu_Helpers;
1286 if (Keyboard::is_context_menu_event (ev)) {
1287 ARDOUR_UI::instance()->add_route (this);
1288 return true;
1291 return false;
1294 void
1295 Mixer_UI::set_strip_width (Width w)
1297 _strip_width = w;
1299 for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
1300 (*i)->set_width (w, this);
1304 void
1305 Mixer_UI::set_window_pos_and_size ()
1307 resize (m_width, m_height);
1308 move (m_root_x, m_root_y);
1311 void
1312 Mixer_UI::get_window_pos_and_size ()
1314 get_position(m_root_x, m_root_y);
1315 get_size(m_width, m_height);
1319 Mixer_UI::set_state (const XMLNode& node)
1321 const XMLProperty* prop;
1322 XMLNode* geometry;
1324 m_width = default_width;
1325 m_height = default_height;
1326 m_root_x = 1;
1327 m_root_y = 1;
1329 if ((geometry = find_named_node (node, "geometry")) != 0) {
1331 XMLProperty* prop;
1333 if ((prop = geometry->property("x_size")) == 0) {
1334 prop = geometry->property ("x-size");
1336 if (prop) {
1337 m_width = atoi(prop->value());
1339 if ((prop = geometry->property("y_size")) == 0) {
1340 prop = geometry->property ("y-size");
1342 if (prop) {
1343 m_height = atoi(prop->value());
1346 if ((prop = geometry->property ("x_pos")) == 0) {
1347 prop = geometry->property ("x-pos");
1349 if (prop) {
1350 m_root_x = atoi (prop->value());
1353 if ((prop = geometry->property ("y_pos")) == 0) {
1354 prop = geometry->property ("y-pos");
1356 if (prop) {
1357 m_root_y = atoi (prop->value());
1361 set_window_pos_and_size ();
1363 if ((prop = node.property ("narrow-strips"))) {
1364 if (string_is_affirmative (prop->value())) {
1365 set_strip_width (Narrow);
1366 } else {
1367 set_strip_width (Wide);
1371 if ((prop = node.property ("show-mixer"))) {
1372 if (string_is_affirmative (prop->value())) {
1373 _visible = true;
1377 return 0;
1380 XMLNode&
1381 Mixer_UI::get_state (void)
1383 XMLNode* node = new XMLNode ("Mixer");
1385 if (is_realized()) {
1386 Glib::RefPtr<Gdk::Window> win = get_window();
1388 get_window_pos_and_size ();
1390 XMLNode* geometry = new XMLNode ("geometry");
1391 char buf[32];
1392 snprintf(buf, sizeof(buf), "%d", m_width);
1393 geometry->add_property(X_("x_size"), string(buf));
1394 snprintf(buf, sizeof(buf), "%d", m_height);
1395 geometry->add_property(X_("y_size"), string(buf));
1396 snprintf(buf, sizeof(buf), "%d", m_root_x);
1397 geometry->add_property(X_("x_pos"), string(buf));
1398 snprintf(buf, sizeof(buf), "%d", m_root_y);
1399 geometry->add_property(X_("y_pos"), string(buf));
1401 // written only for compatibility, they are not used.
1402 snprintf(buf, sizeof(buf), "%d", 0);
1403 geometry->add_property(X_("x_off"), string(buf));
1404 snprintf(buf, sizeof(buf), "%d", 0);
1405 geometry->add_property(X_("y_off"), string(buf));
1407 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&rhs_pane1)->gobj()));
1408 geometry->add_property(X_("mixer_rhs_pane1_pos"), string(buf));
1409 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&list_hpane)->gobj()));
1410 geometry->add_property(X_("mixer_list_hpane_pos"), string(buf));
1412 node->add_child_nocopy (*geometry);
1415 node->add_property ("narrow-strips", _strip_width == Narrow ? "yes" : "no");
1417 node->add_property ("show-mixer", _visible ? "yes" : "no");
1419 return *node;
1423 void
1424 Mixer_UI::pane_allocation_handler (Allocation& alloc, Gtk::Paned* which)
1426 int pos;
1427 XMLProperty* prop = 0;
1428 char buf[32];
1429 XMLNode* node = ARDOUR_UI::instance()->mixer_settings();
1430 XMLNode* geometry;
1431 int width, height;
1432 static int32_t done[3] = { 0, 0, 0 };
1434 width = default_width;
1435 height = default_height;
1437 if ((geometry = find_named_node (*node, "geometry")) != 0) {
1440 if ((prop = geometry->property ("x_size")) == 0) {
1441 prop = geometry->property ("x-size");
1443 if (prop) {
1444 width = atoi (prop->value());
1446 if ((prop = geometry->property ("y_size")) == 0) {
1447 prop = geometry->property ("y-size");
1449 if (prop) {
1450 height = atoi (prop->value());
1454 if (which == static_cast<Gtk::Paned*> (&rhs_pane1)) {
1456 if (done[0]) {
1457 return;
1460 if (!geometry || (prop = geometry->property("mixer_rhs_pane1_pos")) == 0) {
1461 pos = height / 3;
1462 snprintf (buf, sizeof(buf), "%d", pos);
1463 } else {
1464 pos = atoi (prop->value());
1467 if ((done[0] = GTK_WIDGET(rhs_pane1.gobj())->allocation.height > pos)) {
1468 rhs_pane1.set_position (pos);
1471 } else if (which == static_cast<Gtk::Paned*> (&list_hpane)) {
1473 if (done[2]) {
1474 return;
1477 if (!geometry || (prop = geometry->property("mixer_list_hpane_pos")) == 0) {
1478 pos = 75;
1479 snprintf (buf, sizeof(buf), "%d", pos);
1480 } else {
1481 pos = atoi (prop->value());
1484 if ((done[2] = GTK_WIDGET(list_hpane.gobj())->allocation.width > pos)) {
1485 list_hpane.set_position (pos);
1490 bool
1491 Mixer_UI::on_key_press_event (GdkEventKey* ev)
1493 return key_press_focus_accelerator_handler (*this, ev);