if STRIP is not set, don't strip included libraries either
[ardour2.git] / gtk2_ardour / mixer_ui.cc
blob264b975735188679cf617e40f2e8f3f0ed564d99
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 #ifdef WAF_BUILD
21 #include "gtk2ardour-config.h"
22 #endif
24 #include <algorithm>
25 #include <map>
26 #include <sigc++/bind.h>
28 #include <gtkmm/accelmap.h>
30 #include "pbd/convert.h"
31 #include "pbd/stacktrace.h"
32 #include <glibmm/thread.h>
34 #include <gtkmm2ext/gtk_ui.h>
35 #include <gtkmm2ext/utils.h>
36 #include <gtkmm2ext/tearoff.h>
37 #include <gtkmm2ext/window_title.h>
39 #include "ardour/audio_track.h"
40 #include "ardour/plugin_manager.h"
41 #include "ardour/route_group.h"
42 #include "ardour/session.h"
43 #include "ardour/session_route.h"
45 #include "keyboard.h"
46 #include "mixer_ui.h"
47 #include "mixer_strip.h"
48 #include "monitor_section.h"
49 #include "plugin_selector.h"
50 #include "ardour_ui.h"
51 #include "prompter.h"
52 #include "utils.h"
53 #include "actions.h"
54 #include "gui_thread.h"
55 #include "mixer_group_tabs.h"
57 #include "i18n.h"
59 using namespace ARDOUR;
60 using namespace PBD;
61 using namespace Gtk;
62 using namespace Glib;
63 using namespace Gtkmm2ext;
64 using namespace std;
66 using PBD::atoi;
68 Mixer_UI::Mixer_UI ()
69 : Window (Gtk::WINDOW_TOPLEVEL)
71 _strip_width = Config->get_default_narrow_ms() ? Narrow : Wide;
72 track_menu = 0;
73 _monitor_section = 0;
74 no_track_list_redisplay = false;
75 in_group_row_change = false;
76 _visible = false;
77 strip_redisplay_does_not_reset_order_keys = false;
78 strip_redisplay_does_not_sync_order_keys = false;
79 ignore_sync = false;
81 Route::SyncOrderKeys.connect (*this, invalidator (*this), ui_bind (&Mixer_UI::sync_order_keys, this, _1), gui_context());
83 scroller_base.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
84 scroller_base.set_name ("MixerWindow");
85 scroller_base.signal_button_release_event().connect (sigc::mem_fun(*this, &Mixer_UI::strip_scroller_button_release));
86 // add as last item of strip packer
87 strip_packer.pack_end (scroller_base, true, true);
89 _group_tabs = new MixerGroupTabs (this);
90 VBox* b = manage (new VBox);
91 b->pack_start (*_group_tabs, PACK_SHRINK);
92 b->pack_start (strip_packer);
93 b->show_all ();
95 scroller.add (*b);
96 scroller.set_policy (Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
98 setup_track_display ();
100 group_model = ListStore::create (group_columns);
101 group_display.set_model (group_model);
102 group_display.append_column (_("Group"), group_columns.text);
103 group_display.append_column (_("Show"), group_columns.visible);
104 group_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0));
105 group_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
106 group_display.get_column (0)->set_expand(true);
107 group_display.get_column (1)->set_expand(false);
108 group_display.set_name ("MixerGroupList");
109 group_display.get_selection()->set_mode (Gtk::SELECTION_SINGLE);
110 group_display.set_reorderable (true);
111 group_display.set_headers_visible (true);
112 group_display.set_rules_hint (true);
114 /* name is directly editable */
116 CellRendererText* name_cell = dynamic_cast<CellRendererText*>(group_display.get_column_cell_renderer (0));
117 name_cell->property_editable() = true;
118 name_cell->signal_edited().connect (sigc::mem_fun (*this, &Mixer_UI::route_group_name_edit));
120 /* use checkbox for the active column */
122 CellRendererToggle* active_cell = dynamic_cast<CellRendererToggle*>(group_display.get_column_cell_renderer (1));
123 active_cell->property_activatable() = true;
124 active_cell->property_radio() = false;
126 group_model->signal_row_changed().connect (sigc::mem_fun (*this, &Mixer_UI::route_group_row_change));
128 group_display.signal_button_press_event().connect (sigc::mem_fun (*this, &Mixer_UI::group_display_button_press), false);
130 group_display_scroller.add (group_display);
131 group_display_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
133 HBox* route_group_display_button_box = manage (new HBox());
135 Button* route_group_add_button = manage (new Button ());
136 Button* route_group_remove_button = manage (new Button ());
138 Widget* w;
140 w = manage (new Image (Stock::ADD, ICON_SIZE_BUTTON));
141 w->show();
142 route_group_add_button->add (*w);
144 w = manage (new Image (Stock::REMOVE, ICON_SIZE_BUTTON));
145 w->show();
146 route_group_remove_button->add (*w);
148 route_group_display_button_box->set_homogeneous (true);
150 route_group_add_button->signal_clicked().connect (sigc::mem_fun (*this, &Mixer_UI::new_route_group));
151 route_group_remove_button->signal_clicked().connect (sigc::mem_fun (*this, &Mixer_UI::remove_selected_route_group));
153 route_group_display_button_box->add (*route_group_add_button);
154 route_group_display_button_box->add (*route_group_remove_button);
156 group_display_vbox.pack_start (group_display_scroller, true, true);
157 group_display_vbox.pack_start (*route_group_display_button_box, false, false);
159 group_display_frame.set_name ("BaseFrame");
160 group_display_frame.set_shadow_type (Gtk::SHADOW_IN);
161 group_display_frame.add (group_display_vbox);
163 rhs_pane1.pack1 (track_display_frame);
164 rhs_pane1.pack2 (group_display_frame);
166 list_vpacker.pack_start (rhs_pane1, true, true);
168 global_hpacker.pack_start (scroller, true, true);
169 #ifdef GTKOSX
170 /* current gtk-quartz has dirty updates on borders like this one */
171 global_hpacker.pack_start (out_packer, false, false, 0);
172 #else
173 global_hpacker.pack_start (out_packer, false, false, 12);
174 #endif
175 list_hpane.add1(list_vpacker);
176 list_hpane.add2(global_hpacker);
178 rhs_pane1.signal_size_allocate().connect (sigc::bind (sigc::mem_fun(*this, &Mixer_UI::pane_allocation_handler),
179 static_cast<Gtk::Paned*> (&rhs_pane1)));
180 list_hpane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun(*this, &Mixer_UI::pane_allocation_handler),
181 static_cast<Gtk::Paned*> (&list_hpane)));
183 global_vpacker.pack_start (list_hpane, true, true);
185 add (global_vpacker);
186 set_name ("MixerWindow");
188 WindowTitle title(Glib::get_application_name());
189 title += _("Mixer");
190 set_title (title.get_string());
192 set_wmclass (X_("ardour_mixer"), PROGRAM_NAME);
194 add_accel_group (ActionManager::ui_manager->get_accel_group());
196 signal_delete_event().connect (sigc::mem_fun (*this, &Mixer_UI::hide_window));
197 add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
199 signal_configure_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::configure_handler));
201 _selection.RoutesChanged.connect (sigc::mem_fun(*this, &Mixer_UI::follow_strip_selection));
203 route_group_display_button_box->show();
204 route_group_add_button->show();
205 route_group_remove_button->show();
207 global_hpacker.show();
208 global_vpacker.show();
209 scroller.show();
210 scroller_base.show();
211 scroller_hpacker.show();
212 mixer_scroller_vpacker.show();
213 list_vpacker.show();
214 group_display_button_label.show();
215 group_display_button.show();
216 group_display_scroller.show();
217 group_display_vbox.show();
218 group_display_frame.show();
219 rhs_pane1.show();
220 strip_packer.show();
221 out_packer.show();
222 list_hpane.show();
223 group_display.show();
225 auto_rebinding = FALSE;
227 MixerStrip::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&Mixer_UI::remove_strip, this, _1), gui_context());
229 MonitorSection::setup_knob_images ();
231 #ifndef DEFER_PLUGIN_SELECTOR_LOAD
232 _plugin_selector = new PluginSelector (PluginManager::the_manager ());
233 #endif
236 Mixer_UI::~Mixer_UI ()
240 void
241 Mixer_UI::ensure_float (Window& win)
243 win.set_transient_for (*this);
246 void
247 Mixer_UI::show_window ()
249 present ();
250 if (!_visible) {
251 set_window_pos_and_size ();
253 /* now reset each strips width so the right widgets are shown */
254 MixerStrip* ms;
256 TreeModel::Children rows = track_model->children();
257 TreeModel::Children::iterator ri;
259 for (ri = rows.begin(); ri != rows.end(); ++ri) {
260 ms = (*ri)[track_columns.strip];
261 ms->set_width_enum (ms->get_width_enum (), ms->width_owner());
264 _visible = true;
267 bool
268 Mixer_UI::hide_window (GdkEventAny *ev)
270 get_window_pos_and_size ();
272 _visible = false;
273 return just_hide_it(ev, static_cast<Gtk::Window *>(this));
277 void
278 Mixer_UI::add_strip (RouteList& routes)
280 ENSURE_GUI_THREAD (*this, &Mixer_UI::add_strip, routes)
282 MixerStrip* strip;
284 no_track_list_redisplay = true;
285 strip_redisplay_does_not_sync_order_keys = true;
287 for (RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
288 boost::shared_ptr<Route> route = (*x);
290 if (route->is_hidden()) {
291 continue;
294 if (route->is_monitor()) {
295 if (!_monitor_section) {
296 _monitor_section = new MonitorSection (_session);
297 out_packer.pack_end (_monitor_section->tearoff(), false, false);
298 } else {
299 _monitor_section->set_session (_session);
302 _monitor_section->tearoff().show_all ();
304 XMLNode* mnode = ARDOUR_UI::instance()->tearoff_settings (X_("monitor-section"));
305 if (mnode) {
306 _monitor_section->tearoff().set_state (*mnode);
309 /* no regular strip shown for control out */
311 continue;
314 strip = new MixerStrip (*this, _session, route);
315 strips.push_back (strip);
317 Config->get_default_narrow_ms() ? _strip_width = Narrow : _strip_width = Wide;
319 if (strip->width_owner() != strip) {
320 strip->set_width_enum (_strip_width, this);
323 show_strip (strip);
325 TreeModel::Row row = *(track_model->append());
326 row[track_columns.text] = route->name();
327 row[track_columns.visible] = strip->marked_for_display();
328 row[track_columns.route] = route;
329 row[track_columns.strip] = strip;
331 if (route->order_key (N_("signal")) == -1) {
332 route->set_order_key (N_("signal"), track_model->children().size()-1);
335 route->PropertyChanged.connect (*this, invalidator (*this), ui_bind (&Mixer_UI::strip_property_changed, this, _1, strip), gui_context());
337 strip->WidthChanged.connect (sigc::mem_fun(*this, &Mixer_UI::strip_width_changed));
338 strip->signal_button_release_event().connect (sigc::bind (sigc::mem_fun(*this, &Mixer_UI::strip_button_release_event), strip));
341 no_track_list_redisplay = false;
343 redisplay_track_list ();
345 strip_redisplay_does_not_sync_order_keys = false;
348 void
349 Mixer_UI::remove_strip (MixerStrip* strip)
351 if (_session && _session->deletion_in_progress()) {
352 /* its all being taken care of */
353 return;
356 ENSURE_GUI_THREAD (*this, &Mixer_UI::remove_strip, strip);
358 cerr << "Mixer UI removing strip for " << strip << endl;
360 TreeModel::Children rows = track_model->children();
361 TreeModel::Children::iterator ri;
362 list<MixerStrip *>::iterator i;
364 if ((i = find (strips.begin(), strips.end(), strip)) != strips.end()) {
365 strips.erase (i);
368 strip_redisplay_does_not_sync_order_keys = true;
370 for (ri = rows.begin(); ri != rows.end(); ++ri) {
371 if ((*ri)[track_columns.strip] == strip) {
372 track_model->erase (ri);
373 break;
377 strip_redisplay_does_not_sync_order_keys = false;
380 void
381 Mixer_UI::sync_order_keys (string const & src)
383 TreeModel::Children rows = track_model->children();
384 TreeModel::Children::iterator ri;
386 if (src == N_("signal") || !_session || (_session->state_of_the_state() & (Session::Loading|Session::Deletion)) || rows.empty()) {
387 return;
390 std::map<int,int> keys;
392 bool changed = false;
394 unsigned order = 0;
395 for (ri = rows.begin(); ri != rows.end(); ++ri, ++order) {
396 boost::shared_ptr<Route> route = (*ri)[track_columns.route];
397 unsigned int old_key = order;
398 unsigned int new_key = route->order_key (N_("signal"));
400 keys[new_key] = old_key;
402 if (new_key != old_key) {
403 changed = true;
407 if (keys.size() != rows.size()) {
408 PBD::stacktrace (cerr, 20);
410 assert(keys.size() == rows.size());
412 // Remove any gaps in keys caused by automation children tracks
413 vector<int> neworder;
414 for (std::map<int,int>::const_iterator i = keys.begin(); i != keys.end(); ++i) {
415 neworder.push_back(i->second);
417 assert(neworder.size() == rows.size());
419 if (changed) {
420 strip_redisplay_does_not_reset_order_keys = true;
421 track_model->reorder (neworder);
422 strip_redisplay_does_not_reset_order_keys = false;
426 void
427 Mixer_UI::follow_strip_selection ()
429 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
430 (*i)->set_selected (_selection.selected ((*i)->route()));
434 bool
435 Mixer_UI::strip_button_release_event (GdkEventButton *ev, MixerStrip *strip)
437 if (ev->button == 1) {
439 /* this allows the user to click on the strip to terminate comment
440 editing. XXX it needs improving so that we don't select the strip
441 at the same time.
444 if (_selection.selected (strip->route())) {
445 _selection.remove (strip->route());
446 } else {
447 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
448 _selection.add (strip->route());
449 } else {
450 _selection.set (strip->route());
455 return true;
458 void
459 Mixer_UI::set_session (Session* sess)
461 SessionHandlePtr::set_session (sess);
463 if (_plugin_selector) {
464 _plugin_selector->set_session (_session);
467 _group_tabs->set_session (sess);
469 if (!_session) {
470 return;
473 XMLNode* node = ARDOUR_UI::instance()->mixer_settings();
474 set_state (*node);
476 WindowTitle title(_session->name());
477 title += _("Mixer");
478 title += Glib::get_application_name();
480 set_title (title.get_string());
482 initial_track_display ();
484 _session->RouteAdded.connect (_session_connections, invalidator (*this), ui_bind (&Mixer_UI::add_strip, this, _1), gui_context());
485 _session->route_group_added.connect (_session_connections, invalidator (*this), ui_bind (&Mixer_UI::add_route_group, this, _1), gui_context());
486 _session->route_group_removed.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::route_groups_changed, this), gui_context());
487 _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), ui_bind (&Mixer_UI::parameter_changed, this, _1), gui_context());
489 Config->ParameterChanged.connect (*this, invalidator (*this), ui_bind (&Mixer_UI::parameter_changed, this, _1), gui_context ());
491 route_groups_changed ();
493 if (_visible) {
494 show_window();
497 start_updating ();
500 void
501 Mixer_UI::session_going_away ()
503 ENSURE_GUI_THREAD (*this, &Mixer_UI::session_going_away)
505 group_model->clear ();
506 _selection.clear ();
507 track_model->clear ();
509 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
510 delete (*i);
513 if (_monitor_section) {
514 _monitor_section->tearoff().hide_visible ();
517 strips.clear ();
519 WindowTitle title(Glib::get_application_name());
520 title += _("Mixer");
521 set_title (title.get_string());
523 stop_updating ();
525 SessionHandlePtr::session_going_away ();
528 void
529 Mixer_UI::show_strip (MixerStrip* ms)
531 TreeModel::Children rows = track_model->children();
532 TreeModel::Children::iterator i;
534 for (i = rows.begin(); i != rows.end(); ++i) {
536 MixerStrip* strip = (*i)[track_columns.strip];
537 if (strip == ms) {
538 (*i)[track_columns.visible] = true;
539 break;
544 void
545 Mixer_UI::hide_strip (MixerStrip* ms)
547 TreeModel::Children rows = track_model->children();
548 TreeModel::Children::iterator i;
550 for (i = rows.begin(); i != rows.end(); ++i) {
552 MixerStrip* strip = (*i)[track_columns.strip];
553 if (strip == ms) {
554 (*i)[track_columns.visible] = false;
555 break;
560 gint
561 Mixer_UI::start_updating ()
563 fast_screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (sigc::mem_fun(*this, &Mixer_UI::fast_update_strips));
564 return 0;
567 gint
568 Mixer_UI::stop_updating ()
570 fast_screen_update_connection.disconnect();
571 return 0;
574 void
575 Mixer_UI::fast_update_strips ()
577 if (is_mapped () && _session) {
578 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
579 (*i)->fast_update ();
584 void
585 Mixer_UI::set_all_strips_visibility (bool yn)
587 TreeModel::Children rows = track_model->children();
588 TreeModel::Children::iterator i;
590 no_track_list_redisplay = true;
592 for (i = rows.begin(); i != rows.end(); ++i) {
594 TreeModel::Row row = (*i);
595 MixerStrip* strip = row[track_columns.strip];
597 if (strip == 0) {
598 continue;
601 if (strip->route()->is_master() || strip->route()->is_monitor()) {
602 continue;
605 (*i)[track_columns.visible] = yn;
608 no_track_list_redisplay = false;
609 redisplay_track_list ();
613 void
614 Mixer_UI::set_all_audio_visibility (int tracks, bool yn)
616 TreeModel::Children rows = track_model->children();
617 TreeModel::Children::iterator i;
619 no_track_list_redisplay = true;
621 for (i = rows.begin(); i != rows.end(); ++i) {
622 TreeModel::Row row = (*i);
623 MixerStrip* strip = row[track_columns.strip];
625 if (strip == 0) {
626 continue;
629 if (strip->route()->is_master() || strip->route()->is_monitor()) {
630 continue;
633 boost::shared_ptr<AudioTrack> at = strip->audio_track();
635 switch (tracks) {
636 case 0:
637 (*i)[track_columns.visible] = yn;
638 break;
640 case 1:
641 if (at) { /* track */
642 (*i)[track_columns.visible] = yn;
644 break;
646 case 2:
647 if (!at) { /* bus */
648 (*i)[track_columns.visible] = yn;
650 break;
654 no_track_list_redisplay = false;
655 redisplay_track_list ();
658 void
659 Mixer_UI::hide_all_routes ()
661 set_all_strips_visibility (false);
664 void
665 Mixer_UI::show_all_routes ()
667 set_all_strips_visibility (true);
670 void
671 Mixer_UI::show_all_audiobus ()
673 set_all_audio_visibility (2, true);
675 void
676 Mixer_UI::hide_all_audiobus ()
678 set_all_audio_visibility (2, false);
681 void
682 Mixer_UI::show_all_audiotracks()
684 set_all_audio_visibility (1, true);
686 void
687 Mixer_UI::hide_all_audiotracks ()
689 set_all_audio_visibility (1, false);
692 void
693 Mixer_UI::track_list_reorder (const TreeModel::Path&, const TreeModel::iterator&, int* /*new_order*/)
695 strip_redisplay_does_not_sync_order_keys = true;
696 _session->set_remote_control_ids();
697 redisplay_track_list ();
698 strip_redisplay_does_not_sync_order_keys = false;
701 void
702 Mixer_UI::track_list_change (const Gtk::TreeModel::Path&, const Gtk::TreeModel::iterator&)
704 // never reset order keys because of a property change
705 strip_redisplay_does_not_reset_order_keys = true;
706 _session->set_remote_control_ids();
707 redisplay_track_list ();
708 strip_redisplay_does_not_reset_order_keys = false;
711 void
712 Mixer_UI::track_list_delete (const Gtk::TreeModel::Path&)
714 /* this could require an order sync */
715 if (_session && !_session->deletion_in_progress()) {
716 _session->set_remote_control_ids();
717 redisplay_track_list ();
721 void
722 Mixer_UI::redisplay_track_list ()
724 TreeModel::Children rows = track_model->children();
725 TreeModel::Children::iterator i;
726 long order;
728 if (no_track_list_redisplay) {
729 return;
732 for (order = 0, i = rows.begin(); i != rows.end(); ++i, ++order) {
733 MixerStrip* strip = (*i)[track_columns.strip];
735 if (strip == 0) {
736 /* we're in the middle of changing a row, don't worry */
737 continue;
740 bool visible = (*i)[track_columns.visible];
742 if (visible) {
743 strip->set_marked_for_display (true);
744 strip->route()->set_order_key (N_("signal"), order);
746 if (!strip_redisplay_does_not_reset_order_keys) {
747 strip->route()->set_order_key (N_("signal"), order);
750 if (strip->packed()) {
752 if (strip->route()->is_master() || strip->route()->is_monitor()) {
753 out_packer.reorder_child (*strip, -1);
754 } else {
755 strip_packer.reorder_child (*strip, -1); /* put at end */
758 } else {
760 if (strip->route()->is_master() || strip->route()->is_monitor()) {
761 out_packer.pack_start (*strip, false, false);
762 } else {
763 strip_packer.pack_start (*strip, false, false);
765 strip->set_packed (true);
766 //strip->show();
769 } else {
771 strip->set_marked_for_display (false);
773 if (strip->route()->is_master() || strip->route()->is_monitor()) {
774 /* do nothing, these cannot be hidden */
775 } else {
776 if (strip->packed()) {
777 strip_packer.remove (*strip);
778 strip->set_packed (false);
784 if (!strip_redisplay_does_not_reset_order_keys && !strip_redisplay_does_not_sync_order_keys) {
785 _session->sync_order_keys (N_("signal"));
788 // Resigc::bind all of the midi controls automatically
790 if (auto_rebinding)
791 auto_rebind_midi_controls ();
793 _group_tabs->set_dirty ();
796 void
797 Mixer_UI::strip_width_changed ()
799 _group_tabs->set_dirty ();
801 #ifdef GTKOSX
802 TreeModel::Children rows = track_model->children();
803 TreeModel::Children::iterator i;
804 long order;
806 for (order = 0, i = rows.begin(); i != rows.end(); ++i, ++order) {
807 MixerStrip* strip = (*i)[track_columns.strip];
809 if (strip == 0) {
810 continue;
813 bool visible = (*i)[track_columns.visible];
815 if (visible) {
816 strip->queue_draw();
819 #endif
823 void
824 Mixer_UI::set_auto_rebinding( bool val )
826 if( val == TRUE )
828 auto_rebinding = TRUE;
829 Session::AutoBindingOff();
831 else
833 auto_rebinding = FALSE;
834 Session::AutoBindingOn();
838 void
839 Mixer_UI::toggle_auto_rebinding()
841 if (auto_rebinding)
843 set_auto_rebinding( FALSE );
846 else
848 set_auto_rebinding( TRUE );
851 auto_rebind_midi_controls();
854 void
855 Mixer_UI::auto_rebind_midi_controls ()
857 TreeModel::Children rows = track_model->children();
858 TreeModel::Children::iterator i;
859 int pos;
861 // Create bindings for all visible strips and remove those that are not visible
862 pos = 1; // 0 is reserved for the master strip
863 for (i = rows.begin(); i != rows.end(); ++i) {
864 MixerStrip* strip = (*i)[track_columns.strip];
866 if ( (*i)[track_columns.visible] == true ) { // add bindings for
867 // make the actual binding
868 //cout<<"Auto Binding: Visible Strip Found: "<<strip->name()<<endl;
870 int controlValue = pos;
871 if( strip->route()->is_master() ) {
872 controlValue = 0;
874 else {
875 pos++;
878 PBD::Controllable::CreateBinding ( strip->solo_button->get_controllable().get(), controlValue, 0);
879 PBD::Controllable::CreateBinding ( strip->mute_button->get_controllable().get(), controlValue, 1);
881 if( strip->is_audio_track() ) {
882 PBD::Controllable::CreateBinding ( strip->rec_enable_button->get_controllable().get(), controlValue, 2);
885 PBD::Controllable::CreateBinding ( strip->gpm.get_controllable().get(), controlValue, 3);
886 PBD::Controllable::CreateBinding ( strip->panners.get_controllable().get(), controlValue, 4);
889 else { // Remove any existing binding
890 PBD::Controllable::DeleteBinding ( strip->solo_button->get_controllable().get() );
891 PBD::Controllable::DeleteBinding ( strip->mute_button->get_controllable().get() );
893 if( strip->is_audio_track() ) {
894 PBD::Controllable::DeleteBinding ( strip->rec_enable_button->get_controllable().get() );
897 PBD::Controllable::DeleteBinding ( strip->gpm.get_controllable().get() );
898 PBD::Controllable::DeleteBinding ( strip->panners.get_controllable().get() ); // This only takes the first panner if there are multiples...
901 } // for
905 struct SignalOrderRouteSorter {
906 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
907 /* use of ">" forces the correct sort order */
908 return a->order_key (N_("signal")) < b->order_key (N_("signal"));
912 void
913 Mixer_UI::initial_track_display ()
915 boost::shared_ptr<RouteList> routes = _session->get_routes();
916 RouteList copy (*routes);
917 SignalOrderRouteSorter sorter;
919 copy.sort (sorter);
921 no_track_list_redisplay = true;
923 track_model->clear ();
925 add_strip (copy);
927 no_track_list_redisplay = false;
929 redisplay_track_list ();
932 void
933 Mixer_UI::show_track_list_menu ()
935 if (track_menu == 0) {
936 build_track_menu ();
939 track_menu->popup (1, gtk_get_current_event_time());
942 bool
943 Mixer_UI::track_display_button_press (GdkEventButton* ev)
945 if (Keyboard::is_context_menu_event (ev)) {
946 show_track_list_menu ();
947 return true;
950 TreeIter iter;
951 TreeModel::Path path;
952 TreeViewColumn* column;
953 int cellx;
954 int celly;
956 if (!track_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
957 return false;
960 switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
961 case 0:
962 /* allow normal processing to occur */
963 return false;
965 case 1: /* visibility */
967 if ((iter = track_model->get_iter (path))) {
968 MixerStrip* strip = (*iter)[track_columns.strip];
969 if (strip) {
971 if (!strip->route()->is_master() && !strip->route()->is_monitor()) {
972 bool visible = (*iter)[track_columns.visible];
973 (*iter)[track_columns.visible] = !visible;
975 #ifdef GTKOSX
976 track_display.queue_draw();
977 #endif
980 return true;
982 default:
983 break;
986 return false;
990 void
991 Mixer_UI::build_track_menu ()
993 using namespace Menu_Helpers;
994 using namespace Gtk;
996 track_menu = new Menu;
997 track_menu->set_name ("ArdourContextMenu");
998 MenuList& items = track_menu->items();
1000 items.push_back (MenuElem (_("Show All"), sigc::mem_fun(*this, &Mixer_UI::show_all_routes)));
1001 items.push_back (MenuElem (_("Hide All"), sigc::mem_fun(*this, &Mixer_UI::hide_all_routes)));
1002 items.push_back (MenuElem (_("Show All Audio Tracks"), sigc::mem_fun(*this, &Mixer_UI::show_all_audiotracks)));
1003 items.push_back (MenuElem (_("Hide All Audio Tracks"), sigc::mem_fun(*this, &Mixer_UI::hide_all_audiotracks)));
1004 items.push_back (MenuElem (_("Show All Audio Busses"), sigc::mem_fun(*this, &Mixer_UI::show_all_audiobus)));
1005 items.push_back (MenuElem (_("Hide All Audio Busses"), sigc::mem_fun(*this, &Mixer_UI::hide_all_audiobus)));
1009 void
1010 Mixer_UI::strip_property_changed (const PropertyChange& what_changed, MixerStrip* mx)
1012 if (!what_changed.contains (ARDOUR::Properties::name)) {
1013 return;
1016 ENSURE_GUI_THREAD (*this, &Mixer_UI::strip_name_changed, what_changed, mx)
1018 TreeModel::Children rows = track_model->children();
1019 TreeModel::Children::iterator i;
1021 for (i = rows.begin(); i != rows.end(); ++i) {
1022 if ((*i)[track_columns.strip] == mx) {
1023 (*i)[track_columns.text] = mx->route()->name();
1024 return;
1028 error << _("track display list item for renamed strip not found!") << endmsg;
1031 bool
1032 Mixer_UI::group_display_button_press (GdkEventButton* ev)
1034 TreeModel::Path path;
1035 TreeViewColumn* column;
1036 int cellx;
1037 int celly;
1039 if (!group_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
1040 return false;
1043 TreeIter iter = group_model->get_iter (path);
1044 if (!iter) {
1045 return false;
1048 RouteGroup* group = (*iter)[group_columns.group];
1050 if (Keyboard::is_context_menu_event (ev)) {
1051 _group_tabs->get_menu(group)->popup (1, ev->time);
1052 return true;
1055 switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
1056 case 0:
1057 if (Keyboard::is_edit_event (ev)) {
1058 if (group) {
1059 // edit_route_group (group);
1060 #ifdef GTKOSX
1061 group_display.queue_draw();
1062 #endif
1063 return true;
1066 break;
1068 case 1:
1070 bool visible = (*iter)[group_columns.visible];
1071 (*iter)[group_columns.visible] = !visible;
1072 #ifdef GTKOSX
1073 group_display.queue_draw();
1074 #endif
1075 return true;
1078 default:
1079 break;
1082 return false;
1085 void
1086 Mixer_UI::activate_all_route_groups ()
1088 _session->foreach_route_group (sigc::bind (sigc::mem_fun (*this, &Mixer_UI::set_route_group_activation), true));
1091 void
1092 Mixer_UI::disable_all_route_groups ()
1094 _session->foreach_route_group (sigc::bind (sigc::mem_fun (*this, &Mixer_UI::set_route_group_activation), false));
1097 void
1098 Mixer_UI::route_groups_changed ()
1100 ENSURE_GUI_THREAD (*this, &Mixer_UI::route_groups_changed)
1102 /* just rebuild the while thing */
1104 group_model->clear ();
1107 TreeModel::Row row;
1108 row = *(group_model->append());
1109 row[group_columns.visible] = true;
1110 row[group_columns.text] = (_("-all-"));
1111 row[group_columns.group] = 0;
1114 _session->foreach_route_group (sigc::mem_fun (*this, &Mixer_UI::add_route_group));
1116 _group_tabs->set_dirty ();
1119 void
1120 Mixer_UI::new_route_group ()
1122 RouteList rl;
1124 _group_tabs->run_new_group_dialog (rl);
1127 void
1128 Mixer_UI::remove_selected_route_group ()
1130 Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
1131 TreeView::Selection::ListHandle_Path rows = selection->get_selected_rows ();
1133 if (rows.empty()) {
1134 return;
1137 TreeView::Selection::ListHandle_Path::iterator i = rows.begin();
1138 TreeIter iter;
1140 /* selection mode is single, so rows.begin() is it */
1142 if ((iter = group_model->get_iter (*i))) {
1144 RouteGroup* rg = (*iter)[group_columns.group];
1146 if (rg) {
1147 _session->remove_route_group (*rg);
1152 void
1153 Mixer_UI::route_group_property_changed (RouteGroup* group, const PropertyChange& change)
1155 if (in_group_row_change) {
1156 return;
1159 /* force an update of any mixer strips that are using this group,
1160 otherwise mix group names don't change in mixer strips
1163 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1164 if ((*i)->route_group() == group) {
1165 (*i)->route_group_changed();
1169 TreeModel::iterator i;
1170 TreeModel::Children rows = group_model->children();
1171 Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
1173 in_group_row_change = true;
1175 for (i = rows.begin(); i != rows.end(); ++i) {
1176 if ((*i)[group_columns.group] == group) {
1177 (*i)[group_columns.visible] = !group->is_hidden ();
1178 (*i)[group_columns.text] = group->name ();
1179 break;
1183 in_group_row_change = false;
1185 if (change.contains (Properties::name)) {
1186 _group_tabs->set_dirty ();
1190 void
1191 Mixer_UI::route_group_name_edit (const std::string& path, const std::string& new_text)
1193 RouteGroup* group;
1194 TreeIter iter;
1196 if ((iter = group_model->get_iter (path))) {
1198 if ((group = (*iter)[group_columns.group]) == 0) {
1199 return;
1202 if (new_text != group->name()) {
1203 group->set_name (new_text);
1208 void
1209 Mixer_UI::route_group_row_change (const Gtk::TreeModel::Path&, const Gtk::TreeModel::iterator& iter)
1211 RouteGroup* group;
1213 if (in_group_row_change) {
1214 return;
1217 if ((group = (*iter)[group_columns.group]) == 0) {
1218 return;
1221 if ((*iter)[group_columns.visible]) {
1222 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1223 if ((*i)->route_group() == group) {
1224 show_strip (*i);
1227 } else {
1228 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1229 if ((*i)->route_group() == group) {
1230 hide_strip (*i);
1235 std::string name = (*iter)[group_columns.text];
1237 if (name != group->name()) {
1238 group->set_name (name);
1243 void
1244 Mixer_UI::add_route_group (RouteGroup* group)
1246 ENSURE_GUI_THREAD (*this, &Mixer_UI::add_route_group, group)
1247 bool focus = false;
1249 in_group_row_change = true;
1251 TreeModel::Row row = *(group_model->append());
1252 row[group_columns.visible] = true;
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->PropertyChanged.connect (*this, invalidator (*this), ui_bind (&Mixer_UI::route_group_property_changed, this, group, _1), gui_context());
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 _group_tabs->set_dirty ();
1271 in_group_row_change = false;
1274 bool
1275 Mixer_UI::strip_scroller_button_release (GdkEventButton* ev)
1277 using namespace Menu_Helpers;
1279 if (Keyboard::is_context_menu_event (ev)) {
1280 ARDOUR_UI::instance()->add_route (this);
1281 return true;
1284 return false;
1287 void
1288 Mixer_UI::set_strip_width (Width w)
1290 _strip_width = w;
1292 for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
1293 (*i)->set_width_enum (w, this);
1297 void
1298 Mixer_UI::set_window_pos_and_size ()
1300 resize (m_width, m_height);
1301 move (m_root_x, m_root_y);
1304 void
1305 Mixer_UI::get_window_pos_and_size ()
1307 get_position(m_root_x, m_root_y);
1308 get_size(m_width, m_height);
1312 Mixer_UI::set_state (const XMLNode& node)
1314 const XMLProperty* prop;
1315 XMLNode* geometry;
1317 m_width = default_width;
1318 m_height = default_height;
1319 m_root_x = 1;
1320 m_root_y = 1;
1322 if ((geometry = find_named_node (node, "geometry")) != 0) {
1324 XMLProperty* prop;
1326 if ((prop = geometry->property("x_size")) == 0) {
1327 prop = geometry->property ("x-size");
1329 if (prop) {
1330 m_width = atoi(prop->value());
1332 if ((prop = geometry->property("y_size")) == 0) {
1333 prop = geometry->property ("y-size");
1335 if (prop) {
1336 m_height = atoi(prop->value());
1339 if ((prop = geometry->property ("x_pos")) == 0) {
1340 prop = geometry->property ("x-pos");
1342 if (prop) {
1343 m_root_x = atoi (prop->value());
1346 if ((prop = geometry->property ("y_pos")) == 0) {
1347 prop = geometry->property ("y-pos");
1349 if (prop) {
1350 m_root_y = atoi (prop->value());
1354 set_window_pos_and_size ();
1356 if ((prop = node.property ("narrow-strips"))) {
1357 if (string_is_affirmative (prop->value())) {
1358 set_strip_width (Narrow);
1359 } else {
1360 set_strip_width (Wide);
1364 if ((prop = node.property ("show-mixer"))) {
1365 if (string_is_affirmative (prop->value())) {
1366 _visible = true;
1370 return 0;
1373 XMLNode&
1374 Mixer_UI::get_state (void)
1376 XMLNode* node = new XMLNode ("Mixer");
1378 if (is_realized()) {
1379 Glib::RefPtr<Gdk::Window> win = get_window();
1381 get_window_pos_and_size ();
1383 XMLNode* geometry = new XMLNode ("geometry");
1384 char buf[32];
1385 snprintf(buf, sizeof(buf), "%d", m_width);
1386 geometry->add_property(X_("x_size"), string(buf));
1387 snprintf(buf, sizeof(buf), "%d", m_height);
1388 geometry->add_property(X_("y_size"), string(buf));
1389 snprintf(buf, sizeof(buf), "%d", m_root_x);
1390 geometry->add_property(X_("x_pos"), string(buf));
1391 snprintf(buf, sizeof(buf), "%d", m_root_y);
1392 geometry->add_property(X_("y_pos"), string(buf));
1394 // written only for compatibility, they are not used.
1395 snprintf(buf, sizeof(buf), "%d", 0);
1396 geometry->add_property(X_("x_off"), string(buf));
1397 snprintf(buf, sizeof(buf), "%d", 0);
1398 geometry->add_property(X_("y_off"), string(buf));
1400 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&rhs_pane1)->gobj()));
1401 geometry->add_property(X_("mixer_rhs_pane1_pos"), string(buf));
1402 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&list_hpane)->gobj()));
1403 geometry->add_property(X_("mixer_list_hpane_pos"), string(buf));
1405 node->add_child_nocopy (*geometry);
1408 node->add_property ("narrow-strips", _strip_width == Narrow ? "yes" : "no");
1410 node->add_property ("show-mixer", _visible ? "yes" : "no");
1412 return *node;
1416 void
1417 Mixer_UI::pane_allocation_handler (Allocation&, Gtk::Paned* which)
1419 int pos;
1420 XMLProperty* prop = 0;
1421 char buf[32];
1422 XMLNode* node = ARDOUR_UI::instance()->mixer_settings();
1423 XMLNode* geometry;
1424 int width, height;
1425 static int32_t done[3] = { 0, 0, 0 };
1427 width = default_width;
1428 height = default_height;
1430 if ((geometry = find_named_node (*node, "geometry")) != 0) {
1433 if ((prop = geometry->property ("x_size")) == 0) {
1434 prop = geometry->property ("x-size");
1436 if (prop) {
1437 width = atoi (prop->value());
1439 if ((prop = geometry->property ("y_size")) == 0) {
1440 prop = geometry->property ("y-size");
1442 if (prop) {
1443 height = atoi (prop->value());
1447 if (which == static_cast<Gtk::Paned*> (&rhs_pane1)) {
1449 if (done[0]) {
1450 return;
1453 if (!geometry || (prop = geometry->property("mixer-rhs-pane1-pos")) == 0) {
1454 pos = height / 3;
1455 snprintf (buf, sizeof(buf), "%d", pos);
1456 } else {
1457 pos = atoi (prop->value());
1460 if ((done[0] = GTK_WIDGET(rhs_pane1.gobj())->allocation.height > pos)) {
1461 rhs_pane1.set_position (pos);
1464 } else if (which == static_cast<Gtk::Paned*> (&list_hpane)) {
1466 if (done[2]) {
1467 return;
1470 if (!geometry || (prop = geometry->property("mixer-list-hpane-pos")) == 0) {
1471 pos = 75;
1472 snprintf (buf, sizeof(buf), "%d", pos);
1473 } else {
1474 pos = atoi (prop->value());
1477 if ((done[2] = GTK_WIDGET(list_hpane.gobj())->allocation.width > pos)) {
1478 list_hpane.set_position (pos);
1482 void
1483 Mixer_UI::scroll_left ()
1485 Adjustment* adj = scroller.get_hscrollbar()->get_adjustment();
1486 /* stupid GTK: can't rely on clamping across versions */
1487 scroller.get_hscrollbar()->set_value (max (adj->get_lower(), adj->get_value() - adj->get_step_increment()));
1490 void
1491 Mixer_UI::scroll_right ()
1493 Adjustment* adj = scroller.get_hscrollbar()->get_adjustment();
1494 /* stupid GTK: can't rely on clamping across versions */
1495 scroller.get_hscrollbar()->set_value (min (adj->get_upper(), adj->get_value() + adj->get_step_increment()));
1498 bool
1499 Mixer_UI::on_key_press_event (GdkEventKey* ev)
1501 switch (ev->keyval) {
1502 case GDK_Left:
1503 scroll_left ();
1504 return true;
1506 case GDK_Right:
1507 scroll_right ();
1508 return true;
1510 default:
1511 break;
1514 return key_press_focus_accelerator_handler (*this, ev);
1517 bool
1518 Mixer_UI::on_key_release_event (GdkEventKey* ev)
1520 return Gtk::Window::on_key_release_event (ev);
1521 // return key_press_focus_accelerator_handler (*this, ev);
1525 bool
1526 Mixer_UI::on_scroll_event (GdkEventScroll* ev)
1528 switch (ev->direction) {
1529 case GDK_SCROLL_LEFT:
1530 scroll_left ();
1531 return true;
1532 case GDK_SCROLL_UP:
1533 if (ev->state & Keyboard::TertiaryModifier) {
1534 scroll_left ();
1535 return true;
1537 return false;
1539 case GDK_SCROLL_RIGHT:
1540 scroll_right ();
1541 return true;
1543 case GDK_SCROLL_DOWN:
1544 if (ev->state & Keyboard::TertiaryModifier) {
1545 scroll_right ();
1546 return true;
1548 return false;
1551 return false;
1555 void
1556 Mixer_UI::parameter_changed (string const & p)
1558 if (p == "show-group-tabs") {
1559 bool const s = _session->config.get_show_group_tabs ();
1560 if (s) {
1561 _group_tabs->show ();
1562 } else {
1563 _group_tabs->hide ();
1565 } else if (p == "default-narrow_ms") {
1566 bool const s = Config->get_default_narrow_ms ();
1567 for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
1568 (*i)->set_width_enum (s ? Narrow : Wide, this);
1573 void
1574 Mixer_UI::set_route_group_activation (RouteGroup* g, bool a)
1576 g->set_active (a, this);
1579 PluginSelector*
1580 Mixer_UI::plugin_selector()
1582 #ifdef DEFER_PLUGIN_SELECTOR_LOAD
1583 if (!_plugin_selector)
1584 _plugin_selector = new PluginSelector (PluginManager::the_manager ());
1585 #endif
1587 return _plugin_selector;
1590 void
1591 Mixer_UI::setup_track_display ()
1593 track_model = ListStore::create (track_columns);
1594 track_display.set_model (track_model);
1595 track_display.append_column (_("Strips"), track_columns.text);
1596 track_display.append_column (_("Show"), track_columns.visible);
1597 track_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0));
1598 track_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
1599 track_display.get_column (0)->set_expand(true);
1600 track_display.get_column (1)->set_expand(false);
1601 track_display.set_name (X_("MixerTrackDisplayList"));
1602 track_display.get_selection()->set_mode (Gtk::SELECTION_NONE);
1603 track_display.set_reorderable (true);
1604 track_display.set_headers_visible (true);
1606 track_model->signal_row_deleted().connect (sigc::mem_fun (*this, &Mixer_UI::track_list_delete));
1607 track_model->signal_row_changed().connect (sigc::mem_fun (*this, &Mixer_UI::track_list_change));
1608 track_model->signal_rows_reordered().connect (sigc::mem_fun (*this, &Mixer_UI::track_list_reorder));
1610 CellRendererToggle* track_list_visible_cell = dynamic_cast<CellRendererToggle*>(track_display.get_column_cell_renderer (1));
1611 track_list_visible_cell->property_activatable() = true;
1612 track_list_visible_cell->property_radio() = false;
1614 track_display.signal_button_press_event().connect (sigc::mem_fun (*this, &Mixer_UI::track_display_button_press), false);
1616 track_display_scroller.add (track_display);
1617 track_display_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
1619 VBox* v = manage (new VBox);
1620 v->show ();
1621 v->pack_start (track_display_scroller, true, true);
1623 Button* b = manage (new Button);
1624 b->show ();
1625 Widget* w = manage (new Image (Stock::ADD, ICON_SIZE_BUTTON));
1626 w->show ();
1627 b->add (*w);
1629 b->signal_clicked().connect (sigc::mem_fun (*this, &Mixer_UI::new_track_or_bus));
1631 v->pack_start (*b, false, false);
1633 track_display_frame.set_name("BaseFrame");
1634 track_display_frame.set_shadow_type (Gtk::SHADOW_IN);
1635 track_display_frame.add (*v);
1637 track_display_scroller.show();
1638 track_display_frame.show();
1639 track_display.show();
1642 void
1643 Mixer_UI::new_track_or_bus ()
1645 ARDOUR_UI::instance()->add_route (this);