0be9b03444c5fcc730281ac9f70334a108faeccc
[ardour.git] / gtk2_ardour / mixer_ui.cc
1 /*
2     Copyright (C) 2000-2004 Paul Davis 
3
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.
8
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.
13
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.
17
18 */
19
20 #include <algorithm>
21 #include <sigc++/bind.h>
22
23 #include <gtkmm/accelmap.h>
24
25 #include <pbd/convert.h>
26 #include <glibmm/thread.h>
27
28 #include <gtkmm2ext/gtk_ui.h>
29 #include <gtkmm2ext/utils.h>
30 #include <gtkmm2ext/stop_signal.h>
31 #include <gtkmm2ext/window_title.h>
32
33 #include <ardour/session.h>
34 #include <ardour/audio_track.h>
35 #include <ardour/session_route.h>
36 #include <ardour/audio_diskstream.h>
37 #include <ardour/plugin_manager.h>
38
39 #include "keyboard.h"
40 #include "mixer_ui.h"
41 #include "mixer_strip.h"
42 #include "plugin_selector.h"
43 #include "ardour_ui.h"
44 #include "prompter.h"
45 #include "utils.h"
46 #include "actions.h"
47 #include "gui_thread.h"
48
49 #include "i18n.h"
50
51 using namespace ARDOUR;
52 using namespace PBD;
53 using namespace Gtk;
54 using namespace Glib;
55 using namespace Gtkmm2ext;
56 using namespace sigc;
57 using namespace std;
58
59 using PBD::atoi;
60
61 Mixer_UI::Mixer_UI ()
62         : Window (Gtk::WINDOW_TOPLEVEL)
63 {
64         session = 0;
65         _strip_width = Config->get_default_narrow_ms() ? Narrow : Wide;
66         track_menu = 0;
67         mix_group_context_menu = 0;
68         no_track_list_redisplay = false;
69         in_group_row_change = false;
70         _visible = false;
71         strip_redisplay_does_not_reset_order_keys = false;
72         strip_redisplay_does_not_sync_order_keys = false;
73         ignore_sync = false;
74
75         Route::SyncOrderKeys.connect (mem_fun (*this, &Mixer_UI::sync_order_keys));
76
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);
82
83         scroller.add (strip_packer);
84         scroller.set_policy (Gtk::POLICY_ALWAYS, Gtk::POLICY_AUTOMATIC);
85
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);
98
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));
102
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;
106
107         track_display.signal_button_press_event().connect (mem_fun (*this, &Mixer_UI::track_display_button_press), false);
108
109         track_display_scroller.add (track_display);
110         track_display_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
111
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);
128
129         /* name is directly editable */
130
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));
134
135         /* use checkbox for the active column */
136
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;
140
141         /* use checkbox for the visible column */
142
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;
146
147         group_model->signal_row_changed().connect (mem_fun (*this, &Mixer_UI::mix_group_row_change));
148
149         group_display.signal_button_press_event().connect (mem_fun (*this, &Mixer_UI::group_display_button_press), false);
150
151         group_display_scroller.add (group_display);
152         group_display_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
153
154         HBox* mix_group_display_button_box = manage (new HBox());
155
156         Button* mix_group_add_button = manage (new Button ());
157         Button* mix_group_remove_button = manage (new Button ());
158
159         Widget* w;
160
161         w = manage (new Image (Stock::ADD, ICON_SIZE_BUTTON));
162         w->show();
163         mix_group_add_button->add (*w);
164
165         w = manage (new Image (Stock::REMOVE, ICON_SIZE_BUTTON));
166         w->show();
167         mix_group_remove_button->add (*w);
168
169         mix_group_display_button_box->set_homogeneous (true);
170
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));
173
174         mix_group_display_button_box->add (*mix_group_remove_button);
175         mix_group_display_button_box->add (*mix_group_add_button);
176
177         group_display_vbox.pack_start (group_display_scroller, true, true);
178         group_display_vbox.pack_start (*mix_group_display_button_box, false, false);
179
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);
183
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);
187
188         rhs_pane1.pack1 (track_display_frame);
189         rhs_pane1.pack2 (group_display_frame);
190
191         list_vpacker.pack_start (rhs_pane1, true, true);
192
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);
202
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)));
207         
208         global_vpacker.pack_start (list_hpane, true, true);
209
210         add (global_vpacker);
211         set_name ("MixerWindow");
212         
213         WindowTitle title(Glib::get_application_name());
214         title += _("Mixer");
215         set_title (title.get_string());
216
217         set_wmclass (X_("ardour_mixer"), "Ardour");
218
219         add_accel_group (ActionManager::ui_manager->get_accel_group());
220
221         signal_delete_event().connect (mem_fun (*this, &Mixer_UI::hide_window));
222         add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
223
224         _plugin_selector = new PluginSelector (PluginManager::the_manager());
225
226         signal_configure_event().connect (mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::configure_handler));
227
228         _selection.RoutesChanged.connect (mem_fun(*this, &Mixer_UI::follow_strip_selection));
229
230         mix_group_display_button_box->show();
231         mix_group_add_button->show();
232         mix_group_remove_button->show();
233
234         global_hpacker.show();
235         global_vpacker.show();
236         scroller.show();
237         scroller_base.show();
238         scroller_hpacker.show();
239         mixer_scroller_vpacker.show();
240         list_vpacker.show();
241         group_display_button_label.show();
242         group_display_button.show();
243         track_display_scroller.show();
244         group_display_scroller.show();
245         group_display_vbox.show();
246         track_display_frame.show();
247         group_display_frame.show();
248         rhs_pane1.show();
249         strip_packer.show();
250         out_packer.show();
251         list_hpane.show();
252         track_display.show();
253         group_display.show();
254
255         auto_rebinding = FALSE;
256 }
257
258 Mixer_UI::~Mixer_UI ()
259 {
260 }
261
262 void
263 Mixer_UI::ensure_float (Window& win)
264 {
265         win.set_transient_for (*this);
266 }
267
268 void
269 Mixer_UI::show_window ()
270 {
271         present ();
272         if (!_visible) {
273                 set_window_pos_and_size ();
274                 
275                 /* now reset each strips width so the right widgets are shown */
276                 MixerStrip* ms;
277                 
278                 TreeModel::Children rows = track_model->children();
279                 TreeModel::Children::iterator ri;
280                 
281                 for (ri = rows.begin(); ri != rows.end(); ++ri) {
282                         ms = (*ri)[track_columns.strip];
283                         ms->set_width (ms->get_width(), ms->width_owner());
284                 }
285         }
286         _visible = true;
287 }
288
289 bool
290 Mixer_UI::hide_window (GdkEventAny *ev)
291 {
292         get_window_pos_and_size ();
293
294         _visible = false;
295         return just_hide_it(ev, static_cast<Gtk::Window *>(this));
296 }
297
298
299 void
300 Mixer_UI::add_strip (Session::RouteList& routes)
301 {
302         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::add_strip), routes));
303         
304         MixerStrip* strip;
305
306         no_track_list_redisplay = true;
307         strip_redisplay_does_not_sync_order_keys = true;
308
309         for (Session::RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
310                 boost::shared_ptr<Route> route = (*x);
311
312                 if (route->is_hidden()) {
313                         return;
314                 }
315                 
316                 strip = new MixerStrip (*this, *session, route);
317                 strips.push_back (strip);
318                 
319                 Config->get_default_narrow_ms() ? _strip_width = Narrow : _strip_width = Wide;
320
321                 Config->get_default_narrow_ms() ? _strip_width = Narrow : _strip_width = Wide;
322
323                 if (strip->width_owner() != strip) {
324                         strip->set_width (_strip_width, this);
325                 }
326
327                 show_strip (strip);
328                 
329                 TreeModel::Row row = *(track_model->append());
330                 row[track_columns.text] = route->name();
331                 row[track_columns.visible] = strip->marked_for_display();
332                 row[track_columns.route] = route;
333                 row[track_columns.strip] = strip;
334
335                 if (route->order_key (N_("signal")) == -1) {
336                         route->set_order_key (N_("signal"), track_model->children().size()-1);
337                 }
338                 
339                 route->NameChanged.connect (bind (mem_fun(*this, &Mixer_UI::strip_name_changed), strip));
340
341                 strip->GoingAway.connect (bind (mem_fun(*this, &Mixer_UI::remove_strip), strip));
342                 
343                 strip->signal_button_release_event().connect (bind (mem_fun(*this, &Mixer_UI::strip_button_release_event), strip));
344         }
345
346         no_track_list_redisplay = false;
347
348         redisplay_track_list ();
349         
350         strip_redisplay_does_not_sync_order_keys = false;
351 }
352
353 void
354 Mixer_UI::remove_strip (MixerStrip* strip)
355 {
356         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::remove_strip), strip));
357
358         TreeModel::Children rows = track_model->children();
359         TreeModel::Children::iterator ri;
360         list<MixerStrip *>::iterator i;
361
362         if ((i = find (strips.begin(), strips.end(), strip)) != strips.end()) {
363                 strips.erase (i);
364         }
365
366         strip_redisplay_does_not_sync_order_keys = true;
367
368         for (ri = rows.begin(); ri != rows.end(); ++ri) {
369                 if ((*ri)[track_columns.strip] == strip) {
370                         track_model->erase (ri);
371                         break;
372                 }
373         }
374
375         strip_redisplay_does_not_sync_order_keys = false;
376 }
377
378 const char*
379 Mixer_UI::get_order_key() 
380 {
381         return X_("signal");
382 #if 0
383         if (Config->get_sync_all_route_ordering()) {
384                 return X_("editor");
385         } else {
386                 return X_("signal");
387         }
388 #endif
389 }
390
391 void
392 Mixer_UI::sync_order_keys (const char *src)
393 {
394         vector<int> neworder;
395         TreeModel::Children rows = track_model->children();
396         TreeModel::Children::iterator ri;
397
398         if ((strcmp (src, get_order_key()) == 0) || !session || (session->state_of_the_state() & Session::Loading) || rows.empty()) {
399                 return;
400         }
401
402         for (ri = rows.begin(); ri != rows.end(); ++ri) {
403                 neworder.push_back (0);
404         }
405
406         bool changed = false;
407         int order;
408
409         for (order = 0, ri = rows.begin(); ri != rows.end(); ++ri, ++order) {
410                 boost::shared_ptr<Route> route = (*ri)[track_columns.route];
411                 int old_key = order;
412                 int new_key = route->order_key (get_order_key());
413
414                 neworder[new_key] = old_key;
415
416                 if (new_key != old_key) {
417                         changed = true;
418                 }
419         }
420
421         if (changed) {
422                 strip_redisplay_does_not_reset_order_keys = true;
423                 track_model->reorder (neworder);
424                 strip_redisplay_does_not_reset_order_keys = false;
425         }
426 }
427
428 void
429 Mixer_UI::follow_strip_selection ()
430 {
431         for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
432                 (*i)->set_selected (_selection.selected ((*i)->route()));
433         }
434 }
435
436 bool
437 Mixer_UI::strip_button_release_event (GdkEventButton *ev, MixerStrip *strip)
438 {
439         if (ev->button == 1) {
440
441                 /* this allows the user to click on the strip to terminate comment
442                    editing. XXX it needs improving so that we don't select the strip
443                    at the same time.
444                 */
445                 
446                 if (_selection.selected (strip->route())) {
447                         _selection.remove (strip->route());
448                 } else {
449                         if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
450                                 _selection.add (strip->route());
451                         } else {
452                                 _selection.set (strip->route());
453                         }
454                 }
455         }
456
457         return true;
458 }
459
460 void
461 Mixer_UI::connect_to_session (Session* sess)
462 {
463
464         session = sess;
465
466         XMLNode* node = ARDOUR_UI::instance()->mixer_settings();
467         set_state (*node);
468
469         WindowTitle title(session->name());
470         title += _("Mixer");
471         title += Glib::get_application_name();
472
473         set_title (title.get_string());
474
475         initial_track_display ();
476
477         session->GoingAway.connect (mem_fun(*this, &Mixer_UI::disconnect_from_session));
478         session->RouteAdded.connect (mem_fun(*this, &Mixer_UI::add_strip));
479         session->mix_group_added.connect (mem_fun(*this, &Mixer_UI::add_mix_group));
480         session->mix_group_removed.connect (mem_fun(*this, &Mixer_UI::mix_groups_changed));
481
482         mix_groups_changed ();
483         
484         _plugin_selector->set_session (session);
485
486         if (_visible) {
487                show_window();
488         }
489
490         start_updating ();
491 }
492
493 void
494 Mixer_UI::disconnect_from_session ()
495 {
496         ENSURE_GUI_THREAD(mem_fun(*this, &Mixer_UI::disconnect_from_session));
497         
498         group_model->clear ();
499         _selection.clear ();
500
501         WindowTitle title(Glib::get_application_name());
502         title += _("Mixer");
503         set_title (title.get_string());
504         
505         stop_updating ();
506 }
507
508 void
509 Mixer_UI::show_strip (MixerStrip* ms)
510 {
511         TreeModel::Children rows = track_model->children();
512         TreeModel::Children::iterator i;
513         
514         for (i = rows.begin(); i != rows.end(); ++i) {
515         
516                 MixerStrip* strip = (*i)[track_columns.strip];
517                 if (strip == ms) {
518                         (*i)[track_columns.visible] = true;
519                         break;
520                 }
521         }
522 }
523
524 void
525 Mixer_UI::hide_strip (MixerStrip* ms)
526 {
527         TreeModel::Children rows = track_model->children();
528         TreeModel::Children::iterator i;
529         
530         for (i = rows.begin(); i != rows.end(); ++i) {
531                 
532                 MixerStrip* strip = (*i)[track_columns.strip];
533                 if (strip == ms) {
534                         (*i)[track_columns.visible] = false;
535                         break;
536                 }
537         }
538 }
539
540 gint
541 Mixer_UI::start_updating ()
542 {
543     fast_screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (mem_fun(*this, &Mixer_UI::fast_update_strips));
544     return 0;
545 }
546
547 gint
548 Mixer_UI::stop_updating ()
549 {
550     fast_screen_update_connection.disconnect();
551     return 0;
552 }
553
554 void
555 Mixer_UI::fast_update_strips ()
556 {
557         if (is_mapped () && session) {
558                 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
559                         (*i)->fast_update ();
560                 }
561         }
562 }
563
564 void
565 Mixer_UI::set_all_strips_visibility (bool yn)
566 {
567         TreeModel::Children rows = track_model->children();
568         TreeModel::Children::iterator i;
569
570         no_track_list_redisplay = true;
571
572         for (i = rows.begin(); i != rows.end(); ++i) {
573
574                 TreeModel::Row row = (*i);
575                 MixerStrip* strip = row[track_columns.strip];
576                 
577                 if (strip == 0) {
578                         continue;
579                 }
580                 
581                 if (strip->route()->is_master() || strip->route()->is_control()) {
582                         continue;
583                 }
584
585                 (*i)[track_columns.visible] = yn;
586         }
587
588         no_track_list_redisplay = false;
589         redisplay_track_list ();
590 }
591
592
593 void
594 Mixer_UI::set_all_audio_visibility (int tracks, bool yn) 
595 {
596         TreeModel::Children rows = track_model->children();
597         TreeModel::Children::iterator i;
598
599         no_track_list_redisplay = true;
600
601         for (i = rows.begin(); i != rows.end(); ++i) {
602                 TreeModel::Row row = (*i);
603                 MixerStrip* strip = row[track_columns.strip];
604
605                 if (strip == 0) {
606                         continue;
607                 }
608
609                 if (strip->route()->is_master() || strip->route()->is_control()) {
610                         continue;
611                 }
612
613                 boost::shared_ptr<AudioTrack> at = strip->audio_track();
614
615                 switch (tracks) {
616                 case 0:
617                         (*i)[track_columns.visible] = yn;
618                         break;
619                         
620                 case 1:
621                         if (at) { /* track */
622                                 (*i)[track_columns.visible] = yn;
623                         }
624                         break;
625                         
626                 case 2:
627                         if (!at) { /* bus */
628                                 (*i)[track_columns.visible] = yn;
629                         }
630                         break;
631                 }
632         }
633
634         no_track_list_redisplay = false;
635         redisplay_track_list ();
636 }
637
638 void
639 Mixer_UI::hide_all_routes ()
640 {
641         set_all_strips_visibility (false);
642 }
643
644 void
645 Mixer_UI::show_all_routes ()
646 {
647         set_all_strips_visibility (true);
648 }
649
650 void
651 Mixer_UI::show_all_audiobus ()
652 {
653         set_all_audio_visibility (2, true);
654 }
655 void
656 Mixer_UI::hide_all_audiobus ()
657 {
658         set_all_audio_visibility (2, false);
659 }
660
661 void
662 Mixer_UI::show_all_audiotracks()
663 {
664         set_all_audio_visibility (1, true);
665 }
666 void
667 Mixer_UI::hide_all_audiotracks ()
668 {
669         set_all_audio_visibility (1, false);
670 }
671
672 void
673 Mixer_UI::track_list_reorder (const TreeModel::Path& path, const TreeModel::iterator& iter, int* new_order)
674 {
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;
679 }
680
681 void
682 Mixer_UI::track_list_change (const Gtk::TreeModel::Path& path,const Gtk::TreeModel::iterator& iter)
683 {
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;
689 }
690
691 void
692 Mixer_UI::track_list_delete (const Gtk::TreeModel::Path& path)
693 {
694         /* this could require an order sync */
695         session->set_remote_control_ids();
696         redisplay_track_list ();
697 }
698
699 void
700 Mixer_UI::redisplay_track_list ()
701 {
702         TreeModel::Children rows = track_model->children();
703         TreeModel::Children::iterator i;
704         long order;
705
706         if (no_track_list_redisplay) {
707                 return;
708         }
709
710         for (order = 0, i = rows.begin(); i != rows.end(); ++i, ++order) {
711                 MixerStrip* strip = (*i)[track_columns.strip];
712
713                 if (strip == 0) {
714                         /* we're in the middle of changing a row, don't worry */
715                         continue;
716                 }
717
718                 bool visible = (*i)[track_columns.visible];
719
720                 if (visible) {
721                         strip->set_marked_for_display (true);
722                         strip->route()->set_order_key (N_("signal"), order);
723
724                         if (!strip_redisplay_does_not_reset_order_keys) {
725                                 strip->route()->set_order_key (get_order_key(), order);
726                         } 
727
728                         if (strip->packed()) {
729
730                                 if (strip->route()->is_master() || strip->route()->is_control()) {
731                                         out_packer.reorder_child (*strip, -1);
732                                 } else {
733                                         strip_packer.reorder_child (*strip, -1); /* put at end */
734                                 }
735
736                         } else {
737
738                                 if (strip->route()->is_master() || strip->route()->is_control()) {
739                                         out_packer.pack_start (*strip, false, false);
740                                 } else {
741                                         strip_packer.pack_start (*strip, false, false);
742                                 }
743                                 strip->set_packed (true);
744                                 //strip->show();
745                         }
746
747                 } else {
748
749                         if (strip->route()->is_master() || strip->route()->is_control()) {
750                                 /* do nothing, these cannot be hidden */
751                         } else {
752                                 if (strip->packed()) {
753                                         strip_packer.remove (*strip);
754                                         strip->set_packed (false);
755                                 }
756                         }
757                 }
758         }
759         
760         if (!strip_redisplay_does_not_reset_order_keys && !strip_redisplay_does_not_sync_order_keys) {
761                 session->sync_order_keys (get_order_key());
762         }
763
764         // Rebind all of the midi controls automatically
765         
766         if (auto_rebinding)
767                 auto_rebind_midi_controls ();
768 }
769
770 void
771 Mixer_UI::set_auto_rebinding( bool val )
772 {
773         if( val == TRUE )
774         {
775                 auto_rebinding = TRUE;
776                 Session::AutoBindingOff();
777         }
778         else
779         {
780                 auto_rebinding = FALSE;
781                 Session::AutoBindingOn();
782         }
783 }
784
785 void 
786 Mixer_UI::toggle_auto_rebinding() 
787 {
788         if (auto_rebinding)
789         {
790                 set_auto_rebinding( FALSE );
791         }
792         
793         else
794         {
795                 set_auto_rebinding( TRUE );
796         }
797
798         auto_rebind_midi_controls();
799 }
800
801 void 
802 Mixer_UI::auto_rebind_midi_controls () 
803 {
804         TreeModel::Children rows = track_model->children();
805         TreeModel::Children::iterator i;
806         int pos;
807
808         // Create bindings for all visible strips and remove those that are not visible
809         pos = 1;  // 0 is reserved for the master strip
810         for (i = rows.begin(); i != rows.end(); ++i) {
811                 MixerStrip* strip = (*i)[track_columns.strip];
812     
813                 if ( (*i)[track_columns.visible] == true ) {  // add bindings for
814                         // make the actual binding
815                         //cout<<"Auto Binding:  Visible Strip Found: "<<strip->name()<<endl;
816
817                         int controlValue = pos;
818                         if( strip->route()->is_master() ) {
819                                 controlValue = 0;
820                         }
821                         else {
822                                 pos++;
823                         }
824
825                         PBD::Controllable::CreateBinding ( strip->solo_button->get_controllable().get(), controlValue, 0);
826                         PBD::Controllable::CreateBinding ( strip->mute_button->get_controllable().get(), controlValue, 1);
827
828                         if( strip->is_audio_track() ) {
829                                 PBD::Controllable::CreateBinding ( strip->rec_enable_button->get_controllable().get(), controlValue, 2);
830                         }
831
832                         PBD::Controllable::CreateBinding ( strip->gpm.get_controllable().get(), controlValue, 3);
833                         PBD::Controllable::CreateBinding ( strip->panners.get_controllable().get(), controlValue, 4);
834
835                 }
836                 else {  // Remove any existing binding
837                         PBD::Controllable::DeleteBinding ( strip->solo_button->get_controllable().get() );
838                         PBD::Controllable::DeleteBinding ( strip->mute_button->get_controllable().get() );
839
840                         if( strip->is_audio_track() ) {
841                                 PBD::Controllable::DeleteBinding ( strip->rec_enable_button->get_controllable().get() );
842                         }
843
844                         PBD::Controllable::DeleteBinding ( strip->gpm.get_controllable().get() );
845                         PBD::Controllable::DeleteBinding ( strip->panners.get_controllable().get() ); // This only takes the first panner if there are multiples...
846                 }
847
848         } // for
849   
850 }
851
852 struct SignalOrderRouteSorter {
853     bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
854             /* use of ">" forces the correct sort order */
855             return a->order_key (Mixer_UI::get_order_key()) < b->order_key (Mixer_UI::get_order_key());
856     }
857 };
858
859 void
860 Mixer_UI::initial_track_display ()
861 {
862         boost::shared_ptr<Session::RouteList> routes = session->get_routes();
863         Session::RouteList copy (*routes);
864         SignalOrderRouteSorter sorter;
865
866         copy.sort (sorter);
867         
868         no_track_list_redisplay = true;
869
870         track_model->clear ();
871
872         add_strip (copy);
873
874         no_track_list_redisplay = false;
875
876         redisplay_track_list ();
877 }
878
879 void
880 Mixer_UI::show_track_list_menu ()
881 {
882         if (track_menu == 0) {
883                 build_track_menu ();
884         }
885
886         track_menu->popup (1, gtk_get_current_event_time());
887 }
888
889 bool
890 Mixer_UI::track_display_button_press (GdkEventButton* ev)
891 {
892         if (Keyboard::is_context_menu_event (ev)) {
893                 show_track_list_menu ();
894                 return true;
895         }
896
897         TreeIter iter;
898         TreeModel::Path path;
899         TreeViewColumn* column;
900         int cellx;
901         int celly;
902         
903         if (!track_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
904                 return false;
905         }
906
907         switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
908         case 0:
909                 /* allow normal processing to occur */
910                 return false;
911
912         case 1: /* visibility */
913
914                 if ((iter = track_model->get_iter (path))) {
915                         MixerStrip* strip = (*iter)[track_columns.strip];
916                         if (strip) {
917
918                                 if (!strip->route()->is_master() && !strip->route()->is_control()) {
919                                         bool visible = (*iter)[track_columns.visible];
920                                         (*iter)[track_columns.visible] = !visible;
921                                 }
922                         }
923                 }
924                 return true;
925
926         default:
927                 break;
928         }
929
930         return false;
931 }
932
933
934 void
935 Mixer_UI::build_track_menu ()
936 {
937         using namespace Menu_Helpers;
938         using namespace Gtk;
939
940         track_menu = new Menu;
941         track_menu->set_name ("ArdourContextMenu");
942         MenuList& items = track_menu->items();
943         
944         items.push_back (MenuElem (_("Show All"), mem_fun(*this, &Mixer_UI::show_all_routes)));
945         items.push_back (MenuElem (_("Hide All"), mem_fun(*this, &Mixer_UI::hide_all_routes)));
946         items.push_back (MenuElem (_("Show All Audio Tracks"), mem_fun(*this, &Mixer_UI::show_all_audiotracks)));
947         items.push_back (MenuElem (_("Hide All Audio Tracks"), mem_fun(*this, &Mixer_UI::hide_all_audiotracks)));
948         items.push_back (MenuElem (_("Show All Audio Busses"), mem_fun(*this, &Mixer_UI::show_all_audiobus)));
949         items.push_back (MenuElem (_("Hide All Audio Busses"), mem_fun(*this, &Mixer_UI::hide_all_audiobus)));
950
951 }
952
953 void
954 Mixer_UI::strip_name_changed (MixerStrip* mx)
955 {
956         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::strip_name_changed), mx));
957         
958         TreeModel::Children rows = track_model->children();
959         TreeModel::Children::iterator i;
960         
961         for (i = rows.begin(); i != rows.end(); ++i) {
962                 if ((*i)[track_columns.strip] == mx) {
963                         (*i)[track_columns.text] = mx->route()->name();
964                         return;
965                 }
966         } 
967
968         error << _("track display list item for renamed strip not found!") << endmsg;
969 }
970
971
972 void
973 Mixer_UI::build_mix_group_context_menu ()
974 {
975         using namespace Gtk::Menu_Helpers;
976
977         mix_group_context_menu = new Menu;
978         mix_group_context_menu->set_name ("ArdourContextMenu");
979         MenuList& items = mix_group_context_menu->items();
980
981         items.push_back (MenuElem (_("Activate All"), mem_fun(*this, &Mixer_UI::activate_all_mix_groups)));
982         items.push_back (MenuElem (_("Disable All"), mem_fun(*this, &Mixer_UI::disable_all_mix_groups)));
983         items.push_back (SeparatorElem());
984         items.push_back (MenuElem (_("Add group"), mem_fun(*this, &Mixer_UI::new_mix_group)));
985         
986 }
987
988 bool
989 Mixer_UI::group_display_button_press (GdkEventButton* ev)
990 {
991         if (Keyboard::is_context_menu_event (ev)) {
992                 if (mix_group_context_menu == 0) {
993                         build_mix_group_context_menu ();
994                 }
995                 mix_group_context_menu->popup (1, ev->time);
996                 return true;
997         }
998
999
1000         RouteGroup* group;
1001         TreeIter iter;
1002         TreeModel::Path path;
1003         TreeViewColumn* column;
1004         int cellx;
1005         int celly;
1006
1007         if (!group_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
1008                 return false;
1009         }
1010
1011         switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
1012         case 0:
1013                 if (Keyboard::is_edit_event (ev)) {
1014                         if ((iter = group_model->get_iter (path))) {
1015                                 if ((group = (*iter)[group_columns.group]) != 0) {
1016                                         // edit_mix_group (group);
1017                                         return true;
1018                                 }
1019                         }
1020                         
1021                 } 
1022                 break;
1023
1024         case 1:
1025                 if ((iter = group_model->get_iter (path))) {
1026                         bool active = (*iter)[group_columns.active];
1027                         (*iter)[group_columns.active] = !active;
1028                         return true;
1029                 }
1030                 break;
1031                 
1032         case 2:
1033                 if ((iter = group_model->get_iter (path))) {
1034                         bool visible = (*iter)[group_columns.visible];
1035                         (*iter)[group_columns.visible] = !visible;
1036                         return true;
1037                 }
1038                 break;
1039
1040         default:
1041                 break;
1042         }
1043         
1044         return false;
1045  }
1046
1047 void
1048 Mixer_UI::activate_all_mix_groups ()
1049 {
1050         Gtk::TreeModel::Children children = group_model->children();
1051         for(Gtk::TreeModel::Children::iterator iter = children.begin(); iter != children.end(); ++iter) {
1052                 (*iter)[group_columns.active] = true;
1053         }
1054 }
1055
1056 void
1057 Mixer_UI::disable_all_mix_groups ()
1058 {
1059         Gtk::TreeModel::Children children = group_model->children();
1060         for(Gtk::TreeModel::Children::iterator iter = children.begin(); iter != children.end(); ++iter) {
1061                 (*iter)[group_columns.active] = false;
1062         }
1063 }
1064
1065 void
1066 Mixer_UI::mix_groups_changed ()
1067 {
1068         ENSURE_GUI_THREAD (mem_fun (*this, &Mixer_UI::mix_groups_changed));
1069
1070         /* just rebuild the while thing */
1071
1072         group_model->clear ();
1073
1074         {
1075                 TreeModel::Row row;
1076                 row = *(group_model->append());
1077                 row[group_columns.active] = false;
1078                 row[group_columns.visible] = true;
1079                 row[group_columns.text] = (_("-all-"));
1080                 row[group_columns.group] = 0;
1081         }
1082
1083         session->foreach_mix_group (mem_fun (*this, &Mixer_UI::add_mix_group));
1084 }
1085
1086 void
1087 Mixer_UI::new_mix_group ()
1088 {
1089         session->add_mix_group ("");
1090 }
1091
1092 void
1093 Mixer_UI::remove_selected_mix_group ()
1094 {
1095         Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
1096         TreeView::Selection::ListHandle_Path rows = selection->get_selected_rows ();
1097
1098         if (rows.empty()) {
1099                 return;
1100         }
1101
1102         TreeView::Selection::ListHandle_Path::iterator i = rows.begin();
1103         TreeIter iter;
1104         
1105         /* selection mode is single, so rows.begin() is it */
1106
1107         if ((iter = group_model->get_iter (*i))) {
1108
1109                 RouteGroup* rg = (*iter)[group_columns.group];
1110
1111                 if (rg) {
1112                         session->remove_mix_group (*rg);
1113                 }
1114         }
1115 }
1116
1117 void
1118 Mixer_UI::group_flags_changed (void* src, RouteGroup* group)
1119 {
1120         if (in_group_row_change) {
1121                 return;
1122         }
1123
1124         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::group_flags_changed), src, group));
1125
1126         /* force an update of any mixer strips that are using this group,
1127            otherwise mix group names don't change in mixer strips 
1128         */
1129
1130         for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1131                 if ((*i)->mix_group() == group) {
1132                         (*i)->mix_group_changed(0);
1133                 }
1134         }
1135         
1136         TreeModel::iterator i;
1137         TreeModel::Children rows = group_model->children();
1138         Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
1139
1140         in_group_row_change = true;
1141         
1142         for (i = rows.begin(); i != rows.end(); ++i) {
1143                 if ((*i)[group_columns.group] == group) {
1144                         (*i)[group_columns.visible] = !group->is_hidden ();
1145                         (*i)[group_columns.active] = group->is_active ();
1146                         (*i)[group_columns.text] = group->name ();
1147                         break;
1148                 }
1149         }
1150
1151         in_group_row_change = false;
1152 }
1153
1154 void
1155 Mixer_UI::mix_group_name_edit (const Glib::ustring& path, const Glib::ustring& new_text)
1156 {
1157         RouteGroup* group;
1158         TreeIter iter;
1159
1160         if ((iter = group_model->get_iter (path))) {
1161         
1162                 if ((group = (*iter)[group_columns.group]) == 0) {
1163                         return;
1164                 }
1165                 
1166                 if (new_text != group->name()) {
1167                         group->set_name (new_text);
1168                 }
1169         }
1170 }
1171
1172 void 
1173 Mixer_UI::mix_group_row_change (const Gtk::TreeModel::Path& path,const Gtk::TreeModel::iterator& iter)
1174 {
1175         RouteGroup* group;
1176
1177         if (in_group_row_change) {
1178                 return;
1179         }
1180
1181         if ((group = (*iter)[group_columns.group]) == 0) {
1182                 return;
1183         }
1184
1185         if ((*iter)[group_columns.visible]) {
1186                 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1187                         if ((*i)->mix_group() == group) {
1188                                 show_strip (*i);
1189                         }
1190                 }
1191         } else {
1192                 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1193                         if ((*i)->mix_group() == group) {
1194                                 hide_strip (*i);
1195                         }
1196                 }
1197         } 
1198
1199         bool active = (*iter)[group_columns.active];
1200         group->set_active (active, this);
1201
1202         Glib::ustring name = (*iter)[group_columns.text];
1203
1204         if (name != group->name()) {
1205                 group->set_name (name);
1206         }
1207
1208 }
1209
1210 void
1211 Mixer_UI::add_mix_group (RouteGroup* group)
1212
1213 {
1214         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::add_mix_group), group));
1215         bool focus = false;
1216
1217         in_group_row_change = true;
1218
1219         TreeModel::Row row = *(group_model->append());
1220         row[group_columns.active] = group->is_active();
1221         row[group_columns.visible] = true;
1222         row[group_columns.group] = group;
1223         if (!group->name().empty()) {
1224                 row[group_columns.text] = group->name();
1225         } else {
1226                 row[group_columns.text] = _("unnamed");
1227                 focus = true;
1228         }
1229
1230         group->FlagsChanged.connect (bind (mem_fun(*this, &Mixer_UI::group_flags_changed), group));
1231         
1232         if (focus) {
1233                 TreeViewColumn* col = group_display.get_column (0);
1234                 CellRendererText* name_cell = dynamic_cast<CellRendererText*>(group_display.get_column_cell_renderer (0));
1235                 group_display.set_cursor (group_model->get_path (row), *col, *name_cell, true);
1236         }
1237
1238         in_group_row_change = false;
1239 }
1240
1241 bool
1242 Mixer_UI::strip_scroller_button_release (GdkEventButton* ev)
1243 {
1244         using namespace Menu_Helpers;
1245
1246         if (Keyboard::is_context_menu_event (ev)) {
1247                 ARDOUR_UI::instance()->add_route (this);
1248                 return true;
1249         }
1250
1251         return false;
1252 }
1253
1254 void
1255 Mixer_UI::set_strip_width (Width w)
1256 {
1257         _strip_width = w;
1258
1259         for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
1260                 (*i)->set_width (w, this);
1261         }
1262 }
1263
1264 void
1265 Mixer_UI::set_window_pos_and_size ()
1266 {
1267         resize (m_width, m_height);
1268         move (m_root_x, m_root_y);
1269 }
1270
1271         void
1272 Mixer_UI::get_window_pos_and_size ()
1273 {
1274         get_position(m_root_x, m_root_y);
1275         get_size(m_width, m_height);
1276 }
1277
1278 int
1279 Mixer_UI::set_state (const XMLNode& node)
1280 {
1281         const XMLProperty* prop;
1282         XMLNode* geometry;
1283         
1284         if ((geometry = find_named_node (node, "geometry")) == 0) {
1285
1286                 m_width = default_width;
1287                 m_height = default_height;
1288                 m_root_x = 1;
1289                 m_root_y = 1;
1290
1291         } else {
1292
1293                 m_width = atoi(geometry->property("x-size")->value().c_str());
1294                 m_height = atoi(geometry->property("y-size")->value().c_str());
1295                 m_root_x = atoi(geometry->property("x-pos")->value().c_str());
1296                 m_root_y = atoi(geometry->property("y-pos")->value().c_str());
1297         }
1298
1299         set_window_pos_and_size ();
1300
1301         if ((prop = node.property ("narrow-strips"))) {
1302                 if (prop->value() == "yes") {
1303                         set_strip_width (Narrow);
1304                 } else {
1305                         set_strip_width (Wide);
1306                 }
1307         }
1308
1309         if ((prop = node.property ("show-mixer"))) {
1310                 if (prop->value() == "yes") {
1311                        _visible = true;
1312                 }
1313         }
1314
1315         return 0;
1316 }
1317
1318 XMLNode&
1319 Mixer_UI::get_state (void)
1320 {
1321         XMLNode* node = new XMLNode ("Mixer");
1322
1323         if (is_realized()) {
1324                 Glib::RefPtr<Gdk::Window> win = get_window();
1325         
1326                 get_window_pos_and_size ();
1327
1328                 XMLNode* geometry = new XMLNode ("geometry");
1329                 char buf[32];
1330                 snprintf(buf, sizeof(buf), "%d", m_width);
1331                 geometry->add_property(X_("x_size"), string(buf));
1332                 snprintf(buf, sizeof(buf), "%d", m_height);
1333                 geometry->add_property(X_("y_size"), string(buf));
1334                 snprintf(buf, sizeof(buf), "%d", m_root_x);
1335                 geometry->add_property(X_("x_pos"), string(buf));
1336                 snprintf(buf, sizeof(buf), "%d", m_root_y);
1337                 geometry->add_property(X_("y_pos"), string(buf));
1338                 
1339                 // written only for compatibility, they are not used.
1340                 snprintf(buf, sizeof(buf), "%d", 0);
1341                 geometry->add_property(X_("x_off"), string(buf));
1342                 snprintf(buf, sizeof(buf), "%d", 0);
1343                 geometry->add_property(X_("y_off"), string(buf));
1344
1345                 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&rhs_pane1)->gobj()));
1346                 geometry->add_property(X_("mixer_rhs_pane1_pos"), string(buf));
1347                 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&list_hpane)->gobj()));
1348                 geometry->add_property(X_("mixer_list_hpane_pos"), string(buf));
1349
1350                 node->add_child_nocopy (*geometry);
1351         }
1352
1353         node->add_property ("narrow-strips", _strip_width == Narrow ? "yes" : "no");
1354
1355         node->add_property ("show-mixer", _visible ? "yes" : "no");
1356
1357         return *node;
1358 }
1359
1360
1361 void 
1362 Mixer_UI::pane_allocation_handler (Allocation& alloc, Gtk::Paned* which)
1363 {
1364         int pos;
1365         XMLProperty* prop = 0;
1366         char buf[32];
1367         XMLNode* node = ARDOUR_UI::instance()->mixer_settings();
1368         XMLNode* geometry;
1369         int width, height;
1370         static int32_t done[3] = { 0, 0, 0 };
1371
1372         if ((geometry = find_named_node (*node, "geometry")) == 0) {
1373                 width = default_width;
1374                 height = default_height;
1375         } else {
1376                 width = atoi(geometry->property("x-size")->value());
1377                 height = atoi(geometry->property("y-size")->value());
1378         }
1379
1380         if (which == static_cast<Gtk::Paned*> (&rhs_pane1)) {
1381
1382                 if (done[0]) {
1383                         return;
1384                 }
1385
1386                 if (!geometry || (prop = geometry->property("mixer-rhs-pane1-pos")) == 0) {
1387                         pos = height / 3;
1388                         snprintf (buf, sizeof(buf), "%d", pos);
1389                 } else {
1390                         pos = atoi (prop->value());
1391                 }
1392
1393                 if ((done[0] = GTK_WIDGET(rhs_pane1.gobj())->allocation.height > pos)) {
1394                         rhs_pane1.set_position (pos);
1395                 }
1396
1397         } else if (which == static_cast<Gtk::Paned*> (&list_hpane)) {
1398
1399                 if (done[2]) {
1400                         return;
1401                 }
1402
1403                 if (!geometry || (prop = geometry->property("mixer-list-hpane-pos")) == 0) {
1404                         pos = 75;
1405                         snprintf (buf, sizeof(buf), "%d", pos);
1406                 } else {
1407                         pos = atoi (prop->value());
1408                 }
1409
1410                 if ((done[2] = GTK_WIDGET(list_hpane.gobj())->allocation.width > pos)) {
1411                         list_hpane.set_position (pos);
1412                 }
1413         }
1414 }
1415
1416 bool
1417 Mixer_UI::on_key_press_event (GdkEventKey* ev)
1418 {
1419         return key_press_focus_accelerator_handler (*this, ev);
1420 }