colinf's patch to make the cursor be the dbl vertical arrow when over the track resiz...
[ardour2.git] / gtk2_ardour / mixer_ui.cc
blob59bba9aea0bdaa67e872a250630de74d7fcf7bb4
1 /*
2 Copyright (C) 2000-2004 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 #include <algorithm>
21 #include <sigc++/bind.h>
23 #include <gtkmm/accelmap.h>
25 #include <pbd/convert.h>
26 #include <pbd/stacktrace.h>
27 #include <glibmm/thread.h>
29 #include <gtkmm2ext/gtk_ui.h>
30 #include <gtkmm2ext/utils.h>
31 #include <gtkmm2ext/stop_signal.h>
32 #include <gtkmm2ext/window_title.h>
34 #include <ardour/session.h>
35 #include <ardour/audio_track.h>
36 #include <ardour/session_route.h>
37 #include <ardour/audio_diskstream.h>
38 #include <ardour/plugin_manager.h>
40 #include "keyboard.h"
41 #include "mixer_ui.h"
42 #include "mixer_strip.h"
43 #include "plugin_selector.h"
44 #include "ardour_ui.h"
45 #include "prompter.h"
46 #include "utils.h"
47 #include "actions.h"
48 #include "gui_thread.h"
50 #include "i18n.h"
52 using namespace ARDOUR;
53 using namespace PBD;
54 using namespace Gtk;
55 using namespace Glib;
56 using namespace Gtkmm2ext;
57 using namespace sigc;
58 using namespace std;
60 using PBD::atoi;
62 Mixer_UI::Mixer_UI ()
63 : Window (Gtk::WINDOW_TOPLEVEL)
65 session = 0;
66 _strip_width = Config->get_default_narrow_ms() ? Narrow : Wide;
67 track_menu = 0;
68 mix_group_context_menu = 0;
69 no_track_list_redisplay = false;
70 in_group_row_change = false;
71 _visible = false;
72 strip_redisplay_does_not_reset_order_keys = false;
73 strip_redisplay_does_not_sync_order_keys = false;
75 Route::SyncOrderKeys.connect (mem_fun (*this, &Mixer_UI::sync_order_keys));
77 scroller_base.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
78 scroller_base.set_name ("MixerWindow");
79 scroller_base.signal_button_release_event().connect (mem_fun(*this, &Mixer_UI::strip_scroller_button_release));
80 // add as last item of strip packer
81 strip_packer.pack_end (scroller_base, true, true);
83 scroller.add (strip_packer);
84 scroller.set_policy (Gtk::POLICY_ALWAYS, Gtk::POLICY_AUTOMATIC);
86 track_model = ListStore::create (track_columns);
87 track_display.set_model (track_model);
88 track_display.append_column (_("Strips"), track_columns.text);
89 track_display.append_column (_("Show"), track_columns.visible);
90 track_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0));
91 track_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
92 track_display.get_column (0)->set_expand(true);
93 track_display.get_column (1)->set_expand(false);
94 track_display.set_name (X_("MixerTrackDisplayList"));
95 track_display.get_selection()->set_mode (Gtk::SELECTION_NONE);
96 track_display.set_reorderable (true);
97 track_display.set_headers_visible (true);
99 track_model->signal_row_deleted().connect (mem_fun (*this, &Mixer_UI::track_list_delete));
100 track_model->signal_row_changed().connect (mem_fun (*this, &Mixer_UI::track_list_change));
101 track_model->signal_rows_reordered().connect (mem_fun (*this, &Mixer_UI::track_list_reorder));
103 CellRendererToggle* track_list_visible_cell = dynamic_cast<CellRendererToggle*>(track_display.get_column_cell_renderer (1));
104 track_list_visible_cell->property_activatable() = true;
105 track_list_visible_cell->property_radio() = false;
107 track_display.signal_button_press_event().connect (mem_fun (*this, &Mixer_UI::track_display_button_press), false);
109 track_display_scroller.add (track_display);
110 track_display_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
112 group_model = ListStore::create (group_columns);
113 group_display.set_model (group_model);
114 group_display.append_column (_("Group"), group_columns.text);
115 group_display.append_column (_("Active"), group_columns.active);
116 group_display.append_column (_("Show"), group_columns.visible);
117 group_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0));
118 group_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
119 group_display.get_column (2)->set_data (X_("colnum"), GUINT_TO_POINTER(2));
120 group_display.get_column (0)->set_expand(true);
121 group_display.get_column (1)->set_expand(false);
122 group_display.get_column (2)->set_expand(false);
123 group_display.set_name ("MixerGroupList");
124 group_display.get_selection()->set_mode (Gtk::SELECTION_SINGLE);
125 group_display.set_reorderable (true);
126 group_display.set_headers_visible (true);
127 group_display.set_rules_hint (true);
129 /* name is directly editable */
131 CellRendererText* name_cell = dynamic_cast<CellRendererText*>(group_display.get_column_cell_renderer (0));
132 name_cell->property_editable() = true;
133 name_cell->signal_edited().connect (mem_fun (*this, &Mixer_UI::mix_group_name_edit));
135 /* use checkbox for the active column */
137 CellRendererToggle* active_cell = dynamic_cast<CellRendererToggle*>(group_display.get_column_cell_renderer (1));
138 active_cell->property_activatable() = true;
139 active_cell->property_radio() = false;
141 /* use checkbox for the visible column */
143 active_cell = dynamic_cast<CellRendererToggle*>(group_display.get_column_cell_renderer (2));
144 active_cell->property_activatable() = true;
145 active_cell->property_radio() = false;
147 group_model->signal_row_changed().connect (mem_fun (*this, &Mixer_UI::mix_group_row_change));
149 group_display.signal_button_press_event().connect (mem_fun (*this, &Mixer_UI::group_display_button_press), false);
151 group_display_scroller.add (group_display);
152 group_display_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
154 HBox* mix_group_display_button_box = manage (new HBox());
156 Button* mix_group_add_button = manage (new Button ());
157 Button* mix_group_remove_button = manage (new Button ());
159 Widget* w;
161 w = manage (new Image (Stock::ADD, ICON_SIZE_BUTTON));
162 w->show();
163 mix_group_add_button->add (*w);
165 w = manage (new Image (Stock::REMOVE, ICON_SIZE_BUTTON));
166 w->show();
167 mix_group_remove_button->add (*w);
169 mix_group_display_button_box->set_homogeneous (true);
171 mix_group_add_button->signal_clicked().connect (mem_fun (*this, &Mixer_UI::new_mix_group));
172 mix_group_remove_button->signal_clicked().connect (mem_fun (*this, &Mixer_UI::remove_selected_mix_group));
174 mix_group_display_button_box->add (*mix_group_remove_button);
175 mix_group_display_button_box->add (*mix_group_add_button);
177 group_display_vbox.pack_start (group_display_scroller, true, true);
178 group_display_vbox.pack_start (*mix_group_display_button_box, false, false);
180 track_display_frame.set_name("BaseFrame");
181 track_display_frame.set_shadow_type (Gtk::SHADOW_IN);
182 track_display_frame.add(track_display_scroller);
184 group_display_frame.set_name ("BaseFrame");
185 group_display_frame.set_shadow_type (Gtk::SHADOW_IN);
186 group_display_frame.add (group_display_vbox);
188 rhs_pane1.pack1 (track_display_frame);
189 rhs_pane1.pack2 (group_display_frame);
191 list_vpacker.pack_start (rhs_pane1, true, true);
193 global_hpacker.pack_start (scroller, true, true);
194 #ifdef GTKOSX
195 /* current gtk-quartz has dirty updates on borders like this one */
196 global_hpacker.pack_start (out_packer, false, false, 0);
197 #else
198 global_hpacker.pack_start (out_packer, false, false, 12);
199 #endif
200 list_hpane.add1(list_vpacker);
201 list_hpane.add2(global_hpacker);
203 rhs_pane1.signal_size_allocate().connect (bind (mem_fun(*this, &Mixer_UI::pane_allocation_handler),
204 static_cast<Gtk::Paned*> (&rhs_pane1)));
205 list_hpane.signal_size_allocate().connect (bind (mem_fun(*this, &Mixer_UI::pane_allocation_handler),
206 static_cast<Gtk::Paned*> (&list_hpane)));
208 global_vpacker.pack_start (list_hpane, true, true);
210 add (global_vpacker);
211 set_name ("MixerWindow");
213 WindowTitle title(Glib::get_application_name());
214 title += _("Mixer");
215 set_title (title.get_string());
217 set_wmclass (X_("ardour_mixer"), "Ardour");
219 add_accel_group (ActionManager::ui_manager->get_accel_group());
221 signal_delete_event().connect (mem_fun (*this, &Mixer_UI::hide_window));
222 add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
224 _plugin_selector = new PluginSelector (PluginManager::the_manager());
226 signal_configure_event().connect (mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::configure_handler));
228 _selection.RoutesChanged.connect (mem_fun(*this, &Mixer_UI::follow_strip_selection));
230 auto_rebinding = FALSE;
233 Mixer_UI::~Mixer_UI ()
237 void
238 Mixer_UI::ensure_float (Window& win)
240 win.set_transient_for (*this);
243 void
244 Mixer_UI::show_window ()
246 show_all ();
247 if (!_visible) {
248 set_window_pos_and_size ();
250 /* now reset each strips width so the right widgets are shown */
251 MixerStrip* ms;
253 TreeModel::Children rows = track_model->children();
254 TreeModel::Children::iterator ri;
256 for (ri = rows.begin(); ri != rows.end(); ++ri) {
257 ms = (*ri)[track_columns.strip];
258 ms->set_width (ms->get_width(), ms->width_owner());
261 _visible = true;
264 bool
265 Mixer_UI::hide_window (GdkEventAny *ev)
267 get_window_pos_and_size ();
269 _visible = false;
270 return just_hide_it(ev, static_cast<Gtk::Window *>(this));
274 void
275 Mixer_UI::add_strip (Session::RouteList& routes)
277 ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::add_strip), routes));
279 MixerStrip* strip;
281 no_track_list_redisplay = true;
282 strip_redisplay_does_not_sync_order_keys = true;
284 for (Session::RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
285 boost::shared_ptr<Route> route = (*x);
287 if (route->hidden()) {
288 return;
291 strip = new MixerStrip (*this, *session, route);
292 strips.push_back (strip);
294 Config->get_default_narrow_ms() ? _strip_width = Narrow : _strip_width = Wide;
296 if (strip->width_owner() != strip) {
297 strip->set_width (_strip_width, this);
300 show_strip (strip);
302 TreeModel::Row row = *(track_model->append());
303 row[track_columns.text] = route->name();
304 row[track_columns.visible] = strip->marked_for_display();
305 row[track_columns.route] = route;
306 row[track_columns.strip] = strip;
308 if (route->order_key (N_("signal")) == -1) {
309 route->set_order_key (N_("signal"), track_model->children().size()-1);
312 route->name_changed.connect (bind (mem_fun(*this, &Mixer_UI::strip_name_changed), strip));
314 strip->GoingAway.connect (bind (mem_fun(*this, &Mixer_UI::remove_strip), strip));
315 #ifdef GTKOSX
316 strip->WidthChanged.connect (mem_fun(*this, &Mixer_UI::queue_draw_all_strips));
317 #endif
318 strip->signal_button_release_event().connect (bind (mem_fun(*this, &Mixer_UI::strip_button_release_event), strip));
321 no_track_list_redisplay = false;
323 redisplay_track_list ();
325 strip_redisplay_does_not_sync_order_keys = false;
328 void
329 Mixer_UI::remove_strip (MixerStrip* strip)
331 ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::remove_strip), strip));
333 TreeModel::Children rows = track_model->children();
334 TreeModel::Children::iterator ri;
335 list<MixerStrip *>::iterator i;
337 if ((i = find (strips.begin(), strips.end(), strip)) != strips.end()) {
338 strips.erase (i);
341 strip_redisplay_does_not_sync_order_keys = true;
343 for (ri = rows.begin(); ri != rows.end(); ++ri) {
344 if ((*ri)[track_columns.strip] == strip) {
345 track_model->erase (ri);
346 break;
350 strip_redisplay_does_not_sync_order_keys = false;
353 const char*
354 Mixer_UI::get_order_key()
356 return X_("signal");
357 #if 0
358 if (Config->get_sync_all_route_ordering()) {
359 return X_("editor");
360 } else {
361 return X_("signal");
363 #endif
367 void
368 Mixer_UI::sync_order_keys (const char *src)
370 vector<int> neworder;
371 TreeModel::Children rows = track_model->children();
372 TreeModel::Children::iterator ri;
374 if ((strcmp (src, get_order_key()) == 0) || !session || (session->state_of_the_state() & Session::Loading) || rows.empty()) {
375 return;
378 for (ri = rows.begin(); ri != rows.end(); ++ri) {
379 neworder.push_back (0);
382 bool changed = false;
383 int order;
385 for (order = 0, ri = rows.begin(); ri != rows.end(); ++ri, ++order) {
386 boost::shared_ptr<Route> route = (*ri)[track_columns.route];
387 int old_key = order;
388 int new_key = route->order_key (get_order_key());
390 neworder[new_key] = old_key;
392 if (new_key != old_key) {
393 changed = true;
397 if (changed) {
398 strip_redisplay_does_not_reset_order_keys = true;
399 track_model->reorder (neworder);
400 strip_redisplay_does_not_reset_order_keys = false;
405 void
406 Mixer_UI::follow_strip_selection ()
408 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
409 (*i)->set_selected (_selection.selected ((*i)->route()));
413 bool
414 Mixer_UI::strip_button_release_event (GdkEventButton *ev, MixerStrip *strip)
416 if (ev->button == 1) {
418 /* this allows the user to click on the strip to terminate comment
419 editing. XXX it needs improving so that we don't select the strip
420 at the same time.
423 if (_selection.selected (strip->route())) {
424 _selection.remove (strip->route());
425 } else {
426 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
427 _selection.add (strip->route());
428 } else {
429 _selection.set (strip->route());
434 return true;
437 void
438 Mixer_UI::connect_to_session (Session* sess)
440 session = sess;
442 XMLNode* node = ARDOUR_UI::instance()->mixer_settings();
443 set_state (*node);
445 WindowTitle title(session->name());
446 title += _("Mixer");
447 title += Glib::get_application_name();
449 set_title (title.get_string());
451 initial_track_display ();
453 session->GoingAway.connect (mem_fun(*this, &Mixer_UI::disconnect_from_session));
454 session->RouteAdded.connect (mem_fun(*this, &Mixer_UI::add_strip));
455 session->mix_group_added.connect (mem_fun(*this, &Mixer_UI::add_mix_group));
456 session->mix_group_removed.connect (mem_fun(*this, &Mixer_UI::mix_groups_changed));
458 mix_groups_changed ();
460 _plugin_selector->set_session (session);
462 if (_visible) {
463 show_window();
466 start_updating ();
469 void
470 Mixer_UI::disconnect_from_session ()
472 ENSURE_GUI_THREAD(mem_fun(*this, &Mixer_UI::disconnect_from_session));
474 group_model->clear ();
475 _selection.clear ();
477 WindowTitle title(Glib::get_application_name());
478 title += _("Mixer");
479 set_title (title.get_string());
481 stop_updating ();
484 void
485 Mixer_UI::show_strip (MixerStrip* ms)
487 TreeModel::Children rows = track_model->children();
488 TreeModel::Children::iterator i;
490 for (i = rows.begin(); i != rows.end(); ++i) {
492 MixerStrip* strip = (*i)[track_columns.strip];
493 if (strip == ms) {
494 (*i)[track_columns.visible] = true;
495 break;
500 void
501 Mixer_UI::hide_strip (MixerStrip* ms)
503 TreeModel::Children rows = track_model->children();
504 TreeModel::Children::iterator i;
506 for (i = rows.begin(); i != rows.end(); ++i) {
508 MixerStrip* strip = (*i)[track_columns.strip];
509 if (strip == ms) {
510 (*i)[track_columns.visible] = false;
511 break;
516 gint
517 Mixer_UI::start_updating ()
519 fast_screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (mem_fun(*this, &Mixer_UI::fast_update_strips));
520 return 0;
523 gint
524 Mixer_UI::stop_updating ()
526 fast_screen_update_connection.disconnect();
527 return 0;
530 void
531 Mixer_UI::fast_update_strips ()
533 if (is_mapped () && session) {
534 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
535 (*i)->fast_update ();
540 void
541 Mixer_UI::set_all_strips_visibility (bool yn)
543 TreeModel::Children rows = track_model->children();
544 TreeModel::Children::iterator i;
546 no_track_list_redisplay = true;
548 for (i = rows.begin(); i != rows.end(); ++i) {
550 TreeModel::Row row = (*i);
551 MixerStrip* strip = row[track_columns.strip];
553 if (strip == 0) {
554 continue;
557 if (strip->route()->master() || strip->route()->control()) {
558 continue;
561 (*i)[track_columns.visible] = yn;
564 no_track_list_redisplay = false;
565 redisplay_track_list ();
569 void
570 Mixer_UI::set_all_audio_visibility (int tracks, bool yn)
572 TreeModel::Children rows = track_model->children();
573 TreeModel::Children::iterator i;
575 no_track_list_redisplay = true;
577 for (i = rows.begin(); i != rows.end(); ++i) {
578 TreeModel::Row row = (*i);
579 MixerStrip* strip = row[track_columns.strip];
581 if (strip == 0) {
582 continue;
585 if (strip->route()->master() || strip->route()->control()) {
586 continue;
589 boost::shared_ptr<AudioTrack> at = strip->audio_track();
591 switch (tracks) {
592 case 0:
593 (*i)[track_columns.visible] = yn;
594 break;
596 case 1:
597 if (at) { /* track */
598 (*i)[track_columns.visible] = yn;
600 break;
602 case 2:
603 if (!at) { /* bus */
604 (*i)[track_columns.visible] = yn;
606 break;
610 no_track_list_redisplay = false;
611 redisplay_track_list ();
614 void
615 Mixer_UI::hide_all_routes ()
617 set_all_strips_visibility (false);
620 void
621 Mixer_UI::show_all_routes ()
623 set_all_strips_visibility (true);
626 void
627 Mixer_UI::show_all_audiobus ()
629 set_all_audio_visibility (2, true);
631 void
632 Mixer_UI::hide_all_audiobus ()
634 set_all_audio_visibility (2, false);
637 void
638 Mixer_UI::show_all_audiotracks()
640 set_all_audio_visibility (1, true);
643 void
644 Mixer_UI::hide_all_audiotracks ()
646 set_all_audio_visibility (1, false);
649 void
650 Mixer_UI::show_tracks_with_regions_at_playhead ()
652 boost::shared_ptr<Session::RouteList> const regions = session->get_routes_with_regions_at (session->transport_frame ());
654 TreeModel::Children rows = track_model->children ();
655 for (TreeModel::Children::iterator i = rows.begin(); i != rows.end(); ++i) {
656 boost::shared_ptr<Route> route = (*i)[track_columns.route];
658 bool found = false;
659 for (Session::RouteList::iterator x = (*regions).begin(); x != (*regions).end(); ++x) {
660 if ((*x) == route)
661 found = true;
664 (*i)[track_columns.visible] = found;
667 strip_redisplay_does_not_sync_order_keys = true;
668 redisplay_track_list ();
669 strip_redisplay_does_not_sync_order_keys = false;
672 void
673 Mixer_UI::track_list_reorder (const TreeModel::Path& path, const TreeModel::iterator& iter, int* new_order)
675 strip_redisplay_does_not_sync_order_keys = true;
676 session->set_remote_control_ids();
677 redisplay_track_list ();
678 strip_redisplay_does_not_sync_order_keys = false;
681 void
682 Mixer_UI::track_list_change (const Gtk::TreeModel::Path& path,const Gtk::TreeModel::iterator& iter)
684 // never reset order keys because of a property change
685 strip_redisplay_does_not_reset_order_keys = true;
686 session->set_remote_control_ids();
687 redisplay_track_list ();
688 strip_redisplay_does_not_reset_order_keys = false;
691 void
692 Mixer_UI::track_list_delete (const Gtk::TreeModel::Path& path)
694 /* this could require an order sync */
695 session->set_remote_control_ids();
696 redisplay_track_list ();
699 void
700 Mixer_UI::redisplay_track_list ()
702 TreeModel::Children rows = track_model->children();
703 TreeModel::Children::iterator i;
704 long order;
706 if (no_track_list_redisplay) {
707 return;
710 for (order = 0, i = rows.begin(); i != rows.end(); ++i, ++order) {
711 MixerStrip* strip = (*i)[track_columns.strip];
713 if (strip == 0) {
714 /* we're in the middle of changing a row, don't worry */
715 continue;
718 bool visible = (*i)[track_columns.visible];
720 boost::shared_ptr<Route> route = (*i)[track_columns.route];
722 if (visible) {
723 strip->set_marked_for_display (true);
725 if (!strip_redisplay_does_not_reset_order_keys) {
726 strip->route()->set_order_key (get_order_key(), order);
729 if (strip->packed()) {
731 if (strip->route()->master() || strip->route()->control()) {
732 out_packer.reorder_child (*strip, -1);
733 } else {
734 strip_packer.reorder_child (*strip, -1); /* put at end */
737 } else {
739 if (strip->route()->master() || strip->route()->control()) {
740 out_packer.pack_start (*strip, false, false);
741 } else {
742 strip_packer.pack_start (*strip, false, false);
744 strip->set_packed (true);
745 strip->show_all ();
748 } else {
750 if (strip->route()->master() || strip->route()->control()) {
751 /* do nothing, these cannot be hidden */
752 } else {
753 if (strip->packed()) {
754 strip_packer.remove (*strip);
755 strip->set_packed (false);
761 if (!strip_redisplay_does_not_reset_order_keys && !strip_redisplay_does_not_sync_order_keys) {
762 session->sync_order_keys (get_order_key());
765 // Rebind all of the midi controls automatically
767 if (auto_rebinding) {
768 auto_rebind_midi_controls ();
772 #ifdef GTKOSX
773 void
774 Mixer_UI::queue_draw_all_strips ()
776 TreeModel::Children rows = track_model->children();
777 TreeModel::Children::iterator i;
778 long order;
780 for (order = 0, i = rows.begin(); i != rows.end(); ++i, ++order) {
781 MixerStrip* strip = (*i)[track_columns.strip];
783 if (strip == 0) {
784 continue;
787 bool visible = (*i)[track_columns.visible];
789 if (visible) {
790 strip->queue_draw();
794 #endif
796 void
797 Mixer_UI::set_auto_rebinding( bool val )
799 if( val == TRUE )
801 auto_rebinding = TRUE;
802 Session::AutoBindingOff();
804 else
806 auto_rebinding = FALSE;
807 Session::AutoBindingOn();
811 void
812 Mixer_UI::toggle_auto_rebinding()
814 set_auto_rebinding (!auto_rebinding);
815 auto_rebind_midi_controls();
818 void
819 Mixer_UI::auto_rebind_midi_controls ()
821 TreeModel::Children rows = track_model->children();
822 TreeModel::Children::iterator i;
823 int pos;
825 // Create bindings for all visible strips and remove those that are not visible
826 pos = 1; // 0 is reserved for the master strip
827 for (i = rows.begin(); i != rows.end(); ++i) {
828 MixerStrip* strip = (*i)[track_columns.strip];
830 if ( (*i)[track_columns.visible] == true ) { // add bindings for
831 // make the actual binding
832 //cout<<"Auto Binding: Visible Strip Found: "<<strip->name()<<endl;
834 int controlValue = pos;
835 if( strip->route()->master() ) {
836 controlValue = 0;
838 else {
839 pos++;
842 PBD::Controllable::CreateBinding ( strip->solo_button->get_controllable(), controlValue, 0);
843 PBD::Controllable::CreateBinding ( strip->mute_button->get_controllable(), controlValue, 1);
845 if( strip->is_audio_track() ) {
846 PBD::Controllable::CreateBinding ( strip->rec_enable_button->get_controllable(), controlValue, 2);
849 PBD::Controllable::CreateBinding ( &(strip->gpm.get_controllable()), controlValue, 3);
850 PBD::Controllable::CreateBinding ( strip->panners.get_controllable(), controlValue, 4);
853 else { // Remove any existing binding
854 PBD::Controllable::DeleteBinding ( strip->solo_button->get_controllable() );
855 PBD::Controllable::DeleteBinding ( strip->mute_button->get_controllable() );
857 if( strip->is_audio_track() ) {
858 PBD::Controllable::DeleteBinding ( strip->rec_enable_button->get_controllable() );
861 PBD::Controllable::DeleteBinding ( &(strip->gpm.get_controllable()) );
862 PBD::Controllable::DeleteBinding ( strip->panners.get_controllable() ); // This only takes the first panner if there are multiples...
865 } // for
870 struct SignalOrderRouteSorter {
871 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
872 /* use of ">" forces the correct sort order */
873 return a->order_key (Mixer_UI::get_order_key()) < b->order_key (Mixer_UI::get_order_key());
877 void
878 Mixer_UI::initial_track_display ()
880 boost::shared_ptr<Session::RouteList> routes = session->get_routes();
881 Session::RouteList copy (*routes);
882 SignalOrderRouteSorter sorter;
884 copy.sort (sorter);
886 no_track_list_redisplay = true;
888 track_model->clear ();
890 add_strip (copy);
892 no_track_list_redisplay = false;
894 redisplay_track_list ();
897 void
898 Mixer_UI::show_track_list_menu ()
900 if (track_menu == 0) {
901 build_track_menu ();
904 track_menu->popup (1, gtk_get_current_event_time());
907 bool
908 Mixer_UI::track_display_button_press (GdkEventButton* ev)
910 if (Keyboard::is_context_menu_event (ev)) {
911 show_track_list_menu ();
912 return true;
915 TreeIter iter;
916 TreeModel::Path path;
917 TreeViewColumn* column;
918 int cellx;
919 int celly;
921 if (!track_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
922 return false;
925 switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
926 case 0:
927 /* allow normal processing to occur */
928 return false;
930 case 1: /* visibility */
932 if ((iter = track_model->get_iter (path))) {
933 MixerStrip* strip = (*iter)[track_columns.strip];
934 if (strip) {
936 if (!strip->route()->master() && !strip->route()->control()) {
937 bool visible = (*iter)[track_columns.visible];
938 (*iter)[track_columns.visible] = !visible;
940 #ifdef GTKOSX
941 track_display.queue_draw();
942 #endif
945 return true;
947 default:
948 break;
951 return false;
955 void
956 Mixer_UI::build_track_menu ()
958 using namespace Menu_Helpers;
959 using namespace Gtk;
961 track_menu = new Menu;
962 track_menu->set_name ("ArdourContextMenu");
963 MenuList& items = track_menu->items();
965 items.push_back (MenuElem (_("Show All"), mem_fun(*this, &Mixer_UI::show_all_routes)));
966 items.push_back (MenuElem (_("Hide All"), mem_fun(*this, &Mixer_UI::hide_all_routes)));
967 items.push_back (MenuElem (_("Show All Audio Tracks"), mem_fun(*this, &Mixer_UI::show_all_audiotracks)));
968 items.push_back (MenuElem (_("Hide All Audio Tracks"), mem_fun(*this, &Mixer_UI::hide_all_audiotracks)));
969 items.push_back (MenuElem (_("Show All Audio Busses"), mem_fun(*this, &Mixer_UI::show_all_audiobus)));
970 items.push_back (MenuElem (_("Hide All Audio Busses"), mem_fun(*this, &Mixer_UI::hide_all_audiobus)));
971 items.push_back (MenuElem (_("Hide All Audio Busses"), mem_fun(*this, &Mixer_UI::hide_all_audiobus)));
972 items.push_back (MenuElem (_("Show Tracks With Regions Under Playhead"), mem_fun (*this, &Mixer_UI::show_tracks_with_regions_at_playhead)));
975 void
976 Mixer_UI::strip_name_changed (void* src, MixerStrip* mx)
978 ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::strip_name_changed), src, mx));
980 TreeModel::Children rows = track_model->children();
981 TreeModel::Children::iterator i;
983 for (i = rows.begin(); i != rows.end(); ++i) {
984 if ((*i)[track_columns.strip] == mx) {
985 (*i)[track_columns.text] = mx->route()->name();
986 return;
990 error << _("track display list item for renamed strip not found!") << endmsg;
994 void
995 Mixer_UI::build_mix_group_context_menu ()
997 using namespace Gtk::Menu_Helpers;
999 mix_group_context_menu = new Menu;
1000 mix_group_context_menu->set_name ("ArdourContextMenu");
1001 MenuList& items = mix_group_context_menu->items();
1003 items.push_back (MenuElem (_("Activate All"), mem_fun(*this, &Mixer_UI::activate_all_mix_groups)));
1004 items.push_back (MenuElem (_("Disable All"), mem_fun(*this, &Mixer_UI::disable_all_mix_groups)));
1005 items.push_back (SeparatorElem());
1006 items.push_back (MenuElem (_("Show All"), mem_fun(*this, &Mixer_UI::show_all_mix_groups)));
1007 items.push_back (MenuElem (_("Hide All"), mem_fun(*this, &Mixer_UI::hide_all_mix_groups)));
1008 items.push_back (SeparatorElem());
1009 items.push_back (MenuElem (_("Add group"), mem_fun(*this, &Mixer_UI::new_mix_group)));
1013 bool
1014 Mixer_UI::group_display_button_press (GdkEventButton* ev)
1016 if (Keyboard::is_context_menu_event (ev)) {
1017 if (mix_group_context_menu == 0) {
1018 build_mix_group_context_menu ();
1020 mix_group_context_menu->popup (1, ev->time);
1021 return true;
1025 RouteGroup* group;
1026 TreeIter iter;
1027 TreeModel::Path path;
1028 TreeViewColumn* column;
1029 int cellx;
1030 int celly;
1032 if (!group_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
1033 return false;
1036 switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
1037 case 0:
1038 if (Keyboard::is_edit_event (ev)) {
1039 if ((iter = group_model->get_iter (path))) {
1040 if ((group = (*iter)[group_columns.group]) != 0) {
1041 // edit_mix_group (group);
1042 #ifdef GTKOSX
1043 group_display.queue_draw();
1044 #endif
1045 return true;
1050 break;
1052 case 1:
1053 if ((iter = group_model->get_iter (path))) {
1054 bool active = (*iter)[group_columns.active];
1055 (*iter)[group_columns.active] = !active;
1056 #ifdef GTKOSX
1057 group_display.queue_draw();
1058 #endif
1059 return true;
1061 break;
1063 case 2:
1064 if ((iter = group_model->get_iter (path))) {
1065 bool visible = (*iter)[group_columns.visible];
1066 (*iter)[group_columns.visible] = !visible;
1067 #ifdef GTKOSX
1068 group_display.queue_draw();
1069 #endif
1070 return true;
1072 break;
1074 default:
1075 break;
1078 return false;
1081 void
1082 Mixer_UI::activate_all_mix_groups ()
1084 Gtk::TreeModel::Children children = group_model->children();
1085 for(Gtk::TreeModel::Children::iterator iter = children.begin(); iter != children.end(); ++iter) {
1086 (*iter)[group_columns.active] = true;
1092 void
1093 Mixer_UI::disable_all_mix_groups ()
1095 Gtk::TreeModel::Children children = group_model->children();
1096 for(Gtk::TreeModel::Children::iterator iter = children.begin(); iter != children.end(); ++iter) {
1097 (*iter)[group_columns.active] = false;
1101 void
1102 Mixer_UI::show_all_mix_groups ()
1104 Gtk::TreeModel::Children children = group_model->children();
1105 for(Gtk::TreeModel::Children::iterator iter = children.begin(); iter != children.end(); ++iter) {
1106 (*iter)[group_columns.visible] = true;
1110 void
1111 Mixer_UI::hide_all_mix_groups ()
1113 Gtk::TreeModel::Children children = group_model->children();
1114 for(Gtk::TreeModel::Children::iterator iter = children.begin(); iter != children.end(); ++iter) {
1115 (*iter)[group_columns.visible] = false;
1119 void
1120 Mixer_UI::mix_groups_changed ()
1122 ENSURE_GUI_THREAD (mem_fun (*this, &Mixer_UI::mix_groups_changed));
1124 /* just rebuild the while thing */
1126 group_model->clear ();
1128 session->foreach_mix_group (mem_fun (*this, &Mixer_UI::add_mix_group));
1131 void
1132 Mixer_UI::new_mix_group ()
1134 session->add_mix_group ("");
1137 void
1138 Mixer_UI::remove_selected_mix_group ()
1140 Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
1141 TreeView::Selection::ListHandle_Path rows = selection->get_selected_rows ();
1143 if (rows.empty()) {
1144 return;
1147 TreeView::Selection::ListHandle_Path::iterator i = rows.begin();
1148 TreeIter iter;
1150 /* selection mode is single, so rows.begin() is it */
1152 if ((iter = group_model->get_iter (*i))) {
1154 RouteGroup* rg = (*iter)[group_columns.group];
1156 if (rg) {
1157 session->remove_mix_group (*rg);
1162 void
1163 Mixer_UI::group_flags_changed (void* src, RouteGroup* group)
1165 if (in_group_row_change) {
1166 return;
1169 ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::group_flags_changed), src, group));
1171 /* force an update of any mixer strips that are using this group,
1172 otherwise mix group names don't change in mixer strips
1175 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1176 if ((*i)->mix_group() == group) {
1177 (*i)->mix_group_changed(0);
1181 TreeModel::iterator i;
1182 TreeModel::Children rows = group_model->children();
1183 Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
1185 in_group_row_change = true;
1187 for (i = rows.begin(); i != rows.end(); ++i) {
1188 if ((*i)[group_columns.group] == group) {
1189 (*i)[group_columns.visible] = !group->is_hidden ();
1190 (*i)[group_columns.active] = group->is_active ();
1191 (*i)[group_columns.text] = group->name ();
1192 break;
1196 in_group_row_change = false;
1199 void
1200 Mixer_UI::mix_group_name_edit (const Glib::ustring& path, const Glib::ustring& new_text)
1202 RouteGroup* group;
1203 TreeIter iter;
1205 if ((iter = group_model->get_iter (path))) {
1207 if ((group = (*iter)[group_columns.group]) == 0) {
1208 return;
1211 if (new_text != group->name()) {
1212 group->set_name (new_text);
1217 void
1218 Mixer_UI::mix_group_row_change (const Gtk::TreeModel::Path& path,const Gtk::TreeModel::iterator& iter)
1220 RouteGroup* group;
1222 if (in_group_row_change) {
1223 return;
1226 if ((group = (*iter)[group_columns.group]) == 0) {
1227 return;
1230 if ((*iter)[group_columns.visible]) {
1231 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1232 if ((*i)->mix_group() == group) {
1233 show_strip (*i);
1236 } else {
1237 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1238 if ((*i)->mix_group() == group) {
1239 hide_strip (*i);
1244 bool active = (*iter)[group_columns.active];
1245 group->set_active (active, this);
1247 Glib::ustring name = (*iter)[group_columns.text];
1249 if (name != group->name()) {
1250 group->set_name (name);
1255 void
1256 Mixer_UI::add_mix_group (RouteGroup* group)
1259 ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::add_mix_group), group));
1260 bool focus = false;
1262 in_group_row_change = true;
1264 TreeModel::Row row = *(group_model->append());
1265 row[group_columns.active] = group->is_active();
1267 row[group_columns.visible] = false;
1269 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1270 if ((*i)->mix_group() == group) {
1271 if ((*i)->marked_for_display()) {
1272 row[group_columns.visible] = true;
1274 break;
1278 row[group_columns.group] = group;
1279 if (!group->name().empty()) {
1280 row[group_columns.text] = group->name();
1281 } else {
1282 row[group_columns.text] = _("unnamed");
1283 focus = true;
1286 group->FlagsChanged.connect (bind (mem_fun(*this, &Mixer_UI::group_flags_changed), group));
1288 if (focus) {
1289 TreeViewColumn* col = group_display.get_column (0);
1290 CellRendererText* name_cell = dynamic_cast<CellRendererText*>(group_display.get_column_cell_renderer (0));
1291 group_display.set_cursor (group_model->get_path (row), *col, *name_cell, true);
1294 in_group_row_change = false;
1297 bool
1298 Mixer_UI::strip_scroller_button_release (GdkEventButton* ev)
1300 using namespace Menu_Helpers;
1302 if (Keyboard::is_context_menu_event (ev)) {
1303 ARDOUR_UI::instance()->add_route (this);
1304 return true;
1307 return false;
1310 void
1311 Mixer_UI::set_strip_width (Width w)
1313 _strip_width = w;
1315 for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
1316 (*i)->set_width (w, this);
1320 void
1321 Mixer_UI::set_window_pos_and_size ()
1323 resize (m_width, m_height);
1324 move (m_root_x, m_root_y);
1327 void
1328 Mixer_UI::get_window_pos_and_size ()
1330 get_position(m_root_x, m_root_y);
1331 get_size(m_width, m_height);
1335 Mixer_UI::set_state (const XMLNode& node)
1337 const XMLProperty* prop;
1338 XMLNode* geometry;
1340 m_width = default_width;
1341 m_height = default_height;
1342 m_root_x = 1;
1343 m_root_y = 1;
1345 if ((geometry = find_named_node (node, "geometry")) != 0) {
1347 XMLProperty* prop;
1349 if ((prop = geometry->property("x_size")) == 0) {
1350 prop = geometry->property ("x-size");
1352 if (prop) {
1353 m_width = atoi(prop->value());
1355 if ((prop = geometry->property("y_size")) == 0) {
1356 prop = geometry->property ("y-size");
1358 if (prop) {
1359 m_height = atoi(prop->value());
1362 if ((prop = geometry->property ("x_pos")) == 0) {
1363 prop = geometry->property ("x-pos");
1365 if (prop) {
1366 m_root_x = atoi (prop->value());
1369 if ((prop = geometry->property ("y_pos")) == 0) {
1370 prop = geometry->property ("y-pos");
1372 if (prop) {
1373 m_root_y = atoi (prop->value());
1377 set_window_pos_and_size ();
1379 if ((prop = node.property ("narrow-strips"))) {
1380 if (string_is_affirmative (prop->value())) {
1381 set_strip_width (Narrow);
1382 } else {
1383 set_strip_width (Wide);
1387 if ((prop = node.property ("show-mixer"))) {
1388 if (string_is_affirmative (prop->value())) {
1389 _visible = true;
1393 return 0;
1396 XMLNode&
1397 Mixer_UI::get_state (void)
1399 XMLNode* node = new XMLNode ("Mixer");
1401 if (is_realized()) {
1402 Glib::RefPtr<Gdk::Window> win = get_window();
1404 get_window_pos_and_size ();
1406 XMLNode* geometry = new XMLNode ("geometry");
1407 char buf[32];
1408 snprintf(buf, sizeof(buf), "%d", m_width);
1409 geometry->add_property(X_("x_size"), string(buf));
1410 snprintf(buf, sizeof(buf), "%d", m_height);
1411 geometry->add_property(X_("y_size"), string(buf));
1412 snprintf(buf, sizeof(buf), "%d", m_root_x);
1413 geometry->add_property(X_("x_pos"), string(buf));
1414 snprintf(buf, sizeof(buf), "%d", m_root_y);
1415 geometry->add_property(X_("y_pos"), string(buf));
1417 // written only for compatibility, they are not used.
1418 snprintf(buf, sizeof(buf), "%d", 0);
1419 geometry->add_property(X_("x_off"), string(buf));
1420 snprintf(buf, sizeof(buf), "%d", 0);
1421 geometry->add_property(X_("y_off"), string(buf));
1423 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&rhs_pane1)->gobj()));
1424 geometry->add_property(X_("mixer_rhs_pane1_pos"), string(buf));
1425 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&list_hpane)->gobj()));
1426 geometry->add_property(X_("mixer_list_hpane_pos"), string(buf));
1428 node->add_child_nocopy (*geometry);
1431 node->add_property ("narrow-strips", _strip_width == Narrow ? "yes" : "no");
1433 node->add_property ("show-mixer", _visible ? "yes" : "no");
1435 return *node;
1439 void
1440 Mixer_UI::pane_allocation_handler (Allocation& alloc, Gtk::Paned* which)
1442 int pos;
1443 XMLProperty* prop = 0;
1444 char buf[32];
1445 XMLNode* node = ARDOUR_UI::instance()->mixer_settings();
1446 XMLNode* geometry;
1447 int width, height;
1448 static int32_t done[3] = { 0, 0, 0 };
1450 width = default_width;
1451 height = default_height;
1453 if ((geometry = find_named_node (*node, "geometry")) != 0) {
1456 if ((prop = geometry->property ("x_size")) == 0) {
1457 prop = geometry->property ("x-size");
1459 if (prop) {
1460 width = atoi (prop->value());
1462 if ((prop = geometry->property ("y_size")) == 0) {
1463 prop = geometry->property ("y-size");
1465 if (prop) {
1466 height = atoi (prop->value());
1470 if (which == static_cast<Gtk::Paned*> (&rhs_pane1)) {
1472 if (done[0]) {
1473 return;
1476 if (!geometry || (prop = geometry->property("mixer_rhs_pane1_pos")) == 0) {
1477 pos = height / 3;
1478 snprintf (buf, sizeof(buf), "%d", pos);
1479 } else {
1480 pos = atoi (prop->value());
1483 if ((done[0] = GTK_WIDGET(rhs_pane1.gobj())->allocation.height > pos)) {
1484 rhs_pane1.set_position (pos);
1487 } else if (which == static_cast<Gtk::Paned*> (&list_hpane)) {
1489 if (done[2]) {
1490 return;
1493 if (!geometry || (prop = geometry->property("mixer_list_hpane_pos")) == 0) {
1494 pos = 75;
1495 snprintf (buf, sizeof(buf), "%d", pos);
1496 } else {
1497 pos = atoi (prop->value());
1500 if ((done[2] = GTK_WIDGET(list_hpane.gobj())->allocation.width > pos)) {
1501 list_hpane.set_position (pos);
1506 void
1507 Mixer_UI::scroll_left ()
1509 Adjustment* adj = scroller.get_hscrollbar()->get_adjustment();
1510 /* stupid GTK: can't rely on clamping across versions */
1511 scroller.get_hscrollbar()->set_value (max (adj->get_lower(), adj->get_value() - adj->get_step_increment()));
1514 void
1515 Mixer_UI::scroll_right ()
1517 Adjustment* adj = scroller.get_hscrollbar()->get_adjustment();
1518 /* stupid GTK: can't rely on clamping across versions */
1519 scroller.get_hscrollbar()->set_value (min (adj->get_upper(), adj->get_value() + adj->get_step_increment()));
1522 bool
1523 Mixer_UI::on_key_press_event (GdkEventKey* ev)
1525 switch (ev->keyval) {
1526 case GDK_Left:
1527 scroll_left ();
1528 return true;
1530 case GDK_Right:
1531 scroll_right ();
1532 return true;
1534 default:
1535 break;
1538 return key_press_focus_accelerator_handler (*this, ev);
1541 bool
1542 Mixer_UI::on_scroll_event (GdkEventScroll* ev)
1544 switch (ev->direction) {
1545 case GDK_SCROLL_LEFT:
1546 scroll_left ();
1547 return true;
1548 case GDK_SCROLL_UP:
1549 if (ev->state & Keyboard::TertiaryModifier) {
1550 scroll_left ();
1551 return true;
1553 return false;
1555 case GDK_SCROLL_RIGHT:
1556 scroll_right ();
1557 return true;
1559 case GDK_SCROLL_DOWN:
1560 if (ev->state & Keyboard::TertiaryModifier) {
1561 scroll_right ();
1562 return true;
1564 return false;
1567 return false;