key stroke (left/right arrow) and wheel (left/right, shift-down/up) scrolling in...
[ardour2.git] / gtk2_ardour / mixer_ui.cc
blobe2946f823b495f2b490c4cfca989315cad4b4062
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 set_auto_rebinding (!auto_rebinding);
791 auto_rebind_midi_controls();
794 void
795 Mixer_UI::auto_rebind_midi_controls ()
797 TreeModel::Children rows = track_model->children();
798 TreeModel::Children::iterator i;
799 int pos;
801 // Create bindings for all visible strips and remove those that are not visible
802 pos = 1; // 0 is reserved for the master strip
803 for (i = rows.begin(); i != rows.end(); ++i) {
804 MixerStrip* strip = (*i)[track_columns.strip];
806 if ( (*i)[track_columns.visible] == true ) { // add bindings for
807 // make the actual binding
808 //cout<<"Auto Binding: Visible Strip Found: "<<strip->name()<<endl;
810 int controlValue = pos;
811 if( strip->route()->master() ) {
812 controlValue = 0;
814 else {
815 pos++;
818 PBD::Controllable::CreateBinding ( strip->solo_button->get_controllable(), controlValue, 0);
819 PBD::Controllable::CreateBinding ( strip->mute_button->get_controllable(), controlValue, 1);
821 if( strip->is_audio_track() ) {
822 PBD::Controllable::CreateBinding ( strip->rec_enable_button->get_controllable(), controlValue, 2);
825 PBD::Controllable::CreateBinding ( &(strip->gpm.get_controllable()), controlValue, 3);
826 PBD::Controllable::CreateBinding ( strip->panners.get_controllable(), controlValue, 4);
829 else { // Remove any existing binding
830 PBD::Controllable::DeleteBinding ( strip->solo_button->get_controllable() );
831 PBD::Controllable::DeleteBinding ( strip->mute_button->get_controllable() );
833 if( strip->is_audio_track() ) {
834 PBD::Controllable::DeleteBinding ( strip->rec_enable_button->get_controllable() );
837 PBD::Controllable::DeleteBinding ( &(strip->gpm.get_controllable()) );
838 PBD::Controllable::DeleteBinding ( strip->panners.get_controllable() ); // This only takes the first panner if there are multiples...
841 } // for
846 struct SignalOrderRouteSorter {
847 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
848 /* use of ">" forces the correct sort order */
849 return a->order_key (Mixer_UI::get_order_key()) < b->order_key (Mixer_UI::get_order_key());
853 void
854 Mixer_UI::initial_track_display ()
856 boost::shared_ptr<Session::RouteList> routes = session->get_routes();
857 Session::RouteList copy (*routes);
858 SignalOrderRouteSorter sorter;
860 copy.sort (sorter);
862 no_track_list_redisplay = true;
864 track_model->clear ();
866 add_strip (copy);
868 no_track_list_redisplay = false;
870 redisplay_track_list ();
873 void
874 Mixer_UI::show_track_list_menu ()
876 if (track_menu == 0) {
877 build_track_menu ();
880 track_menu->popup (1, gtk_get_current_event_time());
883 bool
884 Mixer_UI::track_display_button_press (GdkEventButton* ev)
886 if (Keyboard::is_context_menu_event (ev)) {
887 show_track_list_menu ();
888 return true;
891 TreeIter iter;
892 TreeModel::Path path;
893 TreeViewColumn* column;
894 int cellx;
895 int celly;
897 if (!track_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
898 return false;
901 switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
902 case 0:
903 /* allow normal processing to occur */
904 return false;
906 case 1: /* visibility */
908 if ((iter = track_model->get_iter (path))) {
909 MixerStrip* strip = (*iter)[track_columns.strip];
910 if (strip) {
912 if (!strip->route()->master() && !strip->route()->control()) {
913 bool visible = (*iter)[track_columns.visible];
914 (*iter)[track_columns.visible] = !visible;
916 #ifdef GTKOSX
917 track_display.queue_draw();
918 #endif
921 return true;
923 default:
924 break;
927 return false;
931 void
932 Mixer_UI::build_track_menu ()
934 using namespace Menu_Helpers;
935 using namespace Gtk;
937 track_menu = new Menu;
938 track_menu->set_name ("ArdourContextMenu");
939 MenuList& items = track_menu->items();
941 items.push_back (MenuElem (_("Show All"), mem_fun(*this, &Mixer_UI::show_all_routes)));
942 items.push_back (MenuElem (_("Hide All"), mem_fun(*this, &Mixer_UI::hide_all_routes)));
943 items.push_back (MenuElem (_("Show All Audio Tracks"), mem_fun(*this, &Mixer_UI::show_all_audiotracks)));
944 items.push_back (MenuElem (_("Hide All Audio Tracks"), mem_fun(*this, &Mixer_UI::hide_all_audiotracks)));
945 items.push_back (MenuElem (_("Show All Audio Busses"), mem_fun(*this, &Mixer_UI::show_all_audiobus)));
946 items.push_back (MenuElem (_("Hide All Audio Busses"), mem_fun(*this, &Mixer_UI::hide_all_audiobus)));
950 void
951 Mixer_UI::strip_name_changed (void* src, MixerStrip* mx)
953 ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::strip_name_changed), src, mx));
955 TreeModel::Children rows = track_model->children();
956 TreeModel::Children::iterator i;
958 for (i = rows.begin(); i != rows.end(); ++i) {
959 if ((*i)[track_columns.strip] == mx) {
960 (*i)[track_columns.text] = mx->route()->name();
961 return;
965 error << _("track display list item for renamed strip not found!") << endmsg;
969 void
970 Mixer_UI::build_mix_group_context_menu ()
972 using namespace Gtk::Menu_Helpers;
974 mix_group_context_menu = new Menu;
975 mix_group_context_menu->set_name ("ArdourContextMenu");
976 MenuList& items = mix_group_context_menu->items();
978 items.push_back (MenuElem (_("Activate All"), mem_fun(*this, &Mixer_UI::activate_all_mix_groups)));
979 items.push_back (MenuElem (_("Disable All"), mem_fun(*this, &Mixer_UI::disable_all_mix_groups)));
980 items.push_back (SeparatorElem());
981 items.push_back (MenuElem (_("Show All"), mem_fun(*this, &Mixer_UI::show_all_mix_groups)));
982 items.push_back (MenuElem (_("Hide All"), mem_fun(*this, &Mixer_UI::hide_all_mix_groups)));
983 items.push_back (SeparatorElem());
984 items.push_back (MenuElem (_("Add group"), mem_fun(*this, &Mixer_UI::new_mix_group)));
988 bool
989 Mixer_UI::group_display_button_press (GdkEventButton* ev)
991 if (Keyboard::is_context_menu_event (ev)) {
992 if (mix_group_context_menu == 0) {
993 build_mix_group_context_menu ();
995 mix_group_context_menu->popup (1, ev->time);
996 return true;
1000 RouteGroup* group;
1001 TreeIter iter;
1002 TreeModel::Path path;
1003 TreeViewColumn* column;
1004 int cellx;
1005 int celly;
1007 if (!group_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
1008 return false;
1011 switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
1012 case 0:
1013 if (Keyboard::is_edit_event (ev)) {
1014 if ((iter = group_model->get_iter (path))) {
1015 if ((group = (*iter)[group_columns.group]) != 0) {
1016 // edit_mix_group (group);
1017 #ifdef GTKOSX
1018 group_display.queue_draw();
1019 #endif
1020 return true;
1025 break;
1027 case 1:
1028 if ((iter = group_model->get_iter (path))) {
1029 bool active = (*iter)[group_columns.active];
1030 (*iter)[group_columns.active] = !active;
1031 #ifdef GTKOSX
1032 group_display.queue_draw();
1033 #endif
1034 return true;
1036 break;
1038 case 2:
1039 if ((iter = group_model->get_iter (path))) {
1040 bool visible = (*iter)[group_columns.visible];
1041 (*iter)[group_columns.visible] = !visible;
1042 #ifdef GTKOSX
1043 group_display.queue_draw();
1044 #endif
1045 return true;
1047 break;
1049 default:
1050 break;
1053 return false;
1056 void
1057 Mixer_UI::activate_all_mix_groups ()
1059 Gtk::TreeModel::Children children = group_model->children();
1060 for(Gtk::TreeModel::Children::iterator iter = children.begin(); iter != children.end(); ++iter) {
1061 (*iter)[group_columns.active] = true;
1067 void
1068 Mixer_UI::disable_all_mix_groups ()
1070 Gtk::TreeModel::Children children = group_model->children();
1071 for(Gtk::TreeModel::Children::iterator iter = children.begin(); iter != children.end(); ++iter) {
1072 (*iter)[group_columns.active] = false;
1076 void
1077 Mixer_UI::show_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.visible] = true;
1085 void
1086 Mixer_UI::hide_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] = false;
1094 void
1095 Mixer_UI::mix_groups_changed ()
1097 ENSURE_GUI_THREAD (mem_fun (*this, &Mixer_UI::mix_groups_changed));
1099 /* just rebuild the while thing */
1101 group_model->clear ();
1103 session->foreach_mix_group (mem_fun (*this, &Mixer_UI::add_mix_group));
1106 void
1107 Mixer_UI::new_mix_group ()
1109 session->add_mix_group ("");
1112 void
1113 Mixer_UI::remove_selected_mix_group ()
1115 Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
1116 TreeView::Selection::ListHandle_Path rows = selection->get_selected_rows ();
1118 if (rows.empty()) {
1119 return;
1122 TreeView::Selection::ListHandle_Path::iterator i = rows.begin();
1123 TreeIter iter;
1125 /* selection mode is single, so rows.begin() is it */
1127 if ((iter = group_model->get_iter (*i))) {
1129 RouteGroup* rg = (*iter)[group_columns.group];
1131 if (rg) {
1132 session->remove_mix_group (*rg);
1137 void
1138 Mixer_UI::group_flags_changed (void* src, RouteGroup* group)
1140 if (in_group_row_change) {
1141 return;
1144 ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::group_flags_changed), src, group));
1146 /* force an update of any mixer strips that are using this group,
1147 otherwise mix group names don't change in mixer strips
1150 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1151 if ((*i)->mix_group() == group) {
1152 (*i)->mix_group_changed(0);
1156 TreeModel::iterator i;
1157 TreeModel::Children rows = group_model->children();
1158 Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
1160 in_group_row_change = true;
1162 for (i = rows.begin(); i != rows.end(); ++i) {
1163 if ((*i)[group_columns.group] == group) {
1164 (*i)[group_columns.visible] = !group->is_hidden ();
1165 (*i)[group_columns.active] = group->is_active ();
1166 (*i)[group_columns.text] = group->name ();
1167 break;
1171 in_group_row_change = false;
1174 void
1175 Mixer_UI::mix_group_name_edit (const Glib::ustring& path, const Glib::ustring& new_text)
1177 RouteGroup* group;
1178 TreeIter iter;
1180 if ((iter = group_model->get_iter (path))) {
1182 if ((group = (*iter)[group_columns.group]) == 0) {
1183 return;
1186 if (new_text != group->name()) {
1187 group->set_name (new_text);
1192 void
1193 Mixer_UI::mix_group_row_change (const Gtk::TreeModel::Path& path,const Gtk::TreeModel::iterator& iter)
1195 RouteGroup* group;
1197 if (in_group_row_change) {
1198 return;
1201 if ((group = (*iter)[group_columns.group]) == 0) {
1202 return;
1205 if ((*iter)[group_columns.visible]) {
1206 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1207 if ((*i)->mix_group() == group) {
1208 show_strip (*i);
1211 } else {
1212 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1213 if ((*i)->mix_group() == group) {
1214 hide_strip (*i);
1219 bool active = (*iter)[group_columns.active];
1220 group->set_active (active, this);
1222 Glib::ustring name = (*iter)[group_columns.text];
1224 if (name != group->name()) {
1225 group->set_name (name);
1230 void
1231 Mixer_UI::add_mix_group (RouteGroup* group)
1234 ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::add_mix_group), group));
1235 bool focus = false;
1237 in_group_row_change = true;
1239 TreeModel::Row row = *(group_model->append());
1240 row[group_columns.active] = group->is_active();
1242 row[group_columns.visible] = false;
1244 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1245 if ((*i)->mix_group() == group) {
1246 if ((*i)->marked_for_display()) {
1247 row[group_columns.visible] = true;
1249 break;
1253 row[group_columns.group] = group;
1254 if (!group->name().empty()) {
1255 row[group_columns.text] = group->name();
1256 } else {
1257 row[group_columns.text] = _("unnamed");
1258 focus = true;
1261 group->FlagsChanged.connect (bind (mem_fun(*this, &Mixer_UI::group_flags_changed), group));
1263 if (focus) {
1264 TreeViewColumn* col = group_display.get_column (0);
1265 CellRendererText* name_cell = dynamic_cast<CellRendererText*>(group_display.get_column_cell_renderer (0));
1266 group_display.set_cursor (group_model->get_path (row), *col, *name_cell, true);
1269 in_group_row_change = false;
1272 bool
1273 Mixer_UI::strip_scroller_button_release (GdkEventButton* ev)
1275 using namespace Menu_Helpers;
1277 if (Keyboard::is_context_menu_event (ev)) {
1278 ARDOUR_UI::instance()->add_route (this);
1279 return true;
1282 return false;
1285 void
1286 Mixer_UI::set_strip_width (Width w)
1288 _strip_width = w;
1290 for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
1291 (*i)->set_width (w, this);
1295 void
1296 Mixer_UI::set_window_pos_and_size ()
1298 resize (m_width, m_height);
1299 move (m_root_x, m_root_y);
1302 void
1303 Mixer_UI::get_window_pos_and_size ()
1305 get_position(m_root_x, m_root_y);
1306 get_size(m_width, m_height);
1310 Mixer_UI::set_state (const XMLNode& node)
1312 const XMLProperty* prop;
1313 XMLNode* geometry;
1315 m_width = default_width;
1316 m_height = default_height;
1317 m_root_x = 1;
1318 m_root_y = 1;
1320 if ((geometry = find_named_node (node, "geometry")) != 0) {
1322 XMLProperty* prop;
1324 if ((prop = geometry->property("x_size")) == 0) {
1325 prop = geometry->property ("x-size");
1327 if (prop) {
1328 m_width = atoi(prop->value());
1330 if ((prop = geometry->property("y_size")) == 0) {
1331 prop = geometry->property ("y-size");
1333 if (prop) {
1334 m_height = atoi(prop->value());
1337 if ((prop = geometry->property ("x_pos")) == 0) {
1338 prop = geometry->property ("x-pos");
1340 if (prop) {
1341 m_root_x = atoi (prop->value());
1344 if ((prop = geometry->property ("y_pos")) == 0) {
1345 prop = geometry->property ("y-pos");
1347 if (prop) {
1348 m_root_y = atoi (prop->value());
1352 set_window_pos_and_size ();
1354 if ((prop = node.property ("narrow-strips"))) {
1355 if (string_is_affirmative (prop->value())) {
1356 set_strip_width (Narrow);
1357 } else {
1358 set_strip_width (Wide);
1362 if ((prop = node.property ("show-mixer"))) {
1363 if (string_is_affirmative (prop->value())) {
1364 _visible = true;
1368 return 0;
1371 XMLNode&
1372 Mixer_UI::get_state (void)
1374 XMLNode* node = new XMLNode ("Mixer");
1376 if (is_realized()) {
1377 Glib::RefPtr<Gdk::Window> win = get_window();
1379 get_window_pos_and_size ();
1381 XMLNode* geometry = new XMLNode ("geometry");
1382 char buf[32];
1383 snprintf(buf, sizeof(buf), "%d", m_width);
1384 geometry->add_property(X_("x_size"), string(buf));
1385 snprintf(buf, sizeof(buf), "%d", m_height);
1386 geometry->add_property(X_("y_size"), string(buf));
1387 snprintf(buf, sizeof(buf), "%d", m_root_x);
1388 geometry->add_property(X_("x_pos"), string(buf));
1389 snprintf(buf, sizeof(buf), "%d", m_root_y);
1390 geometry->add_property(X_("y_pos"), string(buf));
1392 // written only for compatibility, they are not used.
1393 snprintf(buf, sizeof(buf), "%d", 0);
1394 geometry->add_property(X_("x_off"), string(buf));
1395 snprintf(buf, sizeof(buf), "%d", 0);
1396 geometry->add_property(X_("y_off"), string(buf));
1398 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&rhs_pane1)->gobj()));
1399 geometry->add_property(X_("mixer_rhs_pane1_pos"), string(buf));
1400 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&list_hpane)->gobj()));
1401 geometry->add_property(X_("mixer_list_hpane_pos"), string(buf));
1403 node->add_child_nocopy (*geometry);
1406 node->add_property ("narrow-strips", _strip_width == Narrow ? "yes" : "no");
1408 node->add_property ("show-mixer", _visible ? "yes" : "no");
1410 return *node;
1414 void
1415 Mixer_UI::pane_allocation_handler (Allocation& alloc, Gtk::Paned* which)
1417 int pos;
1418 XMLProperty* prop = 0;
1419 char buf[32];
1420 XMLNode* node = ARDOUR_UI::instance()->mixer_settings();
1421 XMLNode* geometry;
1422 int width, height;
1423 static int32_t done[3] = { 0, 0, 0 };
1425 width = default_width;
1426 height = default_height;
1428 if ((geometry = find_named_node (*node, "geometry")) != 0) {
1431 if ((prop = geometry->property ("x_size")) == 0) {
1432 prop = geometry->property ("x-size");
1434 if (prop) {
1435 width = atoi (prop->value());
1437 if ((prop = geometry->property ("y_size")) == 0) {
1438 prop = geometry->property ("y-size");
1440 if (prop) {
1441 height = atoi (prop->value());
1445 if (which == static_cast<Gtk::Paned*> (&rhs_pane1)) {
1447 if (done[0]) {
1448 return;
1451 if (!geometry || (prop = geometry->property("mixer_rhs_pane1_pos")) == 0) {
1452 pos = height / 3;
1453 snprintf (buf, sizeof(buf), "%d", pos);
1454 } else {
1455 pos = atoi (prop->value());
1458 if ((done[0] = GTK_WIDGET(rhs_pane1.gobj())->allocation.height > pos)) {
1459 rhs_pane1.set_position (pos);
1462 } else if (which == static_cast<Gtk::Paned*> (&list_hpane)) {
1464 if (done[2]) {
1465 return;
1468 if (!geometry || (prop = geometry->property("mixer_list_hpane_pos")) == 0) {
1469 pos = 75;
1470 snprintf (buf, sizeof(buf), "%d", pos);
1471 } else {
1472 pos = atoi (prop->value());
1475 if ((done[2] = GTK_WIDGET(list_hpane.gobj())->allocation.width > pos)) {
1476 list_hpane.set_position (pos);
1481 void
1482 Mixer_UI::scroll_left ()
1484 Adjustment* adj = scroller.get_hscrollbar()->get_adjustment();
1485 /* stupid GTK: can't rely on clamping across versions */
1486 scroller.get_hscrollbar()->set_value (max (adj->get_lower(), adj->get_value() - adj->get_step_increment()));
1489 void
1490 Mixer_UI::scroll_right ()
1492 Adjustment* adj = scroller.get_hscrollbar()->get_adjustment();
1493 /* stupid GTK: can't rely on clamping across versions */
1494 scroller.get_hscrollbar()->set_value (min (adj->get_upper(), adj->get_value() + adj->get_step_increment()));
1497 bool
1498 Mixer_UI::on_key_press_event (GdkEventKey* ev)
1500 switch (ev->keyval) {
1501 case GDK_Left:
1502 scroll_left ();
1503 return true;
1505 case GDK_Right:
1506 scroll_right ();
1507 return true;
1509 default:
1510 break;
1513 return key_press_focus_accelerator_handler (*this, ev);
1516 bool
1517 Mixer_UI::on_scroll_event (GdkEventScroll* ev)
1519 switch (ev->direction) {
1520 case GDK_SCROLL_LEFT:
1521 scroll_left ();
1522 return true;
1523 case GDK_SCROLL_UP:
1524 if (ev->state & Keyboard::TertiaryModifier) {
1525 scroll_left ();
1526 return true;
1528 return false;
1530 case GDK_SCROLL_RIGHT:
1531 scroll_right ();
1532 return true;
1534 case GDK_SCROLL_DOWN:
1535 if (ev->state & Keyboard::TertiaryModifier) {
1536 scroll_right ();
1537 return true;
1539 return false;
1542 return false;