more pool allocation debugging for oofus
[ardour2.git] / gtk2_ardour / mixer_ui.cc
blob4a3ec325a1afd2c557e4f7427e57210f7175300c
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_ALWAYS, Gtk::POLICY_AUTOMATIC);
98 track_model = ListStore::create (track_columns);
99 track_display.set_model (track_model);
100 track_display.append_column (_("Strips"), track_columns.text);
101 track_display.append_column (_("Show"), track_columns.visible);
102 track_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0));
103 track_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
104 track_display.get_column (0)->set_expand(true);
105 track_display.get_column (1)->set_expand(false);
106 track_display.set_name (X_("MixerTrackDisplayList"));
107 track_display.get_selection()->set_mode (Gtk::SELECTION_NONE);
108 track_display.set_reorderable (true);
109 track_display.set_headers_visible (true);
111 track_model->signal_row_deleted().connect (sigc::mem_fun (*this, &Mixer_UI::track_list_delete));
112 track_model->signal_row_changed().connect (sigc::mem_fun (*this, &Mixer_UI::track_list_change));
113 track_model->signal_rows_reordered().connect (sigc::mem_fun (*this, &Mixer_UI::track_list_reorder));
115 CellRendererToggle* track_list_visible_cell = dynamic_cast<CellRendererToggle*>(track_display.get_column_cell_renderer (1));
116 track_list_visible_cell->property_activatable() = true;
117 track_list_visible_cell->property_radio() = false;
119 track_display.signal_button_press_event().connect (sigc::mem_fun (*this, &Mixer_UI::track_display_button_press), false);
121 track_display_scroller.add (track_display);
122 track_display_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
124 group_model = ListStore::create (group_columns);
125 group_display.set_model (group_model);
126 group_display.append_column (_("Group"), group_columns.text);
127 group_display.append_column (_("Show"), group_columns.visible);
128 group_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0));
129 group_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
130 group_display.get_column (0)->set_expand(true);
131 group_display.get_column (1)->set_expand(false);
132 group_display.set_name ("MixerGroupList");
133 group_display.get_selection()->set_mode (Gtk::SELECTION_SINGLE);
134 group_display.set_reorderable (true);
135 group_display.set_headers_visible (true);
136 group_display.set_rules_hint (true);
138 /* name is directly editable */
140 CellRendererText* name_cell = dynamic_cast<CellRendererText*>(group_display.get_column_cell_renderer (0));
141 name_cell->property_editable() = true;
142 name_cell->signal_edited().connect (sigc::mem_fun (*this, &Mixer_UI::route_group_name_edit));
144 /* use checkbox for the active column */
146 CellRendererToggle* active_cell = dynamic_cast<CellRendererToggle*>(group_display.get_column_cell_renderer (1));
147 active_cell->property_activatable() = true;
148 active_cell->property_radio() = false;
150 group_model->signal_row_changed().connect (sigc::mem_fun (*this, &Mixer_UI::route_group_row_change));
152 group_display.signal_button_press_event().connect (sigc::mem_fun (*this, &Mixer_UI::group_display_button_press), false);
154 group_display_scroller.add (group_display);
155 group_display_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
157 HBox* route_group_display_button_box = manage (new HBox());
159 Button* route_group_add_button = manage (new Button ());
160 Button* route_group_remove_button = manage (new Button ());
162 Widget* w;
164 w = manage (new Image (Stock::ADD, ICON_SIZE_BUTTON));
165 w->show();
166 route_group_add_button->add (*w);
168 w = manage (new Image (Stock::REMOVE, ICON_SIZE_BUTTON));
169 w->show();
170 route_group_remove_button->add (*w);
172 route_group_display_button_box->set_homogeneous (true);
174 route_group_add_button->signal_clicked().connect (sigc::mem_fun (*this, &Mixer_UI::new_route_group));
175 route_group_remove_button->signal_clicked().connect (sigc::mem_fun (*this, &Mixer_UI::remove_selected_route_group));
177 route_group_display_button_box->add (*route_group_add_button);
178 route_group_display_button_box->add (*route_group_remove_button);
180 group_display_vbox.pack_start (group_display_scroller, true, true);
181 group_display_vbox.pack_start (*route_group_display_button_box, false, false);
183 track_display_frame.set_name("BaseFrame");
184 track_display_frame.set_shadow_type (Gtk::SHADOW_IN);
185 track_display_frame.add(track_display_scroller);
187 group_display_frame.set_name ("BaseFrame");
188 group_display_frame.set_shadow_type (Gtk::SHADOW_IN);
189 group_display_frame.add (group_display_vbox);
191 rhs_pane1.pack1 (track_display_frame);
192 rhs_pane1.pack2 (group_display_frame);
194 list_vpacker.pack_start (rhs_pane1, true, true);
196 global_hpacker.pack_start (scroller, true, true);
197 #ifdef GTKOSX
198 /* current gtk-quartz has dirty updates on borders like this one */
199 global_hpacker.pack_start (out_packer, false, false, 0);
200 #else
201 global_hpacker.pack_start (out_packer, false, false, 12);
202 #endif
203 list_hpane.add1(list_vpacker);
204 list_hpane.add2(global_hpacker);
206 rhs_pane1.signal_size_allocate().connect (sigc::bind (sigc::mem_fun(*this, &Mixer_UI::pane_allocation_handler),
207 static_cast<Gtk::Paned*> (&rhs_pane1)));
208 list_hpane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun(*this, &Mixer_UI::pane_allocation_handler),
209 static_cast<Gtk::Paned*> (&list_hpane)));
211 global_vpacker.pack_start (list_hpane, true, true);
213 add (global_vpacker);
214 set_name ("MixerWindow");
216 WindowTitle title(Glib::get_application_name());
217 title += _("Mixer");
218 set_title (title.get_string());
220 set_wmclass (X_("ardour_mixer"), PROGRAM_NAME);
222 add_accel_group (ActionManager::ui_manager->get_accel_group());
224 signal_delete_event().connect (sigc::mem_fun (*this, &Mixer_UI::hide_window));
225 add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
227 signal_configure_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::configure_handler));
229 _selection.RoutesChanged.connect (sigc::mem_fun(*this, &Mixer_UI::follow_strip_selection));
231 route_group_display_button_box->show();
232 route_group_add_button->show();
233 route_group_remove_button->show();
235 global_hpacker.show();
236 global_vpacker.show();
237 scroller.show();
238 scroller_base.show();
239 scroller_hpacker.show();
240 mixer_scroller_vpacker.show();
241 list_vpacker.show();
242 group_display_button_label.show();
243 group_display_button.show();
244 track_display_scroller.show();
245 group_display_scroller.show();
246 group_display_vbox.show();
247 track_display_frame.show();
248 group_display_frame.show();
249 rhs_pane1.show();
250 strip_packer.show();
251 out_packer.show();
252 list_hpane.show();
253 track_display.show();
254 group_display.show();
256 auto_rebinding = FALSE;
258 MixerStrip::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&Mixer_UI::remove_strip, this, _1), gui_context());
260 MonitorSection::setup_knob_images ();
262 #ifndef DEFER_PLUGIN_SELECTOR_LOAD
263 _plugin_selector = new PluginSelector (PluginManager::the_manager ());
264 #endif
267 Mixer_UI::~Mixer_UI ()
271 void
272 Mixer_UI::ensure_float (Window& win)
274 win.set_transient_for (*this);
277 void
278 Mixer_UI::show_window ()
280 present ();
281 if (!_visible) {
282 set_window_pos_and_size ();
284 /* now reset each strips width so the right widgets are shown */
285 MixerStrip* ms;
287 TreeModel::Children rows = track_model->children();
288 TreeModel::Children::iterator ri;
290 for (ri = rows.begin(); ri != rows.end(); ++ri) {
291 ms = (*ri)[track_columns.strip];
292 ms->set_width_enum (ms->get_width_enum (), ms->width_owner());
295 _visible = true;
298 bool
299 Mixer_UI::hide_window (GdkEventAny *ev)
301 get_window_pos_and_size ();
303 _visible = false;
304 return just_hide_it(ev, static_cast<Gtk::Window *>(this));
308 void
309 Mixer_UI::add_strip (RouteList& routes)
311 ENSURE_GUI_THREAD (*this, &Mixer_UI::add_strip, routes)
313 MixerStrip* strip;
315 no_track_list_redisplay = true;
316 strip_redisplay_does_not_sync_order_keys = true;
318 for (RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
319 boost::shared_ptr<Route> route = (*x);
321 if (route->is_hidden()) {
322 continue;
325 if (route->is_monitor()) {
326 if (!_monitor_section) {
327 _monitor_section = new MonitorSection (_session);
328 out_packer.pack_end (_monitor_section->tearoff(), false, false);
329 } else {
330 _monitor_section->set_session (_session);
333 _monitor_section->tearoff().show_all ();
335 XMLNode* mnode = ARDOUR_UI::instance()->tearoff_settings (X_("monitor-section"));
336 if (mnode) {
337 _monitor_section->tearoff().set_state (*mnode);
340 /* no regular strip shown for control out */
342 continue;
345 strip = new MixerStrip (*this, _session, route);
346 strips.push_back (strip);
348 Config->get_default_narrow_ms() ? _strip_width = Narrow : _strip_width = Wide;
350 if (strip->width_owner() != strip) {
351 strip->set_width_enum (_strip_width, this);
354 show_strip (strip);
356 TreeModel::Row row = *(track_model->append());
357 row[track_columns.text] = route->name();
358 row[track_columns.visible] = strip->marked_for_display();
359 row[track_columns.route] = route;
360 row[track_columns.strip] = strip;
362 if (route->order_key (N_("signal")) == -1) {
363 route->set_order_key (N_("signal"), track_model->children().size()-1);
366 route->PropertyChanged.connect (*this, invalidator (*this), ui_bind (&Mixer_UI::strip_property_changed, this, _1, strip), gui_context());
368 strip->WidthChanged.connect (sigc::mem_fun(*this, &Mixer_UI::strip_width_changed));
369 strip->signal_button_release_event().connect (sigc::bind (sigc::mem_fun(*this, &Mixer_UI::strip_button_release_event), strip));
372 no_track_list_redisplay = false;
374 redisplay_track_list ();
376 strip_redisplay_does_not_sync_order_keys = false;
379 void
380 Mixer_UI::remove_strip (MixerStrip* strip)
382 if (_session && _session->deletion_in_progress()) {
383 /* its all being taken care of */
384 return;
387 ENSURE_GUI_THREAD (*this, &Mixer_UI::remove_strip, strip);
389 cerr << "Mixer UI removing strip for " << strip << endl;
391 TreeModel::Children rows = track_model->children();
392 TreeModel::Children::iterator ri;
393 list<MixerStrip *>::iterator i;
395 if ((i = find (strips.begin(), strips.end(), strip)) != strips.end()) {
396 strips.erase (i);
399 strip_redisplay_does_not_sync_order_keys = true;
401 for (ri = rows.begin(); ri != rows.end(); ++ri) {
402 if ((*ri)[track_columns.strip] == strip) {
403 track_model->erase (ri);
404 break;
408 strip_redisplay_does_not_sync_order_keys = false;
411 void
412 Mixer_UI::sync_order_keys (string const & src)
414 TreeModel::Children rows = track_model->children();
415 TreeModel::Children::iterator ri;
417 if (src == N_("signal") || !_session || (_session->state_of_the_state() & (Session::Loading|Session::Deletion)) || rows.empty()) {
418 return;
421 std::map<int,int> keys;
423 bool changed = false;
425 unsigned order = 0;
426 for (ri = rows.begin(); ri != rows.end(); ++ri, ++order) {
427 boost::shared_ptr<Route> route = (*ri)[track_columns.route];
428 unsigned int old_key = order;
429 unsigned int new_key = route->order_key (N_("signal"));
431 keys[new_key] = old_key;
433 if (new_key != old_key) {
434 changed = true;
438 if (keys.size() != rows.size()) {
439 PBD::stacktrace (cerr, 20);
441 assert(keys.size() == rows.size());
443 // Remove any gaps in keys caused by automation children tracks
444 vector<int> neworder;
445 for (std::map<int,int>::const_iterator i = keys.begin(); i != keys.end(); ++i) {
446 neworder.push_back(i->second);
448 assert(neworder.size() == rows.size());
450 if (changed) {
451 strip_redisplay_does_not_reset_order_keys = true;
452 track_model->reorder (neworder);
453 strip_redisplay_does_not_reset_order_keys = false;
457 void
458 Mixer_UI::follow_strip_selection ()
460 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
461 (*i)->set_selected (_selection.selected ((*i)->route()));
465 bool
466 Mixer_UI::strip_button_release_event (GdkEventButton *ev, MixerStrip *strip)
468 if (ev->button == 1) {
470 /* this allows the user to click on the strip to terminate comment
471 editing. XXX it needs improving so that we don't select the strip
472 at the same time.
475 if (_selection.selected (strip->route())) {
476 _selection.remove (strip->route());
477 } else {
478 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
479 _selection.add (strip->route());
480 } else {
481 _selection.set (strip->route());
486 return true;
489 void
490 Mixer_UI::set_session (Session* sess)
492 SessionHandlePtr::set_session (sess);
494 if (_plugin_selector) {
495 _plugin_selector->set_session (_session);
498 _group_tabs->set_session (sess);
500 if (!_session) {
501 return;
504 XMLNode* node = ARDOUR_UI::instance()->mixer_settings();
505 set_state (*node);
507 WindowTitle title(_session->name());
508 title += _("Mixer");
509 title += Glib::get_application_name();
511 set_title (title.get_string());
513 initial_track_display ();
515 _session->RouteAdded.connect (_session_connections, invalidator (*this), ui_bind (&Mixer_UI::add_strip, this, _1), gui_context());
516 _session->route_group_added.connect (_session_connections, invalidator (*this), ui_bind (&Mixer_UI::add_route_group, this, _1), gui_context());
517 _session->route_group_removed.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::route_groups_changed, this), gui_context());
518 _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), ui_bind (&Mixer_UI::parameter_changed, this, _1), gui_context());
520 Config->ParameterChanged.connect (*this, invalidator (*this), ui_bind (&Mixer_UI::parameter_changed, this, _1), gui_context ());
522 route_groups_changed ();
524 if (_visible) {
525 show_window();
528 start_updating ();
531 void
532 Mixer_UI::session_going_away ()
534 ENSURE_GUI_THREAD (*this, &Mixer_UI::session_going_away)
536 group_model->clear ();
537 _selection.clear ();
538 track_model->clear ();
540 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
541 delete (*i);
544 if (_monitor_section) {
545 _monitor_section->tearoff().hide_visible ();
548 strips.clear ();
550 WindowTitle title(Glib::get_application_name());
551 title += _("Mixer");
552 set_title (title.get_string());
554 stop_updating ();
556 SessionHandlePtr::session_going_away ();
559 void
560 Mixer_UI::show_strip (MixerStrip* ms)
562 TreeModel::Children rows = track_model->children();
563 TreeModel::Children::iterator i;
565 for (i = rows.begin(); i != rows.end(); ++i) {
567 MixerStrip* strip = (*i)[track_columns.strip];
568 if (strip == ms) {
569 (*i)[track_columns.visible] = true;
570 break;
575 void
576 Mixer_UI::hide_strip (MixerStrip* ms)
578 TreeModel::Children rows = track_model->children();
579 TreeModel::Children::iterator i;
581 for (i = rows.begin(); i != rows.end(); ++i) {
583 MixerStrip* strip = (*i)[track_columns.strip];
584 if (strip == ms) {
585 (*i)[track_columns.visible] = false;
586 break;
591 gint
592 Mixer_UI::start_updating ()
594 fast_screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (sigc::mem_fun(*this, &Mixer_UI::fast_update_strips));
595 return 0;
598 gint
599 Mixer_UI::stop_updating ()
601 fast_screen_update_connection.disconnect();
602 return 0;
605 void
606 Mixer_UI::fast_update_strips ()
608 if (is_mapped () && _session) {
609 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
610 (*i)->fast_update ();
615 void
616 Mixer_UI::set_all_strips_visibility (bool yn)
618 TreeModel::Children rows = track_model->children();
619 TreeModel::Children::iterator i;
621 no_track_list_redisplay = true;
623 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 (*i)[track_columns.visible] = yn;
639 no_track_list_redisplay = false;
640 redisplay_track_list ();
644 void
645 Mixer_UI::set_all_audio_visibility (int tracks, bool yn)
647 TreeModel::Children rows = track_model->children();
648 TreeModel::Children::iterator i;
650 no_track_list_redisplay = true;
652 for (i = rows.begin(); i != rows.end(); ++i) {
653 TreeModel::Row row = (*i);
654 MixerStrip* strip = row[track_columns.strip];
656 if (strip == 0) {
657 continue;
660 if (strip->route()->is_master() || strip->route()->is_monitor()) {
661 continue;
664 boost::shared_ptr<AudioTrack> at = strip->audio_track();
666 switch (tracks) {
667 case 0:
668 (*i)[track_columns.visible] = yn;
669 break;
671 case 1:
672 if (at) { /* track */
673 (*i)[track_columns.visible] = yn;
675 break;
677 case 2:
678 if (!at) { /* bus */
679 (*i)[track_columns.visible] = yn;
681 break;
685 no_track_list_redisplay = false;
686 redisplay_track_list ();
689 void
690 Mixer_UI::hide_all_routes ()
692 set_all_strips_visibility (false);
695 void
696 Mixer_UI::show_all_routes ()
698 set_all_strips_visibility (true);
701 void
702 Mixer_UI::show_all_audiobus ()
704 set_all_audio_visibility (2, true);
706 void
707 Mixer_UI::hide_all_audiobus ()
709 set_all_audio_visibility (2, false);
712 void
713 Mixer_UI::show_all_audiotracks()
715 set_all_audio_visibility (1, true);
717 void
718 Mixer_UI::hide_all_audiotracks ()
720 set_all_audio_visibility (1, false);
723 void
724 Mixer_UI::track_list_reorder (const TreeModel::Path&, const TreeModel::iterator&, int* /*new_order*/)
726 strip_redisplay_does_not_sync_order_keys = true;
727 _session->set_remote_control_ids();
728 redisplay_track_list ();
729 strip_redisplay_does_not_sync_order_keys = false;
732 void
733 Mixer_UI::track_list_change (const Gtk::TreeModel::Path&, const Gtk::TreeModel::iterator&)
735 // never reset order keys because of a property change
736 strip_redisplay_does_not_reset_order_keys = true;
737 _session->set_remote_control_ids();
738 redisplay_track_list ();
739 strip_redisplay_does_not_reset_order_keys = false;
742 void
743 Mixer_UI::track_list_delete (const Gtk::TreeModel::Path&)
745 /* this could require an order sync */
746 if (_session && !_session->deletion_in_progress()) {
747 _session->set_remote_control_ids();
748 redisplay_track_list ();
752 void
753 Mixer_UI::redisplay_track_list ()
755 TreeModel::Children rows = track_model->children();
756 TreeModel::Children::iterator i;
757 long order;
759 if (no_track_list_redisplay) {
760 return;
763 for (order = 0, i = rows.begin(); i != rows.end(); ++i, ++order) {
764 MixerStrip* strip = (*i)[track_columns.strip];
766 if (strip == 0) {
767 /* we're in the middle of changing a row, don't worry */
768 continue;
771 bool visible = (*i)[track_columns.visible];
773 if (visible) {
774 strip->set_marked_for_display (true);
775 strip->route()->set_order_key (N_("signal"), order);
777 if (!strip_redisplay_does_not_reset_order_keys) {
778 strip->route()->set_order_key (N_("signal"), order);
781 if (strip->packed()) {
783 if (strip->route()->is_master() || strip->route()->is_monitor()) {
784 out_packer.reorder_child (*strip, -1);
785 } else {
786 strip_packer.reorder_child (*strip, -1); /* put at end */
789 } else {
791 if (strip->route()->is_master() || strip->route()->is_monitor()) {
792 out_packer.pack_start (*strip, false, false);
793 } else {
794 strip_packer.pack_start (*strip, false, false);
796 strip->set_packed (true);
797 //strip->show();
800 } else {
802 strip->set_marked_for_display (false);
804 if (strip->route()->is_master() || strip->route()->is_monitor()) {
805 /* do nothing, these cannot be hidden */
806 } else {
807 if (strip->packed()) {
808 strip_packer.remove (*strip);
809 strip->set_packed (false);
815 if (!strip_redisplay_does_not_reset_order_keys && !strip_redisplay_does_not_sync_order_keys) {
816 _session->sync_order_keys (N_("signal"));
819 // Resigc::bind all of the midi controls automatically
821 if (auto_rebinding)
822 auto_rebind_midi_controls ();
824 _group_tabs->set_dirty ();
827 void
828 Mixer_UI::strip_width_changed ()
830 _group_tabs->set_dirty ();
832 #ifdef GTKOSX
833 TreeModel::Children rows = track_model->children();
834 TreeModel::Children::iterator i;
835 long order;
837 for (order = 0, i = rows.begin(); i != rows.end(); ++i, ++order) {
838 MixerStrip* strip = (*i)[track_columns.strip];
840 if (strip == 0) {
841 continue;
844 bool visible = (*i)[track_columns.visible];
846 if (visible) {
847 strip->queue_draw();
850 #endif
854 void
855 Mixer_UI::set_auto_rebinding( bool val )
857 if( val == TRUE )
859 auto_rebinding = TRUE;
860 Session::AutoBindingOff();
862 else
864 auto_rebinding = FALSE;
865 Session::AutoBindingOn();
869 void
870 Mixer_UI::toggle_auto_rebinding()
872 if (auto_rebinding)
874 set_auto_rebinding( FALSE );
877 else
879 set_auto_rebinding( TRUE );
882 auto_rebind_midi_controls();
885 void
886 Mixer_UI::auto_rebind_midi_controls ()
888 TreeModel::Children rows = track_model->children();
889 TreeModel::Children::iterator i;
890 int pos;
892 // Create bindings for all visible strips and remove those that are not visible
893 pos = 1; // 0 is reserved for the master strip
894 for (i = rows.begin(); i != rows.end(); ++i) {
895 MixerStrip* strip = (*i)[track_columns.strip];
897 if ( (*i)[track_columns.visible] == true ) { // add bindings for
898 // make the actual binding
899 //cout<<"Auto Binding: Visible Strip Found: "<<strip->name()<<endl;
901 int controlValue = pos;
902 if( strip->route()->is_master() ) {
903 controlValue = 0;
905 else {
906 pos++;
909 PBD::Controllable::CreateBinding ( strip->solo_button->get_controllable().get(), controlValue, 0);
910 PBD::Controllable::CreateBinding ( strip->mute_button->get_controllable().get(), controlValue, 1);
912 if( strip->is_audio_track() ) {
913 PBD::Controllable::CreateBinding ( strip->rec_enable_button->get_controllable().get(), controlValue, 2);
916 PBD::Controllable::CreateBinding ( strip->gpm.get_controllable().get(), controlValue, 3);
917 PBD::Controllable::CreateBinding ( strip->panners.get_controllable().get(), controlValue, 4);
920 else { // Remove any existing binding
921 PBD::Controllable::DeleteBinding ( strip->solo_button->get_controllable().get() );
922 PBD::Controllable::DeleteBinding ( strip->mute_button->get_controllable().get() );
924 if( strip->is_audio_track() ) {
925 PBD::Controllable::DeleteBinding ( strip->rec_enable_button->get_controllable().get() );
928 PBD::Controllable::DeleteBinding ( strip->gpm.get_controllable().get() );
929 PBD::Controllable::DeleteBinding ( strip->panners.get_controllable().get() ); // This only takes the first panner if there are multiples...
932 } // for
936 struct SignalOrderRouteSorter {
937 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
938 /* use of ">" forces the correct sort order */
939 return a->order_key (N_("signal")) < b->order_key (N_("signal"));
943 void
944 Mixer_UI::initial_track_display ()
946 boost::shared_ptr<RouteList> routes = _session->get_routes();
947 RouteList copy (*routes);
948 SignalOrderRouteSorter sorter;
950 copy.sort (sorter);
952 no_track_list_redisplay = true;
954 track_model->clear ();
956 add_strip (copy);
958 no_track_list_redisplay = false;
960 redisplay_track_list ();
963 void
964 Mixer_UI::show_track_list_menu ()
966 if (track_menu == 0) {
967 build_track_menu ();
970 track_menu->popup (1, gtk_get_current_event_time());
973 bool
974 Mixer_UI::track_display_button_press (GdkEventButton* ev)
976 if (Keyboard::is_context_menu_event (ev)) {
977 show_track_list_menu ();
978 return true;
981 TreeIter iter;
982 TreeModel::Path path;
983 TreeViewColumn* column;
984 int cellx;
985 int celly;
987 if (!track_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
988 return false;
991 switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
992 case 0:
993 /* allow normal processing to occur */
994 return false;
996 case 1: /* visibility */
998 if ((iter = track_model->get_iter (path))) {
999 MixerStrip* strip = (*iter)[track_columns.strip];
1000 if (strip) {
1002 if (!strip->route()->is_master() && !strip->route()->is_monitor()) {
1003 bool visible = (*iter)[track_columns.visible];
1004 (*iter)[track_columns.visible] = !visible;
1006 #ifdef GTKOSX
1007 track_display.queue_draw();
1008 #endif
1011 return true;
1013 default:
1014 break;
1017 return false;
1021 void
1022 Mixer_UI::build_track_menu ()
1024 using namespace Menu_Helpers;
1025 using namespace Gtk;
1027 track_menu = new Menu;
1028 track_menu->set_name ("ArdourContextMenu");
1029 MenuList& items = track_menu->items();
1031 items.push_back (MenuElem (_("Show All"), sigc::mem_fun(*this, &Mixer_UI::show_all_routes)));
1032 items.push_back (MenuElem (_("Hide All"), sigc::mem_fun(*this, &Mixer_UI::hide_all_routes)));
1033 items.push_back (MenuElem (_("Show All Audio Tracks"), sigc::mem_fun(*this, &Mixer_UI::show_all_audiotracks)));
1034 items.push_back (MenuElem (_("Hide All Audio Tracks"), sigc::mem_fun(*this, &Mixer_UI::hide_all_audiotracks)));
1035 items.push_back (MenuElem (_("Show All Audio Busses"), sigc::mem_fun(*this, &Mixer_UI::show_all_audiobus)));
1036 items.push_back (MenuElem (_("Hide All Audio Busses"), sigc::mem_fun(*this, &Mixer_UI::hide_all_audiobus)));
1040 void
1041 Mixer_UI::strip_property_changed (const PropertyChange& what_changed, MixerStrip* mx)
1043 if (!what_changed.contains (ARDOUR::Properties::name)) {
1044 return;
1047 ENSURE_GUI_THREAD (*this, &Mixer_UI::strip_name_changed, what_changed, mx)
1049 TreeModel::Children rows = track_model->children();
1050 TreeModel::Children::iterator i;
1052 for (i = rows.begin(); i != rows.end(); ++i) {
1053 if ((*i)[track_columns.strip] == mx) {
1054 (*i)[track_columns.text] = mx->route()->name();
1055 return;
1059 error << _("track display list item for renamed strip not found!") << endmsg;
1062 bool
1063 Mixer_UI::group_display_button_press (GdkEventButton* ev)
1065 TreeModel::Path path;
1066 TreeViewColumn* column;
1067 int cellx;
1068 int celly;
1070 if (!group_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
1071 return false;
1074 TreeIter iter = group_model->get_iter (path);
1075 if (!iter) {
1076 return false;
1079 RouteGroup* group = (*iter)[group_columns.group];
1081 if (Keyboard::is_context_menu_event (ev)) {
1082 _group_tabs->get_menu(group)->popup (1, ev->time);
1083 return true;
1086 switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
1087 case 0:
1088 if (Keyboard::is_edit_event (ev)) {
1089 if (group) {
1090 // edit_route_group (group);
1091 #ifdef GTKOSX
1092 group_display.queue_draw();
1093 #endif
1094 return true;
1097 break;
1099 case 1:
1101 bool visible = (*iter)[group_columns.visible];
1102 (*iter)[group_columns.visible] = !visible;
1103 #ifdef GTKOSX
1104 group_display.queue_draw();
1105 #endif
1106 return true;
1109 default:
1110 break;
1113 return false;
1116 void
1117 Mixer_UI::activate_all_route_groups ()
1119 _session->foreach_route_group (sigc::bind (sigc::mem_fun (*this, &Mixer_UI::set_route_group_activation), true));
1122 void
1123 Mixer_UI::disable_all_route_groups ()
1125 _session->foreach_route_group (sigc::bind (sigc::mem_fun (*this, &Mixer_UI::set_route_group_activation), false));
1128 void
1129 Mixer_UI::route_groups_changed ()
1131 ENSURE_GUI_THREAD (*this, &Mixer_UI::route_groups_changed)
1133 /* just rebuild the while thing */
1135 group_model->clear ();
1138 TreeModel::Row row;
1139 row = *(group_model->append());
1140 row[group_columns.visible] = true;
1141 row[group_columns.text] = (_("-all-"));
1142 row[group_columns.group] = 0;
1145 _session->foreach_route_group (sigc::mem_fun (*this, &Mixer_UI::add_route_group));
1147 _group_tabs->set_dirty ();
1150 void
1151 Mixer_UI::new_route_group ()
1153 RouteList rl;
1155 _group_tabs->run_new_group_dialog (rl);
1158 void
1159 Mixer_UI::remove_selected_route_group ()
1161 Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
1162 TreeView::Selection::ListHandle_Path rows = selection->get_selected_rows ();
1164 if (rows.empty()) {
1165 return;
1168 TreeView::Selection::ListHandle_Path::iterator i = rows.begin();
1169 TreeIter iter;
1171 /* selection mode is single, so rows.begin() is it */
1173 if ((iter = group_model->get_iter (*i))) {
1175 RouteGroup* rg = (*iter)[group_columns.group];
1177 if (rg) {
1178 _session->remove_route_group (*rg);
1183 void
1184 Mixer_UI::route_group_property_changed (RouteGroup* group, const PropertyChange& change)
1186 if (in_group_row_change) {
1187 return;
1190 /* force an update of any mixer strips that are using this group,
1191 otherwise mix group names don't change in mixer strips
1194 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1195 if ((*i)->route_group() == group) {
1196 (*i)->route_group_changed();
1200 TreeModel::iterator i;
1201 TreeModel::Children rows = group_model->children();
1202 Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
1204 in_group_row_change = true;
1206 for (i = rows.begin(); i != rows.end(); ++i) {
1207 if ((*i)[group_columns.group] == group) {
1208 (*i)[group_columns.visible] = !group->is_hidden ();
1209 (*i)[group_columns.text] = group->name ();
1210 break;
1214 in_group_row_change = false;
1216 if (change.contains (Properties::name)) {
1217 _group_tabs->set_dirty ();
1221 void
1222 Mixer_UI::route_group_name_edit (const std::string& path, const std::string& new_text)
1224 RouteGroup* group;
1225 TreeIter iter;
1227 if ((iter = group_model->get_iter (path))) {
1229 if ((group = (*iter)[group_columns.group]) == 0) {
1230 return;
1233 if (new_text != group->name()) {
1234 group->set_name (new_text);
1239 void
1240 Mixer_UI::route_group_row_change (const Gtk::TreeModel::Path&, const Gtk::TreeModel::iterator& iter)
1242 RouteGroup* group;
1244 if (in_group_row_change) {
1245 return;
1248 if ((group = (*iter)[group_columns.group]) == 0) {
1249 return;
1252 if ((*iter)[group_columns.visible]) {
1253 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1254 if ((*i)->route_group() == group) {
1255 show_strip (*i);
1258 } else {
1259 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1260 if ((*i)->route_group() == group) {
1261 hide_strip (*i);
1266 std::string name = (*iter)[group_columns.text];
1268 if (name != group->name()) {
1269 group->set_name (name);
1274 void
1275 Mixer_UI::add_route_group (RouteGroup* group)
1277 ENSURE_GUI_THREAD (*this, &Mixer_UI::add_route_group, group)
1278 bool focus = false;
1280 in_group_row_change = true;
1282 TreeModel::Row row = *(group_model->append());
1283 row[group_columns.visible] = true;
1284 row[group_columns.group] = group;
1285 if (!group->name().empty()) {
1286 row[group_columns.text] = group->name();
1287 } else {
1288 row[group_columns.text] = _("unnamed");
1289 focus = true;
1292 group->PropertyChanged.connect (*this, invalidator (*this), ui_bind (&Mixer_UI::route_group_property_changed, this, group, _1), gui_context());
1294 if (focus) {
1295 TreeViewColumn* col = group_display.get_column (0);
1296 CellRendererText* name_cell = dynamic_cast<CellRendererText*>(group_display.get_column_cell_renderer (0));
1297 group_display.set_cursor (group_model->get_path (row), *col, *name_cell, true);
1300 _group_tabs->set_dirty ();
1302 in_group_row_change = false;
1305 bool
1306 Mixer_UI::strip_scroller_button_release (GdkEventButton* ev)
1308 using namespace Menu_Helpers;
1310 if (Keyboard::is_context_menu_event (ev)) {
1311 ARDOUR_UI::instance()->add_route (this);
1312 return true;
1315 return false;
1318 void
1319 Mixer_UI::set_strip_width (Width w)
1321 _strip_width = w;
1323 for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
1324 (*i)->set_width_enum (w, this);
1328 void
1329 Mixer_UI::set_window_pos_and_size ()
1331 resize (m_width, m_height);
1332 move (m_root_x, m_root_y);
1335 void
1336 Mixer_UI::get_window_pos_and_size ()
1338 get_position(m_root_x, m_root_y);
1339 get_size(m_width, m_height);
1343 Mixer_UI::set_state (const XMLNode& node)
1345 const XMLProperty* prop;
1346 XMLNode* geometry;
1348 m_width = default_width;
1349 m_height = default_height;
1350 m_root_x = 1;
1351 m_root_y = 1;
1353 if ((geometry = find_named_node (node, "geometry")) != 0) {
1355 XMLProperty* prop;
1357 if ((prop = geometry->property("x_size")) == 0) {
1358 prop = geometry->property ("x-size");
1360 if (prop) {
1361 m_width = atoi(prop->value());
1363 if ((prop = geometry->property("y_size")) == 0) {
1364 prop = geometry->property ("y-size");
1366 if (prop) {
1367 m_height = atoi(prop->value());
1370 if ((prop = geometry->property ("x_pos")) == 0) {
1371 prop = geometry->property ("x-pos");
1373 if (prop) {
1374 m_root_x = atoi (prop->value());
1377 if ((prop = geometry->property ("y_pos")) == 0) {
1378 prop = geometry->property ("y-pos");
1380 if (prop) {
1381 m_root_y = atoi (prop->value());
1385 set_window_pos_and_size ();
1387 if ((prop = node.property ("narrow-strips"))) {
1388 if (string_is_affirmative (prop->value())) {
1389 set_strip_width (Narrow);
1390 } else {
1391 set_strip_width (Wide);
1395 if ((prop = node.property ("show-mixer"))) {
1396 if (string_is_affirmative (prop->value())) {
1397 _visible = true;
1401 return 0;
1404 XMLNode&
1405 Mixer_UI::get_state (void)
1407 XMLNode* node = new XMLNode ("Mixer");
1409 if (is_realized()) {
1410 Glib::RefPtr<Gdk::Window> win = get_window();
1412 get_window_pos_and_size ();
1414 XMLNode* geometry = new XMLNode ("geometry");
1415 char buf[32];
1416 snprintf(buf, sizeof(buf), "%d", m_width);
1417 geometry->add_property(X_("x_size"), string(buf));
1418 snprintf(buf, sizeof(buf), "%d", m_height);
1419 geometry->add_property(X_("y_size"), string(buf));
1420 snprintf(buf, sizeof(buf), "%d", m_root_x);
1421 geometry->add_property(X_("x_pos"), string(buf));
1422 snprintf(buf, sizeof(buf), "%d", m_root_y);
1423 geometry->add_property(X_("y_pos"), string(buf));
1425 // written only for compatibility, they are not used.
1426 snprintf(buf, sizeof(buf), "%d", 0);
1427 geometry->add_property(X_("x_off"), string(buf));
1428 snprintf(buf, sizeof(buf), "%d", 0);
1429 geometry->add_property(X_("y_off"), string(buf));
1431 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&rhs_pane1)->gobj()));
1432 geometry->add_property(X_("mixer_rhs_pane1_pos"), string(buf));
1433 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&list_hpane)->gobj()));
1434 geometry->add_property(X_("mixer_list_hpane_pos"), string(buf));
1436 node->add_child_nocopy (*geometry);
1439 node->add_property ("narrow-strips", _strip_width == Narrow ? "yes" : "no");
1441 node->add_property ("show-mixer", _visible ? "yes" : "no");
1443 return *node;
1447 void
1448 Mixer_UI::pane_allocation_handler (Allocation&, Gtk::Paned* which)
1450 int pos;
1451 XMLProperty* prop = 0;
1452 char buf[32];
1453 XMLNode* node = ARDOUR_UI::instance()->mixer_settings();
1454 XMLNode* geometry;
1455 int width, height;
1456 static int32_t done[3] = { 0, 0, 0 };
1458 width = default_width;
1459 height = default_height;
1461 if ((geometry = find_named_node (*node, "geometry")) != 0) {
1464 if ((prop = geometry->property ("x_size")) == 0) {
1465 prop = geometry->property ("x-size");
1467 if (prop) {
1468 width = atoi (prop->value());
1470 if ((prop = geometry->property ("y_size")) == 0) {
1471 prop = geometry->property ("y-size");
1473 if (prop) {
1474 height = atoi (prop->value());
1478 if (which == static_cast<Gtk::Paned*> (&rhs_pane1)) {
1480 if (done[0]) {
1481 return;
1484 if (!geometry || (prop = geometry->property("mixer-rhs-pane1-pos")) == 0) {
1485 pos = height / 3;
1486 snprintf (buf, sizeof(buf), "%d", pos);
1487 } else {
1488 pos = atoi (prop->value());
1491 if ((done[0] = GTK_WIDGET(rhs_pane1.gobj())->allocation.height > pos)) {
1492 rhs_pane1.set_position (pos);
1495 } else if (which == static_cast<Gtk::Paned*> (&list_hpane)) {
1497 if (done[2]) {
1498 return;
1501 if (!geometry || (prop = geometry->property("mixer-list-hpane-pos")) == 0) {
1502 pos = 75;
1503 snprintf (buf, sizeof(buf), "%d", pos);
1504 } else {
1505 pos = atoi (prop->value());
1508 if ((done[2] = GTK_WIDGET(list_hpane.gobj())->allocation.width > pos)) {
1509 list_hpane.set_position (pos);
1513 void
1514 Mixer_UI::scroll_left ()
1516 Adjustment* adj = scroller.get_hscrollbar()->get_adjustment();
1517 /* stupid GTK: can't rely on clamping across versions */
1518 scroller.get_hscrollbar()->set_value (max (adj->get_lower(), adj->get_value() - adj->get_step_increment()));
1521 void
1522 Mixer_UI::scroll_right ()
1524 Adjustment* adj = scroller.get_hscrollbar()->get_adjustment();
1525 /* stupid GTK: can't rely on clamping across versions */
1526 scroller.get_hscrollbar()->set_value (min (adj->get_upper(), adj->get_value() + adj->get_step_increment()));
1529 bool
1530 Mixer_UI::on_key_press_event (GdkEventKey* ev)
1532 switch (ev->keyval) {
1533 case GDK_Left:
1534 scroll_left ();
1535 return true;
1537 case GDK_Right:
1538 scroll_right ();
1539 return true;
1541 default:
1542 break;
1545 return key_press_focus_accelerator_handler (*this, ev);
1548 bool
1549 Mixer_UI::on_key_release_event (GdkEventKey* ev)
1551 return Gtk::Window::on_key_release_event (ev);
1552 // return key_press_focus_accelerator_handler (*this, ev);
1556 bool
1557 Mixer_UI::on_scroll_event (GdkEventScroll* ev)
1559 switch (ev->direction) {
1560 case GDK_SCROLL_LEFT:
1561 scroll_left ();
1562 return true;
1563 case GDK_SCROLL_UP:
1564 if (ev->state & Keyboard::TertiaryModifier) {
1565 scroll_left ();
1566 return true;
1568 return false;
1570 case GDK_SCROLL_RIGHT:
1571 scroll_right ();
1572 return true;
1574 case GDK_SCROLL_DOWN:
1575 if (ev->state & Keyboard::TertiaryModifier) {
1576 scroll_right ();
1577 return true;
1579 return false;
1582 return false;
1586 void
1587 Mixer_UI::parameter_changed (string const & p)
1589 if (p == "show-group-tabs") {
1590 bool const s = _session->config.get_show_group_tabs ();
1591 if (s) {
1592 _group_tabs->show ();
1593 } else {
1594 _group_tabs->hide ();
1596 } else if (p == "default-narrow_ms") {
1597 bool const s = Config->get_default_narrow_ms ();
1598 for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
1599 (*i)->set_width_enum (s ? Narrow : Wide, this);
1604 void
1605 Mixer_UI::set_route_group_activation (RouteGroup* g, bool a)
1607 g->set_active (a, this);
1610 PluginSelector*
1611 Mixer_UI::plugin_selector()
1613 #ifdef DEFER_PLUGIN_SELECTOR_LOAD
1614 if (!_plugin_selector)
1615 _plugin_selector = new PluginSelector (PluginManager::the_manager ());
1616 #endif
1618 return _plugin_selector;