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