fix math bug with numthreads computation
[ardour2.git] / gtk2_ardour / mixer_ui.cc
blob4d6f7119cc72b9ac5528a7ed7a1de675165aee5c
1 /*
2 Copyright (C) 2000-2004 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 #include <algorithm>
21 #include <map>
22 #include <sigc++/bind.h>
24 #include <gtkmm/accelmap.h>
26 #include "pbd/convert.h"
27 #include "pbd/stacktrace.h"
28 #include <glibmm/thread.h>
30 #include <gtkmm2ext/gtk_ui.h>
31 #include <gtkmm2ext/utils.h>
32 #include <gtkmm2ext/tearoff.h>
33 #include <gtkmm2ext/window_title.h>
35 #include "ardour/audio_track.h"
36 #include "ardour/plugin_manager.h"
37 #include "ardour/route_group.h"
38 #include "ardour/session.h"
39 #include "ardour/session_route.h"
41 #include "keyboard.h"
42 #include "mixer_ui.h"
43 #include "mixer_strip.h"
44 #include "monitor_section.h"
45 #include "plugin_selector.h"
46 #include "ardour_ui.h"
47 #include "prompter.h"
48 #include "utils.h"
49 #include "actions.h"
50 #include "gui_thread.h"
51 #include "mixer_group_tabs.h"
53 #include "i18n.h"
55 using namespace ARDOUR;
56 using namespace PBD;
57 using namespace Gtk;
58 using namespace Glib;
59 using namespace Gtkmm2ext;
60 using namespace std;
62 using PBD::atoi;
64 Mixer_UI::Mixer_UI ()
65 : Window (Gtk::WINDOW_TOPLEVEL)
67 _strip_width = Config->get_default_narrow_ms() ? Narrow : Wide;
68 track_menu = 0;
69 _monitor_section = 0;
70 route_group_context_menu = 0;
71 no_track_list_redisplay = false;
72 in_group_row_change = false;
73 _visible = false;
74 strip_redisplay_does_not_reset_order_keys = false;
75 strip_redisplay_does_not_sync_order_keys = false;
76 ignore_sync = false;
78 Route::SyncOrderKeys.connect (*this, invalidator (*this), ui_bind (&Mixer_UI::sync_order_keys, this, _1), gui_context());
80 scroller_base.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
81 scroller_base.set_name ("MixerWindow");
82 scroller_base.signal_button_release_event().connect (sigc::mem_fun(*this, &Mixer_UI::strip_scroller_button_release));
83 // add as last item of strip packer
84 strip_packer.pack_end (scroller_base, true, true);
86 _group_tabs = new MixerGroupTabs (this);
87 VBox* b = manage (new VBox);
88 b->pack_start (*_group_tabs, PACK_SHRINK);
89 b->pack_start (strip_packer);
90 b->show_all ();
92 scroller.add (*b);
93 scroller.set_policy (Gtk::POLICY_ALWAYS, Gtk::POLICY_AUTOMATIC);
95 track_model = ListStore::create (track_columns);
96 track_display.set_model (track_model);
97 track_display.append_column (_("Strips"), track_columns.text);
98 track_display.append_column (_("Show"), track_columns.visible);
99 track_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0));
100 track_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
101 track_display.get_column (0)->set_expand(true);
102 track_display.get_column (1)->set_expand(false);
103 track_display.set_name (X_("MixerTrackDisplayList"));
104 track_display.get_selection()->set_mode (Gtk::SELECTION_NONE);
105 track_display.set_reorderable (true);
106 track_display.set_headers_visible (true);
108 track_model->signal_row_deleted().connect (sigc::mem_fun (*this, &Mixer_UI::track_list_delete));
109 track_model->signal_row_changed().connect (sigc::mem_fun (*this, &Mixer_UI::track_list_change));
110 track_model->signal_rows_reordered().connect (sigc::mem_fun (*this, &Mixer_UI::track_list_reorder));
112 CellRendererToggle* track_list_visible_cell = dynamic_cast<CellRendererToggle*>(track_display.get_column_cell_renderer (1));
113 track_list_visible_cell->property_activatable() = true;
114 track_list_visible_cell->property_radio() = false;
116 track_display.signal_button_press_event().connect (sigc::mem_fun (*this, &Mixer_UI::track_display_button_press), false);
118 track_display_scroller.add (track_display);
119 track_display_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
121 group_model = ListStore::create (group_columns);
122 group_display.set_model (group_model);
123 group_display.append_column (_("Group"), group_columns.text);
124 group_display.append_column (_("Show"), group_columns.visible);
125 group_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0));
126 group_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
127 group_display.get_column (0)->set_expand(true);
128 group_display.get_column (1)->set_expand(false);
129 group_display.set_name ("MixerGroupList");
130 group_display.get_selection()->set_mode (Gtk::SELECTION_SINGLE);
131 group_display.set_reorderable (true);
132 group_display.set_headers_visible (true);
133 group_display.set_rules_hint (true);
135 /* name is directly editable */
137 CellRendererText* name_cell = dynamic_cast<CellRendererText*>(group_display.get_column_cell_renderer (0));
138 name_cell->property_editable() = true;
139 name_cell->signal_edited().connect (sigc::mem_fun (*this, &Mixer_UI::route_group_name_edit));
141 /* use checkbox for the active column */
143 CellRendererToggle* active_cell = dynamic_cast<CellRendererToggle*>(group_display.get_column_cell_renderer (1));
144 active_cell->property_activatable() = true;
145 active_cell->property_radio() = false;
147 group_model->signal_row_changed().connect (sigc::mem_fun (*this, &Mixer_UI::route_group_row_change));
149 group_display.signal_button_press_event().connect (sigc::mem_fun (*this, &Mixer_UI::group_display_button_press), false);
151 group_display_scroller.add (group_display);
152 group_display_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
154 HBox* route_group_display_button_box = manage (new HBox());
156 Button* route_group_add_button = manage (new Button ());
157 Button* route_group_remove_button = manage (new Button ());
159 Widget* w;
161 w = manage (new Image (Stock::ADD, ICON_SIZE_BUTTON));
162 w->show();
163 route_group_add_button->add (*w);
165 w = manage (new Image (Stock::REMOVE, ICON_SIZE_BUTTON));
166 w->show();
167 route_group_remove_button->add (*w);
169 route_group_display_button_box->set_homogeneous (true);
171 route_group_add_button->signal_clicked().connect (sigc::mem_fun (*this, &Mixer_UI::new_route_group));
172 route_group_remove_button->signal_clicked().connect (sigc::mem_fun (*this, &Mixer_UI::remove_selected_route_group));
174 route_group_display_button_box->add (*route_group_remove_button);
175 route_group_display_button_box->add (*route_group_add_button);
177 group_display_vbox.pack_start (group_display_scroller, true, true);
178 group_display_vbox.pack_start (*route_group_display_button_box, false, false);
180 track_display_frame.set_name("BaseFrame");
181 track_display_frame.set_shadow_type (Gtk::SHADOW_IN);
182 track_display_frame.add(track_display_scroller);
184 group_display_frame.set_name ("BaseFrame");
185 group_display_frame.set_shadow_type (Gtk::SHADOW_IN);
186 group_display_frame.add (group_display_vbox);
188 rhs_pane1.pack1 (track_display_frame);
189 rhs_pane1.pack2 (group_display_frame);
191 list_vpacker.pack_start (rhs_pane1, true, true);
193 global_hpacker.pack_start (scroller, true, true);
194 #ifdef GTKOSX
195 /* current gtk-quartz has dirty updates on borders like this one */
196 global_hpacker.pack_start (out_packer, false, false, 0);
197 #else
198 global_hpacker.pack_start (out_packer, false, false, 12);
199 #endif
200 list_hpane.add1(list_vpacker);
201 list_hpane.add2(global_hpacker);
203 rhs_pane1.signal_size_allocate().connect (sigc::bind (sigc::mem_fun(*this, &Mixer_UI::pane_allocation_handler),
204 static_cast<Gtk::Paned*> (&rhs_pane1)));
205 list_hpane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun(*this, &Mixer_UI::pane_allocation_handler),
206 static_cast<Gtk::Paned*> (&list_hpane)));
208 global_vpacker.pack_start (list_hpane, true, true);
210 add (global_vpacker);
211 set_name ("MixerWindow");
213 WindowTitle title(Glib::get_application_name());
214 title += _("Mixer");
215 set_title (title.get_string());
217 set_wmclass (X_("ardour_mixer"), "Ardour");
219 add_accel_group (ActionManager::ui_manager->get_accel_group());
221 signal_delete_event().connect (sigc::mem_fun (*this, &Mixer_UI::hide_window));
222 add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
224 signal_configure_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::configure_handler));
226 _selection.RoutesChanged.connect (sigc::mem_fun(*this, &Mixer_UI::follow_strip_selection));
228 route_group_display_button_box->show();
229 route_group_add_button->show();
230 route_group_remove_button->show();
232 global_hpacker.show();
233 global_vpacker.show();
234 scroller.show();
235 scroller_base.show();
236 scroller_hpacker.show();
237 mixer_scroller_vpacker.show();
238 list_vpacker.show();
239 group_display_button_label.show();
240 group_display_button.show();
241 track_display_scroller.show();
242 group_display_scroller.show();
243 group_display_vbox.show();
244 track_display_frame.show();
245 group_display_frame.show();
246 rhs_pane1.show();
247 strip_packer.show();
248 out_packer.show();
249 list_hpane.show();
250 track_display.show();
251 group_display.show();
253 auto_rebinding = FALSE;
255 MixerStrip::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&Mixer_UI::remove_strip, this, _1), gui_context());
257 MonitorSection::setup_knob_images ();
259 #ifndef DEFER_PLUGIN_SELECTOR_LOAD
260 _plugin_selector = new PluginSelector (PluginManager::the_manager ());
261 #endif
264 Mixer_UI::~Mixer_UI ()
268 void
269 Mixer_UI::ensure_float (Window& win)
271 win.set_transient_for (*this);
274 void
275 Mixer_UI::show_window ()
277 present ();
278 if (!_visible) {
279 set_window_pos_and_size ();
281 /* now reset each strips width so the right widgets are shown */
282 MixerStrip* ms;
284 TreeModel::Children rows = track_model->children();
285 TreeModel::Children::iterator ri;
287 for (ri = rows.begin(); ri != rows.end(); ++ri) {
288 ms = (*ri)[track_columns.strip];
289 ms->set_width_enum (ms->get_width_enum (), ms->width_owner());
292 _visible = true;
295 bool
296 Mixer_UI::hide_window (GdkEventAny *ev)
298 get_window_pos_and_size ();
300 _visible = false;
301 return just_hide_it(ev, static_cast<Gtk::Window *>(this));
305 void
306 Mixer_UI::add_strip (RouteList& routes)
308 ENSURE_GUI_THREAD (*this, &Mixer_UI::add_strip, routes)
310 MixerStrip* strip;
312 no_track_list_redisplay = true;
313 strip_redisplay_does_not_sync_order_keys = true;
315 for (RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
316 boost::shared_ptr<Route> route = (*x);
318 if (route->is_hidden()) {
319 continue;
322 if (route->is_monitor()) {
323 if (!_monitor_section) {
324 _monitor_section = new MonitorSection (_session);
325 out_packer.pack_end (_monitor_section->tearoff(), false, false);
326 } else {
327 _monitor_section->set_session (_session);
330 _monitor_section->tearoff().show_all ();
332 XMLNode* mnode = ARDOUR_UI::instance()->tearoff_settings (X_("monitor-section"));
333 if (mnode) {
334 _monitor_section->tearoff().set_state (*mnode);
337 /* no regular strip shown for control out */
339 continue;
342 strip = new MixerStrip (*this, _session, route);
343 strips.push_back (strip);
345 Config->get_default_narrow_ms() ? _strip_width = Narrow : _strip_width = Wide;
347 if (strip->width_owner() != strip) {
348 strip->set_width_enum (_strip_width, this);
351 show_strip (strip);
353 TreeModel::Row row = *(track_model->append());
354 row[track_columns.text] = route->name();
355 row[track_columns.visible] = strip->marked_for_display();
356 row[track_columns.route] = route;
357 row[track_columns.strip] = strip;
359 if (route->order_key (N_("signal")) == -1) {
360 route->set_order_key (N_("signal"), track_model->children().size()-1);
363 route->PropertyChanged.connect (*this, invalidator (*this), ui_bind (&Mixer_UI::strip_property_changed, this, _1, strip), gui_context());
365 strip->WidthChanged.connect (sigc::mem_fun(*this, &Mixer_UI::strip_width_changed));
366 strip->signal_button_release_event().connect (sigc::bind (sigc::mem_fun(*this, &Mixer_UI::strip_button_release_event), strip));
369 no_track_list_redisplay = false;
371 redisplay_track_list ();
373 strip_redisplay_does_not_sync_order_keys = false;
376 void
377 Mixer_UI::remove_strip (MixerStrip* strip)
379 if (_session && _session->deletion_in_progress()) {
380 /* its all being taken care of */
381 return;
384 ENSURE_GUI_THREAD (*this, &Mixer_UI::remove_strip, strip);
386 cerr << "Mixer UI removing strip for " << strip << endl;
388 TreeModel::Children rows = track_model->children();
389 TreeModel::Children::iterator ri;
390 list<MixerStrip *>::iterator i;
392 if ((i = find (strips.begin(), strips.end(), strip)) != strips.end()) {
393 strips.erase (i);
396 strip_redisplay_does_not_sync_order_keys = true;
398 for (ri = rows.begin(); ri != rows.end(); ++ri) {
399 if ((*ri)[track_columns.strip] == strip) {
400 track_model->erase (ri);
401 break;
405 strip_redisplay_does_not_sync_order_keys = false;
408 void
409 Mixer_UI::sync_order_keys (string const & src)
411 TreeModel::Children rows = track_model->children();
412 TreeModel::Children::iterator ri;
414 if (src == N_("signal") || !_session || (_session->state_of_the_state() & (Session::Loading|Session::Deletion)) || rows.empty()) {
415 return;
418 std::map<int,int> keys;
420 bool changed = false;
422 unsigned order = 0;
423 for (ri = rows.begin(); ri != rows.end(); ++ri, ++order) {
424 boost::shared_ptr<Route> route = (*ri)[track_columns.route];
425 unsigned int old_key = order;
426 unsigned int new_key = route->order_key (N_("signal"));
428 keys[new_key] = old_key;
430 if (new_key != old_key) {
431 changed = true;
435 if (keys.size() != rows.size()) {
436 PBD::stacktrace (cerr, 20);
438 assert(keys.size() == rows.size());
440 // Remove any gaps in keys caused by automation children tracks
441 vector<int> neworder;
442 for (std::map<int,int>::const_iterator i = keys.begin(); i != keys.end(); ++i) {
443 neworder.push_back(i->second);
445 assert(neworder.size() == rows.size());
447 if (changed) {
448 strip_redisplay_does_not_reset_order_keys = true;
449 track_model->reorder (neworder);
450 strip_redisplay_does_not_reset_order_keys = false;
454 void
455 Mixer_UI::follow_strip_selection ()
457 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
458 (*i)->set_selected (_selection.selected ((*i)->route()));
462 bool
463 Mixer_UI::strip_button_release_event (GdkEventButton *ev, MixerStrip *strip)
465 if (ev->button == 1) {
467 /* this allows the user to click on the strip to terminate comment
468 editing. XXX it needs improving so that we don't select the strip
469 at the same time.
472 if (_selection.selected (strip->route())) {
473 _selection.remove (strip->route());
474 } else {
475 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
476 _selection.add (strip->route());
477 } else {
478 _selection.set (strip->route());
483 return true;
486 void
487 Mixer_UI::set_session (Session* sess)
489 SessionHandlePtr::set_session (sess);
491 if (_plugin_selector) {
492 _plugin_selector->set_session (_session);
495 _group_tabs->set_session (sess);
497 if (!_session) {
498 return;
501 XMLNode* node = ARDOUR_UI::instance()->mixer_settings();
502 set_state (*node);
504 WindowTitle title(_session->name());
505 title += _("Mixer");
506 title += Glib::get_application_name();
508 set_title (title.get_string());
510 initial_track_display ();
512 _session->RouteAdded.connect (_session_connections, invalidator (*this), ui_bind (&Mixer_UI::add_strip, this, _1), gui_context());
513 _session->route_group_added.connect (_session_connections, invalidator (*this), ui_bind (&Mixer_UI::add_route_group, this, _1), gui_context());
514 _session->route_group_removed.connect (_session_connections, invalidator (*this), boost::bind (&Mixer_UI::route_groups_changed, this), gui_context());
515 _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), ui_bind (&Mixer_UI::parameter_changed, this, _1), gui_context());
517 Config->ParameterChanged.connect (*this, invalidator (*this), ui_bind (&Mixer_UI::parameter_changed, this, _1), gui_context ());
519 route_groups_changed ();
521 if (_visible) {
522 show_window();
525 start_updating ();
528 void
529 Mixer_UI::session_going_away ()
531 ENSURE_GUI_THREAD (*this, &Mixer_UI::session_going_away)
533 group_model->clear ();
534 _selection.clear ();
535 track_model->clear ();
537 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
538 delete (*i);
541 if (_monitor_section) {
542 _monitor_section->tearoff().hide_visible ();
545 strips.clear ();
547 WindowTitle title(Glib::get_application_name());
548 title += _("Mixer");
549 set_title (title.get_string());
551 stop_updating ();
553 SessionHandlePtr::session_going_away ();
556 void
557 Mixer_UI::show_strip (MixerStrip* ms)
559 TreeModel::Children rows = track_model->children();
560 TreeModel::Children::iterator i;
562 for (i = rows.begin(); i != rows.end(); ++i) {
564 MixerStrip* strip = (*i)[track_columns.strip];
565 if (strip == ms) {
566 (*i)[track_columns.visible] = true;
567 break;
572 void
573 Mixer_UI::hide_strip (MixerStrip* ms)
575 TreeModel::Children rows = track_model->children();
576 TreeModel::Children::iterator i;
578 for (i = rows.begin(); i != rows.end(); ++i) {
580 MixerStrip* strip = (*i)[track_columns.strip];
581 if (strip == ms) {
582 (*i)[track_columns.visible] = false;
583 break;
588 gint
589 Mixer_UI::start_updating ()
591 fast_screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (sigc::mem_fun(*this, &Mixer_UI::fast_update_strips));
592 return 0;
595 gint
596 Mixer_UI::stop_updating ()
598 fast_screen_update_connection.disconnect();
599 return 0;
602 void
603 Mixer_UI::fast_update_strips ()
605 if (is_mapped () && _session) {
606 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
607 (*i)->fast_update ();
612 void
613 Mixer_UI::set_all_strips_visibility (bool yn)
615 TreeModel::Children rows = track_model->children();
616 TreeModel::Children::iterator i;
618 no_track_list_redisplay = true;
620 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 (*i)[track_columns.visible] = yn;
636 no_track_list_redisplay = false;
637 redisplay_track_list ();
641 void
642 Mixer_UI::set_all_audio_visibility (int tracks, bool yn)
644 TreeModel::Children rows = track_model->children();
645 TreeModel::Children::iterator i;
647 no_track_list_redisplay = true;
649 for (i = rows.begin(); i != rows.end(); ++i) {
650 TreeModel::Row row = (*i);
651 MixerStrip* strip = row[track_columns.strip];
653 if (strip == 0) {
654 continue;
657 if (strip->route()->is_master() || strip->route()->is_monitor()) {
658 continue;
661 boost::shared_ptr<AudioTrack> at = strip->audio_track();
663 switch (tracks) {
664 case 0:
665 (*i)[track_columns.visible] = yn;
666 break;
668 case 1:
669 if (at) { /* track */
670 (*i)[track_columns.visible] = yn;
672 break;
674 case 2:
675 if (!at) { /* bus */
676 (*i)[track_columns.visible] = yn;
678 break;
682 no_track_list_redisplay = false;
683 redisplay_track_list ();
686 void
687 Mixer_UI::hide_all_routes ()
689 set_all_strips_visibility (false);
692 void
693 Mixer_UI::show_all_routes ()
695 set_all_strips_visibility (true);
698 void
699 Mixer_UI::show_all_audiobus ()
701 set_all_audio_visibility (2, true);
703 void
704 Mixer_UI::hide_all_audiobus ()
706 set_all_audio_visibility (2, false);
709 void
710 Mixer_UI::show_all_audiotracks()
712 set_all_audio_visibility (1, true);
714 void
715 Mixer_UI::hide_all_audiotracks ()
717 set_all_audio_visibility (1, false);
720 void
721 Mixer_UI::track_list_reorder (const TreeModel::Path&, const TreeModel::iterator&, int* /*new_order*/)
723 strip_redisplay_does_not_sync_order_keys = true;
724 _session->set_remote_control_ids();
725 redisplay_track_list ();
726 strip_redisplay_does_not_sync_order_keys = false;
729 void
730 Mixer_UI::track_list_change (const Gtk::TreeModel::Path&, const Gtk::TreeModel::iterator&)
732 // never reset order keys because of a property change
733 strip_redisplay_does_not_reset_order_keys = true;
734 _session->set_remote_control_ids();
735 redisplay_track_list ();
736 strip_redisplay_does_not_reset_order_keys = false;
739 void
740 Mixer_UI::track_list_delete (const Gtk::TreeModel::Path&)
742 /* this could require an order sync */
743 if (_session && !_session->deletion_in_progress()) {
744 _session->set_remote_control_ids();
745 redisplay_track_list ();
749 void
750 Mixer_UI::redisplay_track_list ()
752 TreeModel::Children rows = track_model->children();
753 TreeModel::Children::iterator i;
754 long order;
756 if (no_track_list_redisplay) {
757 return;
760 for (order = 0, i = rows.begin(); i != rows.end(); ++i, ++order) {
761 MixerStrip* strip = (*i)[track_columns.strip];
763 if (strip == 0) {
764 /* we're in the middle of changing a row, don't worry */
765 continue;
768 bool visible = (*i)[track_columns.visible];
770 if (visible) {
771 strip->set_marked_for_display (true);
772 strip->route()->set_order_key (N_("signal"), order);
774 if (!strip_redisplay_does_not_reset_order_keys) {
775 strip->route()->set_order_key (N_("signal"), order);
778 if (strip->packed()) {
780 if (strip->route()->is_master() || strip->route()->is_monitor()) {
781 out_packer.reorder_child (*strip, -1);
782 } else {
783 strip_packer.reorder_child (*strip, -1); /* put at end */
786 } else {
788 if (strip->route()->is_master() || strip->route()->is_monitor()) {
789 out_packer.pack_start (*strip, false, false);
790 } else {
791 strip_packer.pack_start (*strip, false, false);
793 strip->set_packed (true);
794 //strip->show();
797 } else {
799 strip->set_marked_for_display (false);
801 if (strip->route()->is_master() || strip->route()->is_monitor()) {
802 /* do nothing, these cannot be hidden */
803 } else {
804 if (strip->packed()) {
805 strip_packer.remove (*strip);
806 strip->set_packed (false);
812 if (!strip_redisplay_does_not_reset_order_keys && !strip_redisplay_does_not_sync_order_keys) {
813 _session->sync_order_keys (N_("signal"));
816 // Resigc::bind all of the midi controls automatically
818 if (auto_rebinding)
819 auto_rebind_midi_controls ();
821 _group_tabs->set_dirty ();
824 void
825 Mixer_UI::strip_width_changed ()
827 _group_tabs->set_dirty ();
829 #ifdef GTKOSX
830 TreeModel::Children rows = track_model->children();
831 TreeModel::Children::iterator i;
832 long order;
834 for (order = 0, i = rows.begin(); i != rows.end(); ++i, ++order) {
835 MixerStrip* strip = (*i)[track_columns.strip];
837 if (strip == 0) {
838 continue;
841 bool visible = (*i)[track_columns.visible];
843 if (visible) {
844 strip->queue_draw();
847 #endif
851 void
852 Mixer_UI::set_auto_rebinding( bool val )
854 if( val == TRUE )
856 auto_rebinding = TRUE;
857 Session::AutoBindingOff();
859 else
861 auto_rebinding = FALSE;
862 Session::AutoBindingOn();
866 void
867 Mixer_UI::toggle_auto_rebinding()
869 if (auto_rebinding)
871 set_auto_rebinding( FALSE );
874 else
876 set_auto_rebinding( TRUE );
879 auto_rebind_midi_controls();
882 void
883 Mixer_UI::auto_rebind_midi_controls ()
885 TreeModel::Children rows = track_model->children();
886 TreeModel::Children::iterator i;
887 int pos;
889 // Create bindings for all visible strips and remove those that are not visible
890 pos = 1; // 0 is reserved for the master strip
891 for (i = rows.begin(); i != rows.end(); ++i) {
892 MixerStrip* strip = (*i)[track_columns.strip];
894 if ( (*i)[track_columns.visible] == true ) { // add bindings for
895 // make the actual binding
896 //cout<<"Auto Binding: Visible Strip Found: "<<strip->name()<<endl;
898 int controlValue = pos;
899 if( strip->route()->is_master() ) {
900 controlValue = 0;
902 else {
903 pos++;
906 PBD::Controllable::CreateBinding ( strip->solo_button->get_controllable().get(), controlValue, 0);
907 PBD::Controllable::CreateBinding ( strip->mute_button->get_controllable().get(), controlValue, 1);
909 if( strip->is_audio_track() ) {
910 PBD::Controllable::CreateBinding ( strip->rec_enable_button->get_controllable().get(), controlValue, 2);
913 PBD::Controllable::CreateBinding ( strip->gpm.get_controllable().get(), controlValue, 3);
914 PBD::Controllable::CreateBinding ( strip->panners.get_controllable().get(), controlValue, 4);
917 else { // Remove any existing binding
918 PBD::Controllable::DeleteBinding ( strip->solo_button->get_controllable().get() );
919 PBD::Controllable::DeleteBinding ( strip->mute_button->get_controllable().get() );
921 if( strip->is_audio_track() ) {
922 PBD::Controllable::DeleteBinding ( strip->rec_enable_button->get_controllable().get() );
925 PBD::Controllable::DeleteBinding ( strip->gpm.get_controllable().get() );
926 PBD::Controllable::DeleteBinding ( strip->panners.get_controllable().get() ); // This only takes the first panner if there are multiples...
929 } // for
933 struct SignalOrderRouteSorter {
934 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
935 /* use of ">" forces the correct sort order */
936 return a->order_key (N_("signal")) < b->order_key (N_("signal"));
940 void
941 Mixer_UI::initial_track_display ()
943 boost::shared_ptr<RouteList> routes = _session->get_routes();
944 RouteList copy (*routes);
945 SignalOrderRouteSorter sorter;
947 copy.sort (sorter);
949 no_track_list_redisplay = true;
951 track_model->clear ();
953 add_strip (copy);
955 no_track_list_redisplay = false;
957 redisplay_track_list ();
960 void
961 Mixer_UI::show_track_list_menu ()
963 if (track_menu == 0) {
964 build_track_menu ();
967 track_menu->popup (1, gtk_get_current_event_time());
970 bool
971 Mixer_UI::track_display_button_press (GdkEventButton* ev)
973 if (Keyboard::is_context_menu_event (ev)) {
974 show_track_list_menu ();
975 return true;
978 TreeIter iter;
979 TreeModel::Path path;
980 TreeViewColumn* column;
981 int cellx;
982 int celly;
984 if (!track_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
985 return false;
988 switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
989 case 0:
990 /* allow normal processing to occur */
991 return false;
993 case 1: /* visibility */
995 if ((iter = track_model->get_iter (path))) {
996 MixerStrip* strip = (*iter)[track_columns.strip];
997 if (strip) {
999 if (!strip->route()->is_master() && !strip->route()->is_monitor()) {
1000 bool visible = (*iter)[track_columns.visible];
1001 (*iter)[track_columns.visible] = !visible;
1003 #ifdef GTKOSX
1004 track_display.queue_draw();
1005 #endif
1008 return true;
1010 default:
1011 break;
1014 return false;
1018 void
1019 Mixer_UI::build_track_menu ()
1021 using namespace Menu_Helpers;
1022 using namespace Gtk;
1024 track_menu = new Menu;
1025 track_menu->set_name ("ArdourContextMenu");
1026 MenuList& items = track_menu->items();
1028 items.push_back (MenuElem (_("Show All"), sigc::mem_fun(*this, &Mixer_UI::show_all_routes)));
1029 items.push_back (MenuElem (_("Hide All"), sigc::mem_fun(*this, &Mixer_UI::hide_all_routes)));
1030 items.push_back (MenuElem (_("Show All Audio Tracks"), sigc::mem_fun(*this, &Mixer_UI::show_all_audiotracks)));
1031 items.push_back (MenuElem (_("Hide All Audio Tracks"), sigc::mem_fun(*this, &Mixer_UI::hide_all_audiotracks)));
1032 items.push_back (MenuElem (_("Show All Audio Busses"), sigc::mem_fun(*this, &Mixer_UI::show_all_audiobus)));
1033 items.push_back (MenuElem (_("Hide All Audio Busses"), sigc::mem_fun(*this, &Mixer_UI::hide_all_audiobus)));
1037 void
1038 Mixer_UI::strip_property_changed (const PropertyChange& what_changed, MixerStrip* mx)
1040 if (!what_changed.contains (ARDOUR::Properties::name)) {
1041 return;
1044 ENSURE_GUI_THREAD (*this, &Mixer_UI::strip_name_changed, what_changed, mx)
1046 TreeModel::Children rows = track_model->children();
1047 TreeModel::Children::iterator i;
1049 for (i = rows.begin(); i != rows.end(); ++i) {
1050 if ((*i)[track_columns.strip] == mx) {
1051 (*i)[track_columns.text] = mx->route()->name();
1052 return;
1056 error << _("track display list item for renamed strip not found!") << endmsg;
1060 void
1061 Mixer_UI::build_route_group_context_menu ()
1063 using namespace Gtk::Menu_Helpers;
1065 route_group_context_menu = new Menu;
1066 route_group_context_menu->set_name ("ArdourContextMenu");
1067 MenuList& items = route_group_context_menu->items();
1069 items.push_back (MenuElem (_("Activate All"), sigc::mem_fun(*this, &Mixer_UI::activate_all_route_groups)));
1070 items.push_back (MenuElem (_("Disable All"), sigc::mem_fun(*this, &Mixer_UI::disable_all_route_groups)));
1071 items.push_back (SeparatorElem());
1072 items.push_back (MenuElem (_("Add group"), sigc::mem_fun(*this, &Mixer_UI::new_route_group)));
1076 bool
1077 Mixer_UI::group_display_button_press (GdkEventButton* ev)
1079 if (Keyboard::is_context_menu_event (ev)) {
1080 if (route_group_context_menu == 0) {
1081 build_route_group_context_menu ();
1083 route_group_context_menu->popup (1, ev->time);
1084 return true;
1088 RouteGroup* group;
1089 TreeIter iter;
1090 TreeModel::Path path;
1091 TreeViewColumn* column;
1092 int cellx;
1093 int celly;
1095 if (!group_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
1096 return false;
1099 switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
1100 case 0:
1101 if (Keyboard::is_edit_event (ev)) {
1102 if ((iter = group_model->get_iter (path))) {
1103 if ((group = (*iter)[group_columns.group]) != 0) {
1104 // edit_route_group (group);
1105 #ifdef GTKOSX
1106 group_display.queue_draw();
1107 #endif
1108 return true;
1113 break;
1115 case 1:
1116 if ((iter = group_model->get_iter (path))) {
1117 bool visible = (*iter)[group_columns.visible];
1118 (*iter)[group_columns.visible] = !visible;
1119 #ifdef GTKOSX
1120 group_display.queue_draw();
1121 #endif
1122 return true;
1124 break;
1126 default:
1127 break;
1130 return false;
1133 void
1134 Mixer_UI::activate_all_route_groups ()
1136 _session->foreach_route_group (sigc::bind (sigc::mem_fun (*this, &Mixer_UI::set_route_group_activation), true));
1139 void
1140 Mixer_UI::disable_all_route_groups ()
1142 _session->foreach_route_group (sigc::bind (sigc::mem_fun (*this, &Mixer_UI::set_route_group_activation), false));
1145 void
1146 Mixer_UI::route_groups_changed ()
1148 ENSURE_GUI_THREAD (*this, &Mixer_UI::route_groups_changed)
1150 /* just rebuild the while thing */
1152 group_model->clear ();
1155 TreeModel::Row row;
1156 row = *(group_model->append());
1157 row[group_columns.visible] = true;
1158 row[group_columns.text] = (_("-all-"));
1159 row[group_columns.group] = 0;
1162 _session->foreach_route_group (sigc::mem_fun (*this, &Mixer_UI::add_route_group));
1165 void
1166 Mixer_UI::new_route_group ()
1168 PropertyList plist;
1170 plist.add (Properties::active, true);
1171 plist.add (Properties::gain, true);
1172 plist.add (Properties::mute, true);
1173 plist.add (Properties::solo, true);
1175 RouteGroup* g = new RouteGroup (*_session, "");
1176 g->set_properties (plist);
1178 _session->add_route_group (g);
1181 void
1182 Mixer_UI::remove_selected_route_group ()
1184 Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
1185 TreeView::Selection::ListHandle_Path rows = selection->get_selected_rows ();
1187 if (rows.empty()) {
1188 return;
1191 TreeView::Selection::ListHandle_Path::iterator i = rows.begin();
1192 TreeIter iter;
1194 /* selection mode is single, so rows.begin() is it */
1196 if ((iter = group_model->get_iter (*i))) {
1198 RouteGroup* rg = (*iter)[group_columns.group];
1200 if (rg) {
1201 _session->remove_route_group (*rg);
1206 void
1207 Mixer_UI::route_group_property_changed (RouteGroup* group, const PropertyChange& change)
1209 if (in_group_row_change) {
1210 return;
1213 /* force an update of any mixer strips that are using this group,
1214 otherwise mix group names don't change in mixer strips
1217 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1218 if ((*i)->route_group() == group) {
1219 (*i)->route_group_changed();
1223 TreeModel::iterator i;
1224 TreeModel::Children rows = group_model->children();
1225 Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
1227 in_group_row_change = true;
1229 for (i = rows.begin(); i != rows.end(); ++i) {
1230 if ((*i)[group_columns.group] == group) {
1231 (*i)[group_columns.visible] = !group->is_hidden ();
1232 (*i)[group_columns.text] = group->name ();
1233 break;
1237 in_group_row_change = false;
1239 if (change.contains (Properties::name)) {
1240 _group_tabs->set_dirty ();
1244 void
1245 Mixer_UI::route_group_name_edit (const Glib::ustring& path, const Glib::ustring& new_text)
1247 RouteGroup* group;
1248 TreeIter iter;
1250 if ((iter = group_model->get_iter (path))) {
1252 if ((group = (*iter)[group_columns.group]) == 0) {
1253 return;
1256 if (new_text != group->name()) {
1257 group->set_name (new_text);
1262 void
1263 Mixer_UI::route_group_row_change (const Gtk::TreeModel::Path&, const Gtk::TreeModel::iterator& iter)
1265 RouteGroup* group;
1267 if (in_group_row_change) {
1268 return;
1271 if ((group = (*iter)[group_columns.group]) == 0) {
1272 return;
1275 if ((*iter)[group_columns.visible]) {
1276 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1277 if ((*i)->route_group() == group) {
1278 show_strip (*i);
1281 } else {
1282 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1283 if ((*i)->route_group() == group) {
1284 hide_strip (*i);
1289 Glib::ustring name = (*iter)[group_columns.text];
1291 if (name != group->name()) {
1292 group->set_name (name);
1297 void
1298 Mixer_UI::add_route_group (RouteGroup* group)
1300 ENSURE_GUI_THREAD (*this, &Mixer_UI::add_route_group, group)
1301 bool focus = false;
1303 in_group_row_change = true;
1305 TreeModel::Row row = *(group_model->append());
1306 row[group_columns.visible] = true;
1307 row[group_columns.group] = group;
1308 if (!group->name().empty()) {
1309 row[group_columns.text] = group->name();
1310 } else {
1311 row[group_columns.text] = _("unnamed");
1312 focus = true;
1315 group->PropertyChanged.connect (*this, invalidator (*this), ui_bind (&Mixer_UI::route_group_property_changed, this, group, _1), gui_context());
1317 if (focus) {
1318 TreeViewColumn* col = group_display.get_column (0);
1319 CellRendererText* name_cell = dynamic_cast<CellRendererText*>(group_display.get_column_cell_renderer (0));
1320 group_display.set_cursor (group_model->get_path (row), *col, *name_cell, true);
1323 in_group_row_change = false;
1326 bool
1327 Mixer_UI::strip_scroller_button_release (GdkEventButton* ev)
1329 using namespace Menu_Helpers;
1331 if (Keyboard::is_context_menu_event (ev)) {
1332 ARDOUR_UI::instance()->add_route (this);
1333 return true;
1336 return false;
1339 void
1340 Mixer_UI::set_strip_width (Width w)
1342 _strip_width = w;
1344 for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
1345 (*i)->set_width_enum (w, this);
1349 void
1350 Mixer_UI::set_window_pos_and_size ()
1352 resize (m_width, m_height);
1353 move (m_root_x, m_root_y);
1356 void
1357 Mixer_UI::get_window_pos_and_size ()
1359 get_position(m_root_x, m_root_y);
1360 get_size(m_width, m_height);
1364 Mixer_UI::set_state (const XMLNode& node)
1366 const XMLProperty* prop;
1367 XMLNode* geometry;
1369 m_width = default_width;
1370 m_height = default_height;
1371 m_root_x = 1;
1372 m_root_y = 1;
1374 if ((geometry = find_named_node (node, "geometry")) != 0) {
1376 XMLProperty* prop;
1378 if ((prop = geometry->property("x_size")) == 0) {
1379 prop = geometry->property ("x-size");
1381 if (prop) {
1382 m_width = atoi(prop->value());
1384 if ((prop = geometry->property("y_size")) == 0) {
1385 prop = geometry->property ("y-size");
1387 if (prop) {
1388 m_height = atoi(prop->value());
1391 if ((prop = geometry->property ("x_pos")) == 0) {
1392 prop = geometry->property ("x-pos");
1394 if (prop) {
1395 m_root_x = atoi (prop->value());
1398 if ((prop = geometry->property ("y_pos")) == 0) {
1399 prop = geometry->property ("y-pos");
1401 if (prop) {
1402 m_root_y = atoi (prop->value());
1406 set_window_pos_and_size ();
1408 if ((prop = node.property ("narrow-strips"))) {
1409 if (string_is_affirmative (prop->value())) {
1410 set_strip_width (Narrow);
1411 } else {
1412 set_strip_width (Wide);
1416 if ((prop = node.property ("show-mixer"))) {
1417 if (string_is_affirmative (prop->value())) {
1418 _visible = true;
1422 return 0;
1425 XMLNode&
1426 Mixer_UI::get_state (void)
1428 XMLNode* node = new XMLNode ("Mixer");
1430 if (is_realized()) {
1431 Glib::RefPtr<Gdk::Window> win = get_window();
1433 get_window_pos_and_size ();
1435 XMLNode* geometry = new XMLNode ("geometry");
1436 char buf[32];
1437 snprintf(buf, sizeof(buf), "%d", m_width);
1438 geometry->add_property(X_("x_size"), string(buf));
1439 snprintf(buf, sizeof(buf), "%d", m_height);
1440 geometry->add_property(X_("y_size"), string(buf));
1441 snprintf(buf, sizeof(buf), "%d", m_root_x);
1442 geometry->add_property(X_("x_pos"), string(buf));
1443 snprintf(buf, sizeof(buf), "%d", m_root_y);
1444 geometry->add_property(X_("y_pos"), string(buf));
1446 // written only for compatibility, they are not used.
1447 snprintf(buf, sizeof(buf), "%d", 0);
1448 geometry->add_property(X_("x_off"), string(buf));
1449 snprintf(buf, sizeof(buf), "%d", 0);
1450 geometry->add_property(X_("y_off"), string(buf));
1452 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&rhs_pane1)->gobj()));
1453 geometry->add_property(X_("mixer_rhs_pane1_pos"), string(buf));
1454 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&list_hpane)->gobj()));
1455 geometry->add_property(X_("mixer_list_hpane_pos"), string(buf));
1457 node->add_child_nocopy (*geometry);
1460 node->add_property ("narrow-strips", _strip_width == Narrow ? "yes" : "no");
1462 node->add_property ("show-mixer", _visible ? "yes" : "no");
1464 return *node;
1468 void
1469 Mixer_UI::pane_allocation_handler (Allocation&, Gtk::Paned* which)
1471 int pos;
1472 XMLProperty* prop = 0;
1473 char buf[32];
1474 XMLNode* node = ARDOUR_UI::instance()->mixer_settings();
1475 XMLNode* geometry;
1476 int width, height;
1477 static int32_t done[3] = { 0, 0, 0 };
1479 width = default_width;
1480 height = default_height;
1482 if ((geometry = find_named_node (*node, "geometry")) != 0) {
1485 if ((prop = geometry->property ("x_size")) == 0) {
1486 prop = geometry->property ("x-size");
1488 if (prop) {
1489 width = atoi (prop->value());
1491 if ((prop = geometry->property ("y_size")) == 0) {
1492 prop = geometry->property ("y-size");
1494 if (prop) {
1495 height = atoi (prop->value());
1499 if (which == static_cast<Gtk::Paned*> (&rhs_pane1)) {
1501 if (done[0]) {
1502 return;
1505 if (!geometry || (prop = geometry->property("mixer-rhs-pane1-pos")) == 0) {
1506 pos = height / 3;
1507 snprintf (buf, sizeof(buf), "%d", pos);
1508 } else {
1509 pos = atoi (prop->value());
1512 if ((done[0] = GTK_WIDGET(rhs_pane1.gobj())->allocation.height > pos)) {
1513 rhs_pane1.set_position (pos);
1516 } else if (which == static_cast<Gtk::Paned*> (&list_hpane)) {
1518 if (done[2]) {
1519 return;
1522 if (!geometry || (prop = geometry->property("mixer-list-hpane-pos")) == 0) {
1523 pos = 75;
1524 snprintf (buf, sizeof(buf), "%d", pos);
1525 } else {
1526 pos = atoi (prop->value());
1529 if ((done[2] = GTK_WIDGET(list_hpane.gobj())->allocation.width > pos)) {
1530 list_hpane.set_position (pos);
1534 void
1535 Mixer_UI::scroll_left ()
1537 Adjustment* adj = scroller.get_hscrollbar()->get_adjustment();
1538 /* stupid GTK: can't rely on clamping across versions */
1539 scroller.get_hscrollbar()->set_value (max (adj->get_lower(), adj->get_value() - adj->get_step_increment()));
1542 void
1543 Mixer_UI::scroll_right ()
1545 Adjustment* adj = scroller.get_hscrollbar()->get_adjustment();
1546 /* stupid GTK: can't rely on clamping across versions */
1547 scroller.get_hscrollbar()->set_value (min (adj->get_upper(), adj->get_value() + adj->get_step_increment()));
1550 bool
1551 Mixer_UI::on_key_press_event (GdkEventKey* ev)
1553 switch (ev->keyval) {
1554 case GDK_Left:
1555 scroll_left ();
1556 return true;
1558 case GDK_Right:
1559 scroll_right ();
1560 return true;
1562 default:
1563 break;
1566 return key_press_focus_accelerator_handler (*this, ev);
1569 bool
1570 Mixer_UI::on_key_release_event (GdkEventKey* ev)
1572 return Gtk::Window::on_key_release_event (ev);
1573 // return key_press_focus_accelerator_handler (*this, ev);
1577 bool
1578 Mixer_UI::on_scroll_event (GdkEventScroll* ev)
1580 switch (ev->direction) {
1581 case GDK_SCROLL_LEFT:
1582 scroll_left ();
1583 return true;
1584 case GDK_SCROLL_UP:
1585 if (ev->state & Keyboard::TertiaryModifier) {
1586 scroll_left ();
1587 return true;
1589 return false;
1591 case GDK_SCROLL_RIGHT:
1592 scroll_right ();
1593 return true;
1595 case GDK_SCROLL_DOWN:
1596 if (ev->state & Keyboard::TertiaryModifier) {
1597 scroll_right ();
1598 return true;
1600 return false;
1603 return false;
1607 void
1608 Mixer_UI::parameter_changed (string const & p)
1610 if (p == "show-group-tabs") {
1611 bool const s = _session->config.get_show_group_tabs ();
1612 if (s) {
1613 _group_tabs->show ();
1614 } else {
1615 _group_tabs->hide ();
1617 } else if (p == "default-narrow_ms") {
1618 bool const s = Config->get_default_narrow_ms ();
1619 for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
1620 (*i)->set_width_enum (s ? Narrow : Wide, this);
1625 void
1626 Mixer_UI::set_route_group_activation (RouteGroup* g, bool a)
1628 g->set_active (a, this);
1631 PluginSelector*
1632 Mixer_UI::plugin_selector()
1634 #ifdef DEFER_PLUGIN_SELECTOR_LOAD
1635 if (!_plugin_selector)
1636 _plugin_selector = new PluginSelector (PluginManager::the_manager ());
1637 #endif
1639 return _plugin_selector;