bump version
[ardour2.git] / gtk2_ardour / mixer_ui.cc
blob866587bb4ead965dcbb01797a8e5b554fe9ecfeb
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 (_("Add group"), mem_fun(*this, &Mixer_UI::new_mix_group)));
994 bool
995 Mixer_UI::group_display_button_press (GdkEventButton* ev)
997 if (Keyboard::is_context_menu_event (ev)) {
998 if (mix_group_context_menu == 0) {
999 build_mix_group_context_menu ();
1001 mix_group_context_menu->popup (1, ev->time);
1002 return true;
1006 RouteGroup* group;
1007 TreeIter iter;
1008 TreeModel::Path path;
1009 TreeViewColumn* column;
1010 int cellx;
1011 int celly;
1013 if (!group_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
1014 return false;
1017 switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
1018 case 0:
1019 if (Keyboard::is_edit_event (ev)) {
1020 if ((iter = group_model->get_iter (path))) {
1021 if ((group = (*iter)[group_columns.group]) != 0) {
1022 // edit_mix_group (group);
1023 #ifdef GTKOSX
1024 group_display.queue_draw();
1025 #endif
1026 return true;
1031 break;
1033 case 1:
1034 if ((iter = group_model->get_iter (path))) {
1035 bool active = (*iter)[group_columns.active];
1036 (*iter)[group_columns.active] = !active;
1037 #ifdef GTKOSX
1038 group_display.queue_draw();
1039 #endif
1040 return true;
1042 break;
1044 case 2:
1045 if ((iter = group_model->get_iter (path))) {
1046 bool visible = (*iter)[group_columns.visible];
1047 (*iter)[group_columns.visible] = !visible;
1048 #ifdef GTKOSX
1049 group_display.queue_draw();
1050 #endif
1051 return true;
1053 break;
1055 default:
1056 break;
1059 return false;
1062 void
1063 Mixer_UI::activate_all_mix_groups ()
1065 Gtk::TreeModel::Children children = group_model->children();
1066 for(Gtk::TreeModel::Children::iterator iter = children.begin(); iter != children.end(); ++iter) {
1067 (*iter)[group_columns.active] = true;
1071 void
1072 Mixer_UI::disable_all_mix_groups ()
1074 Gtk::TreeModel::Children children = group_model->children();
1075 for(Gtk::TreeModel::Children::iterator iter = children.begin(); iter != children.end(); ++iter) {
1076 (*iter)[group_columns.active] = false;
1080 void
1081 Mixer_UI::mix_groups_changed ()
1083 ENSURE_GUI_THREAD (mem_fun (*this, &Mixer_UI::mix_groups_changed));
1085 /* just rebuild the while thing */
1087 group_model->clear ();
1090 TreeModel::Row row;
1091 row = *(group_model->append());
1092 row[group_columns.active] = false;
1093 row[group_columns.visible] = true;
1094 row[group_columns.text] = (_("-all-"));
1095 row[group_columns.group] = 0;
1098 session->foreach_mix_group (mem_fun (*this, &Mixer_UI::add_mix_group));
1101 void
1102 Mixer_UI::new_mix_group ()
1104 session->add_mix_group ("");
1107 void
1108 Mixer_UI::remove_selected_mix_group ()
1110 Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
1111 TreeView::Selection::ListHandle_Path rows = selection->get_selected_rows ();
1113 if (rows.empty()) {
1114 return;
1117 TreeView::Selection::ListHandle_Path::iterator i = rows.begin();
1118 TreeIter iter;
1120 /* selection mode is single, so rows.begin() is it */
1122 if ((iter = group_model->get_iter (*i))) {
1124 RouteGroup* rg = (*iter)[group_columns.group];
1126 if (rg) {
1127 session->remove_mix_group (*rg);
1132 void
1133 Mixer_UI::group_flags_changed (void* src, RouteGroup* group)
1135 if (in_group_row_change) {
1136 return;
1139 ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::group_flags_changed), src, group));
1141 /* force an update of any mixer strips that are using this group,
1142 otherwise mix group names don't change in mixer strips
1145 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1146 if ((*i)->mix_group() == group) {
1147 (*i)->mix_group_changed(0);
1151 TreeModel::iterator i;
1152 TreeModel::Children rows = group_model->children();
1153 Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
1155 in_group_row_change = true;
1157 for (i = rows.begin(); i != rows.end(); ++i) {
1158 if ((*i)[group_columns.group] == group) {
1159 (*i)[group_columns.visible] = !group->is_hidden ();
1160 (*i)[group_columns.active] = group->is_active ();
1161 (*i)[group_columns.text] = group->name ();
1162 break;
1166 in_group_row_change = false;
1169 void
1170 Mixer_UI::mix_group_name_edit (const Glib::ustring& path, const Glib::ustring& new_text)
1172 RouteGroup* group;
1173 TreeIter iter;
1175 if ((iter = group_model->get_iter (path))) {
1177 if ((group = (*iter)[group_columns.group]) == 0) {
1178 return;
1181 if (new_text != group->name()) {
1182 group->set_name (new_text);
1187 void
1188 Mixer_UI::mix_group_row_change (const Gtk::TreeModel::Path& path,const Gtk::TreeModel::iterator& iter)
1190 RouteGroup* group;
1192 if (in_group_row_change) {
1193 return;
1196 if ((group = (*iter)[group_columns.group]) == 0) {
1197 return;
1200 if ((*iter)[group_columns.visible]) {
1201 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1202 if ((*i)->mix_group() == group) {
1203 show_strip (*i);
1206 } else {
1207 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1208 if ((*i)->mix_group() == group) {
1209 hide_strip (*i);
1214 bool active = (*iter)[group_columns.active];
1215 group->set_active (active, this);
1217 Glib::ustring name = (*iter)[group_columns.text];
1219 if (name != group->name()) {
1220 group->set_name (name);
1225 void
1226 Mixer_UI::add_mix_group (RouteGroup* group)
1229 ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::add_mix_group), group));
1230 bool focus = false;
1232 in_group_row_change = true;
1234 TreeModel::Row row = *(group_model->append());
1235 row[group_columns.active] = group->is_active();
1236 row[group_columns.visible] = true;
1237 row[group_columns.group] = group;
1238 if (!group->name().empty()) {
1239 row[group_columns.text] = group->name();
1240 } else {
1241 row[group_columns.text] = _("unnamed");
1242 focus = true;
1245 group->FlagsChanged.connect (bind (mem_fun(*this, &Mixer_UI::group_flags_changed), group));
1247 if (focus) {
1248 TreeViewColumn* col = group_display.get_column (0);
1249 CellRendererText* name_cell = dynamic_cast<CellRendererText*>(group_display.get_column_cell_renderer (0));
1250 group_display.set_cursor (group_model->get_path (row), *col, *name_cell, true);
1253 in_group_row_change = false;
1256 bool
1257 Mixer_UI::strip_scroller_button_release (GdkEventButton* ev)
1259 using namespace Menu_Helpers;
1261 if (Keyboard::is_context_menu_event (ev)) {
1262 ARDOUR_UI::instance()->add_route (this);
1263 return true;
1266 return false;
1269 void
1270 Mixer_UI::set_strip_width (Width w)
1272 _strip_width = w;
1274 for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
1275 (*i)->set_width (w, this);
1279 void
1280 Mixer_UI::set_window_pos_and_size ()
1282 resize (m_width, m_height);
1283 move (m_root_x, m_root_y);
1286 void
1287 Mixer_UI::get_window_pos_and_size ()
1289 get_position(m_root_x, m_root_y);
1290 get_size(m_width, m_height);
1294 Mixer_UI::set_state (const XMLNode& node)
1296 const XMLProperty* prop;
1297 XMLNode* geometry;
1299 m_width = default_width;
1300 m_height = default_height;
1301 m_root_x = 1;
1302 m_root_y = 1;
1304 if ((geometry = find_named_node (node, "geometry")) != 0) {
1306 XMLProperty* prop;
1308 if ((prop = geometry->property("x_size")) == 0) {
1309 prop = geometry->property ("x-size");
1311 if (prop) {
1312 m_width = atoi(prop->value());
1314 if ((prop = geometry->property("y_size")) == 0) {
1315 prop = geometry->property ("y-size");
1317 if (prop) {
1318 m_height = atoi(prop->value());
1321 if ((prop = geometry->property ("x_pos")) == 0) {
1322 prop = geometry->property ("x-pos");
1324 if (prop) {
1325 m_root_x = atoi (prop->value());
1328 if ((prop = geometry->property ("y_pos")) == 0) {
1329 prop = geometry->property ("y-pos");
1331 if (prop) {
1332 m_root_y = atoi (prop->value());
1336 set_window_pos_and_size ();
1338 if ((prop = node.property ("narrow-strips"))) {
1339 if (prop->value() == "yes") {
1340 set_strip_width (Narrow);
1341 } else {
1342 set_strip_width (Wide);
1346 if ((prop = node.property ("show-mixer"))) {
1347 if (prop->value() == "yes") {
1348 _visible = true;
1352 return 0;
1355 XMLNode&
1356 Mixer_UI::get_state (void)
1358 XMLNode* node = new XMLNode ("Mixer");
1360 if (is_realized()) {
1361 Glib::RefPtr<Gdk::Window> win = get_window();
1363 get_window_pos_and_size ();
1365 XMLNode* geometry = new XMLNode ("geometry");
1366 char buf[32];
1367 snprintf(buf, sizeof(buf), "%d", m_width);
1368 geometry->add_property(X_("x_size"), string(buf));
1369 snprintf(buf, sizeof(buf), "%d", m_height);
1370 geometry->add_property(X_("y_size"), string(buf));
1371 snprintf(buf, sizeof(buf), "%d", m_root_x);
1372 geometry->add_property(X_("x_pos"), string(buf));
1373 snprintf(buf, sizeof(buf), "%d", m_root_y);
1374 geometry->add_property(X_("y_pos"), string(buf));
1376 // written only for compatibility, they are not used.
1377 snprintf(buf, sizeof(buf), "%d", 0);
1378 geometry->add_property(X_("x_off"), string(buf));
1379 snprintf(buf, sizeof(buf), "%d", 0);
1380 geometry->add_property(X_("y_off"), string(buf));
1382 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&rhs_pane1)->gobj()));
1383 geometry->add_property(X_("mixer_rhs_pane1_pos"), string(buf));
1384 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&list_hpane)->gobj()));
1385 geometry->add_property(X_("mixer_list_hpane_pos"), string(buf));
1387 node->add_child_nocopy (*geometry);
1390 node->add_property ("narrow-strips", _strip_width == Narrow ? "yes" : "no");
1392 node->add_property ("show-mixer", _visible ? "yes" : "no");
1394 return *node;
1398 void
1399 Mixer_UI::pane_allocation_handler (Allocation& alloc, Gtk::Paned* which)
1401 int pos;
1402 XMLProperty* prop = 0;
1403 char buf[32];
1404 XMLNode* node = ARDOUR_UI::instance()->mixer_settings();
1405 XMLNode* geometry;
1406 int width, height;
1407 static int32_t done[3] = { 0, 0, 0 };
1409 width = default_width;
1410 height = default_height;
1412 if ((geometry = find_named_node (*node, "geometry")) != 0) {
1415 if ((prop = geometry->property ("x_size")) == 0) {
1416 prop = geometry->property ("x-size");
1418 if (prop) {
1419 width = atoi (prop->value());
1421 if ((prop = geometry->property ("y_size")) == 0) {
1422 prop = geometry->property ("y-size");
1424 if (prop) {
1425 height = atoi (prop->value());
1429 if (which == static_cast<Gtk::Paned*> (&rhs_pane1)) {
1431 if (done[0]) {
1432 return;
1435 if (!geometry || (prop = geometry->property("mixer_rhs_pane1_pos")) == 0) {
1436 pos = height / 3;
1437 snprintf (buf, sizeof(buf), "%d", pos);
1438 } else {
1439 pos = atoi (prop->value());
1442 if ((done[0] = GTK_WIDGET(rhs_pane1.gobj())->allocation.height > pos)) {
1443 rhs_pane1.set_position (pos);
1446 } else if (which == static_cast<Gtk::Paned*> (&list_hpane)) {
1448 if (done[2]) {
1449 return;
1452 if (!geometry || (prop = geometry->property("mixer_list_hpane_pos")) == 0) {
1453 pos = 75;
1454 snprintf (buf, sizeof(buf), "%d", pos);
1455 } else {
1456 pos = atoi (prop->value());
1459 if ((done[2] = GTK_WIDGET(list_hpane.gobj())->allocation.width > pos)) {
1460 list_hpane.set_position (pos);
1465 bool
1466 Mixer_UI::on_key_press_event (GdkEventKey* ev)
1468 return key_press_focus_accelerator_handler (*this, ev);