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