another potential fix for partial stripping of a linux bundle
[ardour2.git] / gtk2_ardour / mixer_ui.cc
blobdc3aced9787d52bd0779f86ce34c97318a3e88bf
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 /* show/hide group tabs as required */
254 parameter_changed ("show-group-tabs");
256 /* now reset each strips width so the right widgets are shown */
257 MixerStrip* ms;
259 TreeModel::Children rows = track_model->children();
260 TreeModel::Children::iterator ri;
262 for (ri = rows.begin(); ri != rows.end(); ++ri) {
263 ms = (*ri)[track_columns.strip];
264 ms->set_width_enum (ms->get_width_enum (), ms->width_owner());
267 _visible = true;
270 bool
271 Mixer_UI::hide_window (GdkEventAny *ev)
273 get_window_pos_and_size ();
275 _visible = false;
276 return just_hide_it(ev, static_cast<Gtk::Window *>(this));
280 void
281 Mixer_UI::add_strip (RouteList& routes)
283 ENSURE_GUI_THREAD (*this, &Mixer_UI::add_strip, routes)
285 MixerStrip* strip;
287 no_track_list_redisplay = true;
288 strip_redisplay_does_not_sync_order_keys = true;
290 for (RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
291 boost::shared_ptr<Route> route = (*x);
293 if (route->is_hidden()) {
294 continue;
297 if (route->is_monitor()) {
298 if (!_monitor_section) {
299 _monitor_section = new MonitorSection (_session);
300 out_packer.pack_end (_monitor_section->tearoff(), false, false);
301 } else {
302 _monitor_section->set_session (_session);
305 _monitor_section->tearoff().show_all ();
307 XMLNode* mnode = ARDOUR_UI::instance()->tearoff_settings (X_("monitor-section"));
308 if (mnode) {
309 _monitor_section->tearoff().set_state (*mnode);
312 /* no regular strip shown for control out */
314 continue;
317 strip = new MixerStrip (*this, _session, route);
318 strips.push_back (strip);
320 Config->get_default_narrow_ms() ? _strip_width = Narrow : _strip_width = Wide;
322 if (strip->width_owner() != strip) {
323 strip->set_width_enum (_strip_width, this);
326 show_strip (strip);
328 TreeModel::Row row = *(track_model->append());
329 row[track_columns.text] = route->name();
330 row[track_columns.visible] = strip->marked_for_display();
331 row[track_columns.route] = route;
332 row[track_columns.strip] = strip;
334 if (route->order_key (N_("signal")) == -1) {
335 route->set_order_key (N_("signal"), track_model->children().size()-1);
338 route->PropertyChanged.connect (*this, invalidator (*this), ui_bind (&Mixer_UI::strip_property_changed, this, _1, strip), gui_context());
340 strip->WidthChanged.connect (sigc::mem_fun(*this, &Mixer_UI::strip_width_changed));
341 strip->signal_button_release_event().connect (sigc::bind (sigc::mem_fun(*this, &Mixer_UI::strip_button_release_event), strip));
344 no_track_list_redisplay = false;
346 redisplay_track_list ();
348 strip_redisplay_does_not_sync_order_keys = false;
351 void
352 Mixer_UI::remove_strip (MixerStrip* strip)
354 if (_session && _session->deletion_in_progress()) {
355 /* its all being taken care of */
356 return;
359 ENSURE_GUI_THREAD (*this, &Mixer_UI::remove_strip, strip);
361 cerr << "Mixer UI removing strip for " << strip << endl;
363 TreeModel::Children rows = track_model->children();
364 TreeModel::Children::iterator ri;
365 list<MixerStrip *>::iterator i;
367 if ((i = find (strips.begin(), strips.end(), strip)) != strips.end()) {
368 strips.erase (i);
371 strip_redisplay_does_not_sync_order_keys = true;
373 for (ri = rows.begin(); ri != rows.end(); ++ri) {
374 if ((*ri)[track_columns.strip] == strip) {
375 track_model->erase (ri);
376 break;
380 strip_redisplay_does_not_sync_order_keys = false;
383 void
384 Mixer_UI::sync_order_keys (string const & src)
386 TreeModel::Children rows = track_model->children();
387 TreeModel::Children::iterator ri;
389 if (src == N_("signal") || !_session || (_session->state_of_the_state() & (Session::Loading|Session::Deletion)) || rows.empty()) {
390 return;
393 std::map<int,int> keys;
395 bool changed = false;
397 unsigned order = 0;
398 for (ri = rows.begin(); ri != rows.end(); ++ri, ++order) {
399 boost::shared_ptr<Route> route = (*ri)[track_columns.route];
400 unsigned int old_key = order;
401 unsigned int new_key = route->order_key (N_("signal"));
403 keys[new_key] = old_key;
405 if (new_key != old_key) {
406 changed = true;
410 if (keys.size() != rows.size()) {
411 PBD::stacktrace (cerr, 20);
413 assert(keys.size() == rows.size());
415 // Remove any gaps in keys caused by automation children tracks
416 vector<int> neworder;
417 for (std::map<int,int>::const_iterator i = keys.begin(); i != keys.end(); ++i) {
418 neworder.push_back(i->second);
420 assert(neworder.size() == rows.size());
422 if (changed) {
423 strip_redisplay_does_not_reset_order_keys = true;
424 track_model->reorder (neworder);
425 strip_redisplay_does_not_reset_order_keys = false;
429 void
430 Mixer_UI::follow_strip_selection ()
432 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
433 (*i)->set_selected (_selection.selected ((*i)->route()));
437 bool
438 Mixer_UI::strip_button_release_event (GdkEventButton *ev, MixerStrip *strip)
440 if (ev->button == 1) {
442 /* this allows the user to click on the strip to terminate comment
443 editing. XXX it needs improving so that we don't select the strip
444 at the same time.
447 if (_selection.selected (strip->route())) {
448 _selection.remove (strip->route());
449 } else {
450 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
451 _selection.add (strip->route());
452 } else {
453 _selection.set (strip->route());
458 return true;
461 void
462 Mixer_UI::set_session (Session* sess)
464 SessionHandlePtr::set_session (sess);
466 if (_plugin_selector) {
467 _plugin_selector->set_session (_session);
470 _group_tabs->set_session (sess);
472 if (!_session) {
473 return;
476 XMLNode* node = ARDOUR_UI::instance()->mixer_settings();
477 set_state (*node);
479 WindowTitle title(_session->name());
480 title += _("Mixer");
481 title += Glib::get_application_name();
483 set_title (title.get_string());
485 initial_track_display ();
487 _session->RouteAdded.connect (_session_connections, invalidator (*this), ui_bind (&Mixer_UI::add_strip, this, _1), gui_context());
488 _session->route_group_added.connect (_session_connections, invalidator (*this), ui_bind (&Mixer_UI::add_route_group, this, _1), gui_context());
489 _session->route_group_removed.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::route_groups_changed, this), gui_context());
490 _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), ui_bind (&Mixer_UI::parameter_changed, this, _1), gui_context());
492 Config->ParameterChanged.connect (*this, invalidator (*this), ui_bind (&Mixer_UI::parameter_changed, this, _1), gui_context ());
494 route_groups_changed ();
496 if (_visible) {
497 show_window();
500 start_updating ();
503 void
504 Mixer_UI::session_going_away ()
506 ENSURE_GUI_THREAD (*this, &Mixer_UI::session_going_away)
508 group_model->clear ();
509 _selection.clear ();
510 track_model->clear ();
512 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
513 delete (*i);
516 if (_monitor_section) {
517 _monitor_section->tearoff().hide_visible ();
520 strips.clear ();
522 WindowTitle title(Glib::get_application_name());
523 title += _("Mixer");
524 set_title (title.get_string());
526 stop_updating ();
528 SessionHandlePtr::session_going_away ();
531 void
532 Mixer_UI::show_strip (MixerStrip* ms)
534 TreeModel::Children rows = track_model->children();
535 TreeModel::Children::iterator i;
537 for (i = rows.begin(); i != rows.end(); ++i) {
539 MixerStrip* strip = (*i)[track_columns.strip];
540 if (strip == ms) {
541 (*i)[track_columns.visible] = true;
542 break;
547 void
548 Mixer_UI::hide_strip (MixerStrip* ms)
550 TreeModel::Children rows = track_model->children();
551 TreeModel::Children::iterator i;
553 for (i = rows.begin(); i != rows.end(); ++i) {
555 MixerStrip* strip = (*i)[track_columns.strip];
556 if (strip == ms) {
557 (*i)[track_columns.visible] = false;
558 break;
563 gint
564 Mixer_UI::start_updating ()
566 fast_screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (sigc::mem_fun(*this, &Mixer_UI::fast_update_strips));
567 return 0;
570 gint
571 Mixer_UI::stop_updating ()
573 fast_screen_update_connection.disconnect();
574 return 0;
577 void
578 Mixer_UI::fast_update_strips ()
580 if (is_mapped () && _session) {
581 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
582 (*i)->fast_update ();
587 void
588 Mixer_UI::set_all_strips_visibility (bool yn)
590 TreeModel::Children rows = track_model->children();
591 TreeModel::Children::iterator i;
593 no_track_list_redisplay = true;
595 for (i = rows.begin(); i != rows.end(); ++i) {
597 TreeModel::Row row = (*i);
598 MixerStrip* strip = row[track_columns.strip];
600 if (strip == 0) {
601 continue;
604 if (strip->route()->is_master() || strip->route()->is_monitor()) {
605 continue;
608 (*i)[track_columns.visible] = yn;
611 no_track_list_redisplay = false;
612 redisplay_track_list ();
616 void
617 Mixer_UI::set_all_audio_visibility (int tracks, bool yn)
619 TreeModel::Children rows = track_model->children();
620 TreeModel::Children::iterator i;
622 no_track_list_redisplay = true;
624 for (i = rows.begin(); i != rows.end(); ++i) {
625 TreeModel::Row row = (*i);
626 MixerStrip* strip = row[track_columns.strip];
628 if (strip == 0) {
629 continue;
632 if (strip->route()->is_master() || strip->route()->is_monitor()) {
633 continue;
636 boost::shared_ptr<AudioTrack> at = strip->audio_track();
638 switch (tracks) {
639 case 0:
640 (*i)[track_columns.visible] = yn;
641 break;
643 case 1:
644 if (at) { /* track */
645 (*i)[track_columns.visible] = yn;
647 break;
649 case 2:
650 if (!at) { /* bus */
651 (*i)[track_columns.visible] = yn;
653 break;
657 no_track_list_redisplay = false;
658 redisplay_track_list ();
661 void
662 Mixer_UI::hide_all_routes ()
664 set_all_strips_visibility (false);
667 void
668 Mixer_UI::show_all_routes ()
670 set_all_strips_visibility (true);
673 void
674 Mixer_UI::show_all_audiobus ()
676 set_all_audio_visibility (2, true);
678 void
679 Mixer_UI::hide_all_audiobus ()
681 set_all_audio_visibility (2, false);
684 void
685 Mixer_UI::show_all_audiotracks()
687 set_all_audio_visibility (1, true);
689 void
690 Mixer_UI::hide_all_audiotracks ()
692 set_all_audio_visibility (1, false);
695 void
696 Mixer_UI::track_list_reorder (const TreeModel::Path&, const TreeModel::iterator&, int* /*new_order*/)
698 strip_redisplay_does_not_sync_order_keys = true;
699 _session->set_remote_control_ids();
700 redisplay_track_list ();
701 strip_redisplay_does_not_sync_order_keys = false;
704 void
705 Mixer_UI::track_list_change (const Gtk::TreeModel::Path&, const Gtk::TreeModel::iterator&)
707 // never reset order keys because of a property change
708 strip_redisplay_does_not_reset_order_keys = true;
709 _session->set_remote_control_ids();
710 redisplay_track_list ();
711 strip_redisplay_does_not_reset_order_keys = false;
714 void
715 Mixer_UI::track_list_delete (const Gtk::TreeModel::Path&)
717 /* this could require an order sync */
718 if (_session && !_session->deletion_in_progress()) {
719 _session->set_remote_control_ids();
720 redisplay_track_list ();
724 void
725 Mixer_UI::redisplay_track_list ()
727 TreeModel::Children rows = track_model->children();
728 TreeModel::Children::iterator i;
729 long order;
731 if (no_track_list_redisplay) {
732 return;
735 for (order = 0, i = rows.begin(); i != rows.end(); ++i, ++order) {
736 MixerStrip* strip = (*i)[track_columns.strip];
738 if (strip == 0) {
739 /* we're in the middle of changing a row, don't worry */
740 continue;
743 bool visible = (*i)[track_columns.visible];
745 if (visible) {
746 strip->set_marked_for_display (true);
747 strip->route()->set_order_key (N_("signal"), order);
749 if (!strip_redisplay_does_not_reset_order_keys) {
750 strip->route()->set_order_key (N_("signal"), order);
753 if (strip->packed()) {
755 if (strip->route()->is_master() || strip->route()->is_monitor()) {
756 out_packer.reorder_child (*strip, -1);
757 } else {
758 strip_packer.reorder_child (*strip, -1); /* put at end */
761 } else {
763 if (strip->route()->is_master() || strip->route()->is_monitor()) {
764 out_packer.pack_start (*strip, false, false);
765 } else {
766 strip_packer.pack_start (*strip, false, false);
768 strip->set_packed (true);
769 //strip->show();
772 } else {
774 strip->set_marked_for_display (false);
776 if (strip->route()->is_master() || strip->route()->is_monitor()) {
777 /* do nothing, these cannot be hidden */
778 } else {
779 if (strip->packed()) {
780 strip_packer.remove (*strip);
781 strip->set_packed (false);
787 if (!strip_redisplay_does_not_reset_order_keys && !strip_redisplay_does_not_sync_order_keys) {
788 _session->sync_order_keys (N_("signal"));
791 // Resigc::bind all of the midi controls automatically
793 if (auto_rebinding)
794 auto_rebind_midi_controls ();
796 _group_tabs->set_dirty ();
799 void
800 Mixer_UI::strip_width_changed ()
802 _group_tabs->set_dirty ();
804 #ifdef GTKOSX
805 TreeModel::Children rows = track_model->children();
806 TreeModel::Children::iterator i;
807 long order;
809 for (order = 0, i = rows.begin(); i != rows.end(); ++i, ++order) {
810 MixerStrip* strip = (*i)[track_columns.strip];
812 if (strip == 0) {
813 continue;
816 bool visible = (*i)[track_columns.visible];
818 if (visible) {
819 strip->queue_draw();
822 #endif
826 void
827 Mixer_UI::set_auto_rebinding( bool val )
829 if( val == TRUE )
831 auto_rebinding = TRUE;
832 Session::AutoBindingOff();
834 else
836 auto_rebinding = FALSE;
837 Session::AutoBindingOn();
841 void
842 Mixer_UI::toggle_auto_rebinding()
844 if (auto_rebinding)
846 set_auto_rebinding( FALSE );
849 else
851 set_auto_rebinding( TRUE );
854 auto_rebind_midi_controls();
857 void
858 Mixer_UI::auto_rebind_midi_controls ()
860 TreeModel::Children rows = track_model->children();
861 TreeModel::Children::iterator i;
862 int pos;
864 // Create bindings for all visible strips and remove those that are not visible
865 pos = 1; // 0 is reserved for the master strip
866 for (i = rows.begin(); i != rows.end(); ++i) {
867 MixerStrip* strip = (*i)[track_columns.strip];
869 if ( (*i)[track_columns.visible] == true ) { // add bindings for
870 // make the actual binding
871 //cout<<"Auto Binding: Visible Strip Found: "<<strip->name()<<endl;
873 int controlValue = pos;
874 if( strip->route()->is_master() ) {
875 controlValue = 0;
877 else {
878 pos++;
881 PBD::Controllable::CreateBinding ( strip->solo_button->get_controllable().get(), controlValue, 0);
882 PBD::Controllable::CreateBinding ( strip->mute_button->get_controllable().get(), controlValue, 1);
884 if( strip->is_audio_track() ) {
885 PBD::Controllable::CreateBinding ( strip->rec_enable_button->get_controllable().get(), controlValue, 2);
888 PBD::Controllable::CreateBinding ( strip->gpm.get_controllable().get(), controlValue, 3);
889 PBD::Controllable::CreateBinding ( strip->panners.get_controllable().get(), controlValue, 4);
892 else { // Remove any existing binding
893 PBD::Controllable::DeleteBinding ( strip->solo_button->get_controllable().get() );
894 PBD::Controllable::DeleteBinding ( strip->mute_button->get_controllable().get() );
896 if( strip->is_audio_track() ) {
897 PBD::Controllable::DeleteBinding ( strip->rec_enable_button->get_controllable().get() );
900 PBD::Controllable::DeleteBinding ( strip->gpm.get_controllable().get() );
901 PBD::Controllable::DeleteBinding ( strip->panners.get_controllable().get() ); // This only takes the first panner if there are multiples...
904 } // for
908 struct SignalOrderRouteSorter {
909 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
910 /* use of ">" forces the correct sort order */
911 return a->order_key (N_("signal")) < b->order_key (N_("signal"));
915 void
916 Mixer_UI::initial_track_display ()
918 boost::shared_ptr<RouteList> routes = _session->get_routes();
919 RouteList copy (*routes);
920 SignalOrderRouteSorter sorter;
922 copy.sort (sorter);
924 no_track_list_redisplay = true;
926 track_model->clear ();
928 add_strip (copy);
930 no_track_list_redisplay = false;
932 redisplay_track_list ();
935 void
936 Mixer_UI::show_track_list_menu ()
938 if (track_menu == 0) {
939 build_track_menu ();
942 track_menu->popup (1, gtk_get_current_event_time());
945 bool
946 Mixer_UI::track_display_button_press (GdkEventButton* ev)
948 if (Keyboard::is_context_menu_event (ev)) {
949 show_track_list_menu ();
950 return true;
953 TreeIter iter;
954 TreeModel::Path path;
955 TreeViewColumn* column;
956 int cellx;
957 int celly;
959 if (!track_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
960 return false;
963 switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
964 case 0:
965 /* allow normal processing to occur */
966 return false;
968 case 1: /* visibility */
970 if ((iter = track_model->get_iter (path))) {
971 MixerStrip* strip = (*iter)[track_columns.strip];
972 if (strip) {
974 if (!strip->route()->is_master() && !strip->route()->is_monitor()) {
975 bool visible = (*iter)[track_columns.visible];
976 (*iter)[track_columns.visible] = !visible;
978 #ifdef GTKOSX
979 track_display.queue_draw();
980 #endif
983 return true;
985 default:
986 break;
989 return false;
993 void
994 Mixer_UI::build_track_menu ()
996 using namespace Menu_Helpers;
997 using namespace Gtk;
999 track_menu = new Menu;
1000 track_menu->set_name ("ArdourContextMenu");
1001 MenuList& items = track_menu->items();
1003 items.push_back (MenuElem (_("Show All"), sigc::mem_fun(*this, &Mixer_UI::show_all_routes)));
1004 items.push_back (MenuElem (_("Hide All"), sigc::mem_fun(*this, &Mixer_UI::hide_all_routes)));
1005 items.push_back (MenuElem (_("Show All Audio Tracks"), sigc::mem_fun(*this, &Mixer_UI::show_all_audiotracks)));
1006 items.push_back (MenuElem (_("Hide All Audio Tracks"), sigc::mem_fun(*this, &Mixer_UI::hide_all_audiotracks)));
1007 items.push_back (MenuElem (_("Show All Audio Busses"), sigc::mem_fun(*this, &Mixer_UI::show_all_audiobus)));
1008 items.push_back (MenuElem (_("Hide All Audio Busses"), sigc::mem_fun(*this, &Mixer_UI::hide_all_audiobus)));
1012 void
1013 Mixer_UI::strip_property_changed (const PropertyChange& what_changed, MixerStrip* mx)
1015 if (!what_changed.contains (ARDOUR::Properties::name)) {
1016 return;
1019 ENSURE_GUI_THREAD (*this, &Mixer_UI::strip_name_changed, what_changed, mx)
1021 TreeModel::Children rows = track_model->children();
1022 TreeModel::Children::iterator i;
1024 for (i = rows.begin(); i != rows.end(); ++i) {
1025 if ((*i)[track_columns.strip] == mx) {
1026 (*i)[track_columns.text] = mx->route()->name();
1027 return;
1031 error << _("track display list item for renamed strip not found!") << endmsg;
1034 bool
1035 Mixer_UI::group_display_button_press (GdkEventButton* ev)
1037 TreeModel::Path path;
1038 TreeViewColumn* column;
1039 int cellx;
1040 int celly;
1042 if (!group_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
1043 return false;
1046 TreeIter iter = group_model->get_iter (path);
1047 if (!iter) {
1048 return false;
1051 RouteGroup* group = (*iter)[group_columns.group];
1053 if (Keyboard::is_context_menu_event (ev)) {
1054 _group_tabs->get_menu(group)->popup (1, ev->time);
1055 return true;
1058 switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
1059 case 0:
1060 if (Keyboard::is_edit_event (ev)) {
1061 if (group) {
1062 // edit_route_group (group);
1063 #ifdef GTKOSX
1064 group_display.queue_draw();
1065 #endif
1066 return true;
1069 break;
1071 case 1:
1073 bool visible = (*iter)[group_columns.visible];
1074 (*iter)[group_columns.visible] = !visible;
1075 #ifdef GTKOSX
1076 group_display.queue_draw();
1077 #endif
1078 return true;
1081 default:
1082 break;
1085 return false;
1088 void
1089 Mixer_UI::activate_all_route_groups ()
1091 _session->foreach_route_group (sigc::bind (sigc::mem_fun (*this, &Mixer_UI::set_route_group_activation), true));
1094 void
1095 Mixer_UI::disable_all_route_groups ()
1097 _session->foreach_route_group (sigc::bind (sigc::mem_fun (*this, &Mixer_UI::set_route_group_activation), false));
1100 void
1101 Mixer_UI::route_groups_changed ()
1103 ENSURE_GUI_THREAD (*this, &Mixer_UI::route_groups_changed)
1105 /* just rebuild the while thing */
1107 group_model->clear ();
1110 TreeModel::Row row;
1111 row = *(group_model->append());
1112 row[group_columns.visible] = true;
1113 row[group_columns.text] = (_("-all-"));
1114 row[group_columns.group] = 0;
1117 _session->foreach_route_group (sigc::mem_fun (*this, &Mixer_UI::add_route_group));
1119 _group_tabs->set_dirty ();
1122 void
1123 Mixer_UI::new_route_group ()
1125 RouteList rl;
1127 _group_tabs->run_new_group_dialog (rl);
1130 void
1131 Mixer_UI::remove_selected_route_group ()
1133 Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
1134 TreeView::Selection::ListHandle_Path rows = selection->get_selected_rows ();
1136 if (rows.empty()) {
1137 return;
1140 TreeView::Selection::ListHandle_Path::iterator i = rows.begin();
1141 TreeIter iter;
1143 /* selection mode is single, so rows.begin() is it */
1145 if ((iter = group_model->get_iter (*i))) {
1147 RouteGroup* rg = (*iter)[group_columns.group];
1149 if (rg) {
1150 _session->remove_route_group (*rg);
1155 void
1156 Mixer_UI::route_group_property_changed (RouteGroup* group, const PropertyChange& change)
1158 if (in_group_row_change) {
1159 return;
1162 /* force an update of any mixer strips that are using this group,
1163 otherwise mix group names don't change in mixer strips
1166 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1167 if ((*i)->route_group() == group) {
1168 (*i)->route_group_changed();
1172 TreeModel::iterator i;
1173 TreeModel::Children rows = group_model->children();
1174 Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
1176 in_group_row_change = true;
1178 for (i = rows.begin(); i != rows.end(); ++i) {
1179 if ((*i)[group_columns.group] == group) {
1180 (*i)[group_columns.visible] = !group->is_hidden ();
1181 (*i)[group_columns.text] = group->name ();
1182 break;
1186 in_group_row_change = false;
1188 if (change.contains (Properties::name)) {
1189 _group_tabs->set_dirty ();
1193 void
1194 Mixer_UI::route_group_name_edit (const std::string& path, const std::string& new_text)
1196 RouteGroup* group;
1197 TreeIter iter;
1199 if ((iter = group_model->get_iter (path))) {
1201 if ((group = (*iter)[group_columns.group]) == 0) {
1202 return;
1205 if (new_text != group->name()) {
1206 group->set_name (new_text);
1211 void
1212 Mixer_UI::route_group_row_change (const Gtk::TreeModel::Path&, const Gtk::TreeModel::iterator& iter)
1214 RouteGroup* group;
1216 if (in_group_row_change) {
1217 return;
1220 if ((group = (*iter)[group_columns.group]) == 0) {
1221 return;
1224 if ((*iter)[group_columns.visible]) {
1225 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1226 if ((*i)->route_group() == group) {
1227 show_strip (*i);
1230 } else {
1231 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1232 if ((*i)->route_group() == group) {
1233 hide_strip (*i);
1238 std::string name = (*iter)[group_columns.text];
1240 if (name != group->name()) {
1241 group->set_name (name);
1246 void
1247 Mixer_UI::add_route_group (RouteGroup* group)
1249 ENSURE_GUI_THREAD (*this, &Mixer_UI::add_route_group, group)
1250 bool focus = false;
1252 in_group_row_change = true;
1254 TreeModel::Row row = *(group_model->append());
1255 row[group_columns.visible] = true;
1256 row[group_columns.group] = group;
1257 if (!group->name().empty()) {
1258 row[group_columns.text] = group->name();
1259 } else {
1260 row[group_columns.text] = _("unnamed");
1261 focus = true;
1264 group->PropertyChanged.connect (*this, invalidator (*this), ui_bind (&Mixer_UI::route_group_property_changed, this, group, _1), gui_context());
1266 if (focus) {
1267 TreeViewColumn* col = group_display.get_column (0);
1268 CellRendererText* name_cell = dynamic_cast<CellRendererText*>(group_display.get_column_cell_renderer (0));
1269 group_display.set_cursor (group_model->get_path (row), *col, *name_cell, true);
1272 _group_tabs->set_dirty ();
1274 in_group_row_change = false;
1277 bool
1278 Mixer_UI::strip_scroller_button_release (GdkEventButton* ev)
1280 using namespace Menu_Helpers;
1282 if (Keyboard::is_context_menu_event (ev)) {
1283 ARDOUR_UI::instance()->add_route (this);
1284 return true;
1287 return false;
1290 void
1291 Mixer_UI::set_strip_width (Width w)
1293 _strip_width = w;
1295 for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
1296 (*i)->set_width_enum (w, this);
1300 void
1301 Mixer_UI::set_window_pos_and_size ()
1303 resize (m_width, m_height);
1304 move (m_root_x, m_root_y);
1307 void
1308 Mixer_UI::get_window_pos_and_size ()
1310 get_position(m_root_x, m_root_y);
1311 get_size(m_width, m_height);
1315 Mixer_UI::set_state (const XMLNode& node)
1317 const XMLProperty* prop;
1318 XMLNode* geometry;
1320 m_width = default_width;
1321 m_height = default_height;
1322 m_root_x = 1;
1323 m_root_y = 1;
1325 if ((geometry = find_named_node (node, "geometry")) != 0) {
1327 XMLProperty* prop;
1329 if ((prop = geometry->property("x_size")) == 0) {
1330 prop = geometry->property ("x-size");
1332 if (prop) {
1333 m_width = atoi(prop->value());
1335 if ((prop = geometry->property("y_size")) == 0) {
1336 prop = geometry->property ("y-size");
1338 if (prop) {
1339 m_height = atoi(prop->value());
1342 if ((prop = geometry->property ("x_pos")) == 0) {
1343 prop = geometry->property ("x-pos");
1345 if (prop) {
1346 m_root_x = atoi (prop->value());
1349 if ((prop = geometry->property ("y_pos")) == 0) {
1350 prop = geometry->property ("y-pos");
1352 if (prop) {
1353 m_root_y = atoi (prop->value());
1357 set_window_pos_and_size ();
1359 if ((prop = node.property ("narrow-strips"))) {
1360 if (string_is_affirmative (prop->value())) {
1361 set_strip_width (Narrow);
1362 } else {
1363 set_strip_width (Wide);
1367 if ((prop = node.property ("show-mixer"))) {
1368 if (string_is_affirmative (prop->value())) {
1369 _visible = true;
1373 return 0;
1376 XMLNode&
1377 Mixer_UI::get_state (void)
1379 XMLNode* node = new XMLNode ("Mixer");
1381 if (is_realized()) {
1382 Glib::RefPtr<Gdk::Window> win = get_window();
1384 get_window_pos_and_size ();
1386 XMLNode* geometry = new XMLNode ("geometry");
1387 char buf[32];
1388 snprintf(buf, sizeof(buf), "%d", m_width);
1389 geometry->add_property(X_("x_size"), string(buf));
1390 snprintf(buf, sizeof(buf), "%d", m_height);
1391 geometry->add_property(X_("y_size"), string(buf));
1392 snprintf(buf, sizeof(buf), "%d", m_root_x);
1393 geometry->add_property(X_("x_pos"), string(buf));
1394 snprintf(buf, sizeof(buf), "%d", m_root_y);
1395 geometry->add_property(X_("y_pos"), string(buf));
1397 // written only for compatibility, they are not used.
1398 snprintf(buf, sizeof(buf), "%d", 0);
1399 geometry->add_property(X_("x_off"), string(buf));
1400 snprintf(buf, sizeof(buf), "%d", 0);
1401 geometry->add_property(X_("y_off"), string(buf));
1403 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&rhs_pane1)->gobj()));
1404 geometry->add_property(X_("mixer_rhs_pane1_pos"), string(buf));
1405 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&list_hpane)->gobj()));
1406 geometry->add_property(X_("mixer_list_hpane_pos"), string(buf));
1408 node->add_child_nocopy (*geometry);
1411 node->add_property ("narrow-strips", _strip_width == Narrow ? "yes" : "no");
1413 node->add_property ("show-mixer", _visible ? "yes" : "no");
1415 return *node;
1419 void
1420 Mixer_UI::pane_allocation_handler (Allocation&, Gtk::Paned* which)
1422 int pos;
1423 XMLProperty* prop = 0;
1424 char buf[32];
1425 XMLNode* node = ARDOUR_UI::instance()->mixer_settings();
1426 XMLNode* geometry;
1427 int width, height;
1428 static int32_t done[3] = { 0, 0, 0 };
1430 width = default_width;
1431 height = default_height;
1433 if ((geometry = find_named_node (*node, "geometry")) != 0) {
1436 if ((prop = geometry->property ("x_size")) == 0) {
1437 prop = geometry->property ("x-size");
1439 if (prop) {
1440 width = atoi (prop->value());
1442 if ((prop = geometry->property ("y_size")) == 0) {
1443 prop = geometry->property ("y-size");
1445 if (prop) {
1446 height = atoi (prop->value());
1450 if (which == static_cast<Gtk::Paned*> (&rhs_pane1)) {
1452 if (done[0]) {
1453 return;
1456 if (!geometry || (prop = geometry->property("mixer-rhs-pane1-pos")) == 0) {
1457 pos = height / 3;
1458 snprintf (buf, sizeof(buf), "%d", pos);
1459 } else {
1460 pos = atoi (prop->value());
1463 if ((done[0] = GTK_WIDGET(rhs_pane1.gobj())->allocation.height > pos)) {
1464 rhs_pane1.set_position (pos);
1467 } else if (which == static_cast<Gtk::Paned*> (&list_hpane)) {
1469 if (done[2]) {
1470 return;
1473 if (!geometry || (prop = geometry->property("mixer-list-hpane-pos")) == 0) {
1474 pos = 75;
1475 snprintf (buf, sizeof(buf), "%d", pos);
1476 } else {
1477 pos = atoi (prop->value());
1480 if ((done[2] = GTK_WIDGET(list_hpane.gobj())->allocation.width > pos)) {
1481 list_hpane.set_position (pos);
1485 void
1486 Mixer_UI::scroll_left ()
1488 Adjustment* adj = scroller.get_hscrollbar()->get_adjustment();
1489 /* stupid GTK: can't rely on clamping across versions */
1490 scroller.get_hscrollbar()->set_value (max (adj->get_lower(), adj->get_value() - adj->get_step_increment()));
1493 void
1494 Mixer_UI::scroll_right ()
1496 Adjustment* adj = scroller.get_hscrollbar()->get_adjustment();
1497 /* stupid GTK: can't rely on clamping across versions */
1498 scroller.get_hscrollbar()->set_value (min (adj->get_upper(), adj->get_value() + adj->get_step_increment()));
1501 bool
1502 Mixer_UI::on_key_press_event (GdkEventKey* ev)
1504 switch (ev->keyval) {
1505 case GDK_Left:
1506 scroll_left ();
1507 return true;
1509 case GDK_Right:
1510 scroll_right ();
1511 return true;
1513 default:
1514 break;
1517 return key_press_focus_accelerator_handler (*this, ev);
1520 bool
1521 Mixer_UI::on_key_release_event (GdkEventKey* ev)
1523 return Gtk::Window::on_key_release_event (ev);
1524 // return key_press_focus_accelerator_handler (*this, ev);
1528 bool
1529 Mixer_UI::on_scroll_event (GdkEventScroll* ev)
1531 switch (ev->direction) {
1532 case GDK_SCROLL_LEFT:
1533 scroll_left ();
1534 return true;
1535 case GDK_SCROLL_UP:
1536 if (ev->state & Keyboard::TertiaryModifier) {
1537 scroll_left ();
1538 return true;
1540 return false;
1542 case GDK_SCROLL_RIGHT:
1543 scroll_right ();
1544 return true;
1546 case GDK_SCROLL_DOWN:
1547 if (ev->state & Keyboard::TertiaryModifier) {
1548 scroll_right ();
1549 return true;
1551 return false;
1554 return false;
1558 void
1559 Mixer_UI::parameter_changed (string const & p)
1561 if (p == "show-group-tabs") {
1562 bool const s = _session->config.get_show_group_tabs ();
1563 if (s) {
1564 _group_tabs->show ();
1565 } else {
1566 _group_tabs->hide ();
1568 } else if (p == "default-narrow_ms") {
1569 bool const s = Config->get_default_narrow_ms ();
1570 for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
1571 (*i)->set_width_enum (s ? Narrow : Wide, this);
1576 void
1577 Mixer_UI::set_route_group_activation (RouteGroup* g, bool a)
1579 g->set_active (a, this);
1582 PluginSelector*
1583 Mixer_UI::plugin_selector()
1585 #ifdef DEFER_PLUGIN_SELECTOR_LOAD
1586 if (!_plugin_selector)
1587 _plugin_selector = new PluginSelector (PluginManager::the_manager ());
1588 #endif
1590 return _plugin_selector;
1593 void
1594 Mixer_UI::setup_track_display ()
1596 track_model = ListStore::create (track_columns);
1597 track_display.set_model (track_model);
1598 track_display.append_column (_("Strips"), track_columns.text);
1599 track_display.append_column (_("Show"), track_columns.visible);
1600 track_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0));
1601 track_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
1602 track_display.get_column (0)->set_expand(true);
1603 track_display.get_column (1)->set_expand(false);
1604 track_display.set_name (X_("MixerTrackDisplayList"));
1605 track_display.get_selection()->set_mode (Gtk::SELECTION_NONE);
1606 track_display.set_reorderable (true);
1607 track_display.set_headers_visible (true);
1609 track_model->signal_row_deleted().connect (sigc::mem_fun (*this, &Mixer_UI::track_list_delete));
1610 track_model->signal_row_changed().connect (sigc::mem_fun (*this, &Mixer_UI::track_list_change));
1611 track_model->signal_rows_reordered().connect (sigc::mem_fun (*this, &Mixer_UI::track_list_reorder));
1613 CellRendererToggle* track_list_visible_cell = dynamic_cast<CellRendererToggle*>(track_display.get_column_cell_renderer (1));
1614 track_list_visible_cell->property_activatable() = true;
1615 track_list_visible_cell->property_radio() = false;
1617 track_display.signal_button_press_event().connect (sigc::mem_fun (*this, &Mixer_UI::track_display_button_press), false);
1619 track_display_scroller.add (track_display);
1620 track_display_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
1622 VBox* v = manage (new VBox);
1623 v->show ();
1624 v->pack_start (track_display_scroller, true, true);
1626 Button* b = manage (new Button);
1627 b->show ();
1628 Widget* w = manage (new Image (Stock::ADD, ICON_SIZE_BUTTON));
1629 w->show ();
1630 b->add (*w);
1632 b->signal_clicked().connect (sigc::mem_fun (*this, &Mixer_UI::new_track_or_bus));
1634 v->pack_start (*b, false, false);
1636 track_display_frame.set_name("BaseFrame");
1637 track_display_frame.set_shadow_type (Gtk::SHADOW_IN);
1638 track_display_frame.add (*v);
1640 track_display_scroller.show();
1641 track_display_frame.show();
1642 track_display.show();
1645 void
1646 Mixer_UI::new_track_or_bus ()
1648 ARDOUR_UI::instance()->add_route (this);