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