a67728b03796f27b3fd0594cec263d4c50f5ce1e
[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 <pbd/lockmonitor.h>
25 #include <gtkmm2ext/gtk_ui.h>
26 #include <gtkmm2ext/utils.h>
27 #include <gtkmm2ext/stop_signal.h>
28
29 #include <ardour/audioengine.h>
30 #include <ardour/session.h>
31 #include <ardour/session_route.h>
32 #include <ardour/diskstream.h>
33 #include <ardour/plugin_manager.h>
34
35 #include "mixer_ui.h"
36 #include "mixer_strip.h"
37 #include "plugin_selector.h"
38 #include "ardour_ui.h"
39 #include "prompter.h"
40 #include "utils.h"
41 #include "gui_thread.h"
42
43 #include "i18n.h"
44
45 using namespace ARDOUR;
46 using namespace Gtk;
47 using namespace Gtkmm2ext;
48 using namespace sigc;
49
50 Mixer_UI::Mixer_UI (AudioEngine& eng)
51         : Window (Gtk::WINDOW_TOPLEVEL),
52           engine (eng)
53 {
54         _strip_width = Wide;
55         track_menu = 0;
56
57         XMLNode* node = ARDOUR_UI::instance()->mixer_settings();
58         set_state (*node);
59
60         scroller_base.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
61         scroller_base.set_name ("MixerWindow");
62         scroller_base.signal_button_release_event().connect (mem_fun(*this, &Mixer_UI::strip_scroller_button_release));
63         // add as last item of strip packer
64         strip_packer.pack_end (scroller_base, true, true);
65
66         scroller.add (strip_packer);
67         scroller.set_policy (Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
68
69         track_display_model = ListStore::create (track_display_columns);
70         track_display.set_model (track_display_model);
71         track_display.append_column (_("Strips"), track_display_columns.text);
72         track_display.set_name (X_("MixerTrackDisplayList"));
73         track_display.get_selection()->set_mode (Gtk::SELECTION_MULTIPLE);
74         track_display.set_reorderable (true);
75         track_display.set_size_request (75, -1);
76         track_display.set_headers_visible (true);
77         track_display.set_headers_clickable (true);
78         track_display_scroller.add (track_display);
79         track_display_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
80
81         group_display_model = ListStore::create (group_display_columns);
82         group_display.set_model (group_display_model);
83         group_display.append_column (_("active"), group_display_columns.active);
84         group_display.append_column (_("groupname"), group_display_columns.text);
85         group_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0));
86         group_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
87
88         /* use checkbox for the active column */
89
90         CellRendererToggle *active_cell = dynamic_cast<CellRendererToggle*>(group_display.get_column_cell_renderer (0));
91         active_cell->property_activatable() = true;
92         active_cell->property_radio() = false;
93         
94         group_display.set_name ("MixerGroupList");
95         group_display.set_reorderable (true);
96         group_display.set_size_request (true);
97         group_display_scroller.add (group_display);
98         group_display_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
99
100         snapshot_display_model = ListStore::create (group_display_columns);
101         snapshot_display.set_model (snapshot_display_model);
102         snapshot_display.append_column (X_("mixgroups"), snapshot_display_columns.visible_name);
103         snapshot_display.set_name ("MixerSnapshotDisplayList");
104         snapshot_display.set_size_request (75, -1);
105         snapshot_display.set_reorderable (true);
106         snapshot_display_scroller.add (snapshot_display);
107         snapshot_display_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
108
109         group_display_vbox.pack_start (group_display_button, false, false);
110         group_display_vbox.pack_start (group_display_scroller, true, true);
111
112         track_display_frame.set_name("BaseFrame");
113         track_display_frame.set_shadow_type (Gtk::SHADOW_IN);
114         track_display_frame.add(track_display_scroller);
115
116         group_display_frame.set_name ("BaseFrame");
117         group_display_frame.set_shadow_type (Gtk::SHADOW_IN);
118         group_display_frame.add (group_display_vbox);
119
120         rhs_pane1.add1 (track_display_frame);
121         rhs_pane1.add2 (rhs_pane2);
122
123         rhs_pane2.add1 (group_display_frame);
124         rhs_pane2.add2 (snapshot_display_scroller);
125
126         list_vpacker.pack_start (rhs_pane1, true, true);
127
128         global_hpacker.pack_start (scroller, true, true);
129         global_hpacker.pack_start (out_packer, false, false);
130
131         list_hpane.add1(list_vpacker);
132         list_hpane.add2(global_hpacker);
133
134         rhs_pane1.signal_size_allocate().connect (bind (mem_fun(*this, &Mixer_UI::pane_allocation_handler), 
135                                                         static_cast<Gtk::Paned*> (&rhs_pane1)));
136         rhs_pane2.signal_size_allocate().connect (bind (mem_fun(*this, &Mixer_UI::pane_allocation_handler), 
137                                                         static_cast<Gtk::Paned*> (&rhs_pane2)));
138         list_hpane.signal_size_allocate().connect (bind (mem_fun(*this, &Mixer_UI::pane_allocation_handler), 
139                                                          static_cast<Gtk::Paned*> (&list_hpane)));
140         
141
142         rhs_pane1.set_data ("collapse-direction", (gpointer) 0);
143         rhs_pane2.set_data ("collapse-direction", (gpointer) 0);
144         list_hpane.set_data ("collapse-direction", (gpointer) 1);
145
146         rhs_pane1.signal_button_release_event().connect (bind (ptr_fun (pane_handler), static_cast<Paned*>(&rhs_pane1)));
147         rhs_pane2.signal_button_release_event().connect (bind (ptr_fun (pane_handler), static_cast<Paned*>(&rhs_pane2)));
148         list_hpane.signal_button_release_event().connect (bind (ptr_fun (pane_handler), static_cast<Paned*>(&list_hpane)));
149         
150         global_vpacker.pack_start (list_hpane, true, true);
151
152         add (global_vpacker);
153         set_name ("MixerWindow");
154         set_title (_("ardour: mixer"));
155         set_wmclass (_("ardour_mixer"), "Ardour");
156
157         signal_delete_event().connect (bind (ptr_fun (just_hide_it), static_cast<Gtk::Window *>(this)));
158         add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
159
160         track_display.get_selection()->signal_changed().connect (mem_fun(*this, &Mixer_UI::track_display_selection_changed));
161         track_display_model->signal_rows_reordered().connect (mem_fun (*this, &Mixer_UI::track_display_reordered_proxy));
162
163         group_display.signal_button_press_event().connect (mem_fun (*this, &Mixer_UI::group_display_button_press));
164         group_display.get_selection()->signal_changed().connect (mem_fun (*this, &Mixer_UI::group_display_selection_changed));
165
166         snapshot_display.get_selection()->signal_changed().connect (mem_fun(*this, &Mixer_UI::snapshot_display_selection_changed));
167         snapshot_display.signal_button_press_event().connect (mem_fun (*this, &Mixer_UI::snapshot_display_button_press));
168
169         _plugin_selector = new PluginSelector (PluginManager::the_manager());
170         _plugin_selector->signal_delete_event().connect (bind (ptr_fun (just_hide_it), 
171                                                      static_cast<Window *> (_plugin_selector)));
172
173         signal_configure_event().connect (mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::configure_handler));
174
175         _selection.RoutesChanged.connect (mem_fun(*this, &Mixer_UI::follow_strip_selection));
176 }
177
178 Mixer_UI::~Mixer_UI ()
179 {
180 }
181
182 void
183 Mixer_UI::ensure_float (Window& win)
184 {
185         win.set_transient_for (*this);
186 }
187
188 void
189 Mixer_UI::show_window ()
190 {
191         show_all ();
192
193         /* now reset each strips width so the right widgets are shown */
194         MixerStrip* ms;
195
196         TreeModel::Children rows = track_display_model->children();
197         TreeModel::Children::iterator ri;
198
199         for (ri = rows.begin(); ri != rows.end(); ++ri) {
200                 ms = (*ri)[track_display_columns.strip];
201                 ms->set_width (ms->get_width());
202         }
203 }
204
205 void
206 Mixer_UI::add_strip (Route* route)
207 {
208         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::add_strip), route));
209         
210         MixerStrip* strip;
211         
212         if (route->hidden()) {
213                 return;
214         }
215
216         strip = new MixerStrip (*this, *session, *route);
217         strips.push_back (strip);
218
219         strip->set_width (_strip_width);
220         show_strip (strip);
221
222         TreeModel::Row row = *(track_display_model->append());
223         row[track_display_columns.text] = route->name();
224         row[track_display_columns.route] = route;
225         row[track_display_columns.strip] = strip;
226
227         if (strip->marked_for_display() || strip->packed()) {
228                 track_display.get_selection()->select (row);
229         }
230         
231         route->name_changed.connect (bind (mem_fun(*this, &Mixer_UI::strip_name_changed), strip));
232         strip->GoingAway.connect (bind (mem_fun(*this, &Mixer_UI::remove_strip), strip));
233
234         strip->signal_button_release_event().connect (bind (mem_fun(*this, &Mixer_UI::strip_button_release_event), strip));
235
236 //      if (width() < gdk_screen_width()) {
237 //              set_size_request (width() + (_strip_width == Wide ? 75 : 50), height());
238 //      }
239 }
240
241 void
242 Mixer_UI::remove_strip (MixerStrip* strip)
243 {
244         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::remove_strip), strip));
245         
246         TreeModel::Children rows = track_display_model->children();
247         TreeModel::Children::iterator ri;
248         list<MixerStrip *>::iterator i;
249
250         if ((i = find (strips.begin(), strips.end(), strip)) != strips.end()) {
251                 strips.erase (i);
252         }
253
254         for (ri = rows.begin(); ri != rows.end(); ++ri) {
255                 if ((*ri)[track_display_columns.strip] == strip) {
256                         track_display_model->erase (ri);
257                         break;
258                 }
259         }
260 }
261
262 void
263 Mixer_UI::follow_strip_selection ()
264 {
265         for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
266                 (*i)->set_selected (_selection.selected (&(*i)->route()));
267         }
268 }
269
270 gint
271 Mixer_UI::strip_button_release_event (GdkEventButton *ev, MixerStrip *strip)
272 {
273         if (ev->button == 1) {
274
275                 /* this allows the user to click on the strip to terminate comment
276                    editing. XXX it needs improving so that we don't select the strip
277                    at the same time.
278                 */
279                 
280                 if (_selection.selected (&strip->route())) {
281                         _selection.remove (&strip->route());
282                 } else {
283                         if (Keyboard::modifier_state_equals (ev->state, Keyboard::Shift)) {
284                                 _selection.add (&strip->route());
285                         } else {
286                                 _selection.set (&strip->route());
287                         }
288                 }
289         }
290
291         return TRUE;
292 }
293
294 void
295 Mixer_UI::connect_to_session (Session* sess)
296 {
297         session = sess;
298
299         string wintitle = _("ardour: mixer: ");
300         wintitle += session->name();
301         set_title (wintitle);
302
303         // GTK2FIX
304         // track_display_list.freeze ();
305
306         track_display_model.clear ();
307
308         session->foreach_route (this, &Mixer_UI::add_strip);
309         
310         // track_display_list.thaw ();
311
312         session->going_away.connect (mem_fun(*this, &Mixer_UI::disconnect_from_session));
313         session->RouteAdded.connect (mem_fun(*this, &Mixer_UI::add_strip));
314         session->mix_group_added.connect (mem_fun(*this, &Mixer_UI::add_mix_group));
315
316         session->foreach_mix_group(this, &Mixer_UI::add_mix_group);
317         
318         session->StateSaved.connect (mem_fun(*this, &Mixer_UI::session_state_saved));
319         redisplay_snapshots ();
320         
321         _plugin_selector->set_session (session);
322
323         start_updating ();
324 }
325
326 void
327 Mixer_UI::disconnect_from_session ()
328 {
329         ENSURE_GUI_THREAD(mem_fun(*this, &Mixer_UI::disconnect_from_session));
330         
331         group_display_model->clear ();
332         set_title (_("ardour: mixer"));
333         stop_updating ();
334         hide_all_strips (false);
335 }
336
337 void
338 Mixer_UI::hide_all_strips (bool with_select)
339 {
340         TreeModel::Children rows = track_display_model->children();
341         TreeModel::Children::iterator i;
342
343         // GTK2FIX
344         // track_display_list.freeze ();
345         
346         for (i = rows.begin(); i != rows.end(); ++i) {
347                 
348                 TreeModel::Row row = (*i);
349                 MixerStrip* ms = row[track_display_columns.strip];
350                 
351                 if (with_select) {
352                         track_display.get_selection()->unselect (i);
353                 } else {
354                         hide_strip (ms);
355                 }
356         }
357
358         // track_display_list.thaw ();
359 }
360
361 void
362 Mixer_UI::unselect_all_strips ()
363 {
364         hide_all_strips (false);
365 }
366
367 void
368 Mixer_UI::select_all_strips ()
369 {
370         TreeModel::Children rows = track_display_model->children();
371         TreeModel::Children::iterator i;
372
373         for (i = rows.begin(); i != rows.end(); ++i) {
374                 track_display.get_selection()->select (i);
375         }
376 }
377
378 void
379 Mixer_UI::strip_select_op (bool audiotrack, bool select)
380 {
381         MixerStrip* ms;
382         TreeModel::Children rows = track_display_model->children();
383         TreeModel::Children::iterator i;
384
385         // GTK2FIX
386         // track_display_list.freeze ();
387         
388         for (i = rows.begin(); i != rows.end(); ++i) {
389                 ms = (*i)[track_display_columns.strip];
390
391                 if (ms->is_audio_track() == audiotrack) {
392                         if (select) {
393                                 track_display.get_selection()->select (i);
394                         } else {
395                                 track_display.get_selection()->unselect (i);
396                         }
397                 }
398         }
399         
400         // track_display_list.thaw ();  
401 }
402
403 void
404 Mixer_UI::select_all_audiotrack_strips ()
405 {
406         strip_select_op (true, true);
407 }
408 void
409 Mixer_UI::unselect_all_audiotrack_strips ()
410 {
411         strip_select_op (true, false);
412 }
413
414 void
415 Mixer_UI::select_all_audiobus_strips ()
416 {
417         strip_select_op (false, true);
418 }
419
420 void
421 Mixer_UI::unselect_all_audiobus_strips ()
422 {
423         strip_select_op (false, false);
424 }
425
426 void
427 Mixer_UI::show_strip (MixerStrip* ms)
428 {
429         if (!ms->packed()) {
430                 
431                 if (ms->route().master() || ms->route().control()) {
432                         out_packer.pack_start (*ms, false, false);
433                 } else {
434                         strip_packer.pack_start (*ms, false, false);
435                 }
436                 ms->set_packed (true);
437                 ms->show ();
438
439         }
440 }
441
442 void
443 Mixer_UI::hide_strip (MixerStrip* ms)
444 {
445         if (ms->packed()) {
446                 if (ms->route().master() || ms->route().control()) {
447                         out_packer.remove (*ms);
448                 } else {
449                         strip_packer.remove (*ms);
450                 }
451                 ms->set_packed (false);
452         }
453 }
454
455 gint
456 Mixer_UI::start_updating ()
457 {
458         screen_update_connection = ARDOUR_UI::instance()->RapidScreenUpdate.connect (mem_fun(*this, &Mixer_UI::update_strips));
459         fast_screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (mem_fun(*this, &Mixer_UI::fast_update_strips));
460         return 0;
461 }
462
463 gint
464 Mixer_UI::stop_updating ()
465 {
466         screen_update_connection.disconnect();
467         fast_screen_update_connection.disconnect();
468         return 0;
469 }
470
471 void
472 Mixer_UI::update_strips ()
473 {
474         if (is_mapped () && session) {
475                 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
476                         (*i)->update ();
477                 }
478         }
479 }
480
481 void
482 Mixer_UI::fast_update_strips ()
483 {
484         if (is_mapped () && session) {
485                 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
486                         (*i)->fast_update ();
487                 }
488         }
489 }
490
491 void
492 Mixer_UI::snapshot_display_selection_changed ()
493 {
494         TreeModel::iterator i = snapshot_display.get_selection()->get_selected();
495
496         Glib::ustring snap_name = (*i)[snapshot_display_columns.real_name];
497         
498         if (session->snap_name() == snap_name) {
499                 return;
500         }
501         
502         ARDOUR_UI::instance()->load_session(session->path(), string (snap_name));
503 }
504
505 bool
506 Mixer_UI::snapshot_display_button_press (GdkEventButton* ev)
507 {
508         return false;
509 }
510
511 void
512 Mixer_UI::track_display_selection_changed ()
513 {
514         MixerStrip* strip;
515         TreeModel::Children rows = track_display_model->children();
516         TreeModel::Children::iterator i;
517         Glib::RefPtr<TreeSelection> selection = track_display.get_selection();
518
519         for (i = rows.begin(); i != rows.end(); ++i) {
520                 if (selection->is_selected (i)) {
521                         strip->set_marked_for_display  (true);
522                         show_strip (strip);
523                 } else {
524                         strip->set_marked_for_display (false);
525                         hide_strip (strip);
526                 }
527         }
528         
529         track_display_reordered ();
530 }
531
532 void
533 Mixer_UI::select_strip_op (MixerStrip *strip, bool yn)
534 {
535         TreeModel::Children rows = track_display_model->children();
536         TreeModel::Children::iterator i;
537         Glib::RefPtr<TreeSelection> selection = track_display.get_selection();
538         
539         for (i = rows.begin(); i != rows.end(); ++i) {
540                 if ((*i)[track_display_columns.strip] == strip) {
541                         if (yn) {
542                                 selection->select (*i);
543                         } else {
544                                 selection->unselect (*i);
545                         }
546                         break;
547                 }
548         }
549 }
550
551 void
552 Mixer_UI::unselect_strip_in_display (MixerStrip *strip)
553 {
554         select_strip_op (strip, true);
555 }
556 void
557 Mixer_UI::select_strip_in_display (MixerStrip *strip)
558 {
559         select_strip_op (strip, false);
560 }
561
562 void
563 Mixer_UI::track_display_reordered_proxy (const TreeModel::Path& path, const TreeModel::iterator& i, int* n)
564 {
565         track_display_reordered ();
566 }
567
568 void
569 Mixer_UI::track_display_reordered ()
570 {
571         TreeModel::Children rows = track_display_model->children();
572         TreeModel::Children::iterator i;
573         long order;
574         
575         for (order = 0, i = rows.begin(); i != rows.end(); ++i, ++order) {
576                 MixerStrip* strip = (*i)[track_display_columns.strip];
577
578                 if (strip->marked_for_display()) {
579                         strip->route().set_order_key (N_("signal"), order);
580                         strip_packer.reorder_child (*strip, -1); /* put at end */
581                 }
582         }
583 }
584
585 void
586 Mixer_UI::track_column_click (gint col)
587 {
588         if (track_menu == 0) {
589                 build_track_menu ();
590         }
591
592         track_menu->popup (0, 0);
593 }
594
595 void
596 Mixer_UI::build_track_menu ()
597 {
598         using namespace Menu_Helpers;
599         using namespace Gtk;
600
601         track_menu = new Menu;
602         track_menu->set_name ("ArdourContextMenu");
603         MenuList& items = track_menu->items();
604         track_menu->set_name ("ArdourContextMenu");
605         
606         items.push_back (MenuElem (_("Show All"), mem_fun(*this, &Mixer_UI::select_all_strips)));
607         items.push_back (MenuElem (_("Hide All"), mem_fun(*this, &Mixer_UI::unselect_all_strips)));
608         items.push_back (MenuElem (_("Show All AudioTrack MixerStrips"), mem_fun(*this, &Mixer_UI::select_all_audiotrack_strips)));
609         items.push_back (MenuElem (_("Hide All AudioTrack MixerStrips"), mem_fun(*this, &Mixer_UI::unselect_all_audiotrack_strips)));
610         items.push_back (MenuElem (_("Show All AudioBus MixerStrips"), mem_fun(*this, &Mixer_UI::select_all_audiobus_strips)));
611         items.push_back (MenuElem (_("Hide All AudioBus MixerStrips"), mem_fun(*this, &Mixer_UI::unselect_all_audiobus_strips)));
612
613 }
614
615 void
616 Mixer_UI::strip_name_changed (void* src, MixerStrip* mx)
617 {
618         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::strip_name_changed), src, mx));
619         
620         TreeModel::Children rows = track_display_model->children();
621         TreeModel::Children::iterator i;
622         
623         for (i = rows.begin(); i != rows.end(); ++i) {
624                 if ((*i)[track_display_columns.strip] == mx) {
625                         (*i)[track_display_columns.text] = mx->route().name();
626                         return;
627                 }
628         } 
629
630         error << _("track display list item for renamed strip not found!") << endmsg;
631 }
632
633 void
634 Mixer_UI::new_mix_group ()
635 {
636         ArdourPrompter prompter;
637         string result;
638
639         prompter.set_prompt (_("Name for new mix group"));
640         prompter.show_all ();
641         
642         switch (prompter.run ()) {
643         case GTK_RESPONSE_ACCEPT:
644                 prompter.get_result (result);
645                 if (result.length()) {
646                         session->add_mix_group (result);
647                 }       
648                 break;
649         }
650 }
651
652 // GTK2FIX
653 //void
654 //Mixer_UI::group_display_button_clicked ()
655 //{
656 //      if (session) {
657 //              new_mix_group ();
658 //      }
659 //}
660
661 bool
662 Mixer_UI::group_display_button_press (GdkEventButton* ev)
663 {
664         RouteGroup* group;
665
666         TreeIter iter;
667         TreeModel::Path path;
668         TreeViewColumn* column;
669         int cellx;
670         int celly;
671
672         if (!group_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
673                 return false;
674         }
675         
676         switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
677         case 0:
678                 /* active column click */
679                 
680                 if ((iter = group_display_model->get_iter (path))) {
681                         /* path points to a valid node */
682                         if ((group = (*iter)[group_display_columns.group]) != 0) {
683                                 group->set_active (!group->is_active (), this);
684                         }
685                 }
686                 break;
687
688         case 1:
689                 if (Keyboard::is_edit_event (ev)) {
690                         // RouteGroup* group = (RouteGroup *) group_display.row(row).get_data ();
691                         // edit_mix_group (group);
692
693                 } else {
694                         /* allow regular select to occur */
695                         return false;
696                 }
697                 break;
698         }
699                 
700         return true;
701 }
702
703 void
704 Mixer_UI::group_display_selection_changed ()
705 {
706         TreeModel::iterator i;
707         TreeModel::Children rows = group_display_model->children();
708         Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
709
710         for (i = rows.begin(); i != rows.end(); ++i) {
711                 RouteGroup* group;
712
713                 group = (*i)[group_display_columns.group];
714
715                 if (selection->is_selected (i)) {
716                         group->set_hidden (true, this);
717                 } else {
718                         group->set_hidden (true, this);
719                 }
720         }
721
722         track_display_reordered ();
723 }
724
725 void
726 Mixer_UI::group_flags_changed (void* src, RouteGroup* group)
727 {
728         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::group_flags_changed), src, group));
729         
730         if (src != this) {
731                 // select row
732         }
733
734         TreeModel::Children rows = group_display_model->children();
735         TreeModel::Children::iterator gi;
736         TreeModel::Children::iterator ti;
737
738         for (gi = rows.begin(); gi != rows.end(); ++gi) {
739                 if ((*gi)[group_display_columns.group] == group) {
740                         break;
741                 }
742         }
743
744         if (gi == rows.end()) {
745                 return;
746         }
747                 
748         rows = track_display_model->children();
749
750         for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
751                 if ((*i)->route().mix_group() == group) {
752                         if (group->is_hidden ()) {
753                                 unselect_strip_in_display(*i);
754                                 group_display.get_selection()->unselect (*gi);
755                         } else {
756                                 select_strip_in_display(*i);
757                                 group_display.get_selection()->select (*gi);
758                         }
759                 }
760         }
761 }
762
763 void
764 Mixer_UI::add_mix_group (RouteGroup* group)
765
766 {
767         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::add_mix_group), group));
768
769         TreeModel::Row row = *(group_display_model->append());
770         row[group_display_columns.active] = group->is_active();
771         row[group_display_columns.text] = group->name();
772         row[group_display_columns.group] = group;
773
774         group->FlagsChanged.connect (bind (mem_fun(*this, &Mixer_UI::group_flags_changed), group));
775 }
776
777 void
778 Mixer_UI::redisplay_snapshots ()
779 {
780         if (session == 0) {
781                 return;
782         }
783
784         vector<string*>* states;
785         if ((states = session->possible_states()) == 0) {
786                 return;
787         }
788
789         snapshot_display_model.clear ();
790
791         for (vector<string*>::iterator i = states->begin(); i != states->end(); ++i) {
792                 string statename = *(*i);
793                 TreeModel::Row row = *(snapshot_display_model->append());
794                 row[snapshot_display_columns.visible_name] = statename;
795                 row[snapshot_display_columns.real_name] = statename;
796         }
797
798         delete states;
799 }
800
801 void
802 Mixer_UI::session_state_saved (string snap_name)
803 {
804         ENSURE_GUI_THREAD (bind (mem_fun(*this, &Mixer_UI::session_state_saved), snap_name));
805         redisplay_snapshots ();
806 }
807
808 gint
809 Mixer_UI::strip_scroller_button_release (GdkEventButton* ev)
810 {
811         using namespace Menu_Helpers;
812
813         if (Keyboard::is_context_menu_event (ev)) {
814                 ARDOUR_UI::instance()->add_route();
815                 return TRUE;
816         }
817
818         return FALSE;
819 }
820
821 void
822 Mixer_UI::set_strip_width (Width w)
823 {
824         _strip_width = w;
825
826         for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
827                 (*i)->set_width (w);
828         }
829 }
830
831
832 int
833 Mixer_UI::set_state (const XMLNode& node)
834 {
835         const XMLProperty* prop;
836         XMLNode* geometry;
837         int x, y, width, height, xoff, yoff;
838         
839         if ((geometry = find_named_node (node, "geometry")) == 0) {
840
841                 width = default_width;
842                 height = default_height;
843                 x = 1;
844                 y = 1;
845                 xoff = 0;
846                 yoff = 21;
847
848         } else {
849
850                 width = atoi(geometry->property("x_size")->value().c_str());
851                 height = atoi(geometry->property("y_size")->value().c_str());
852                 x = atoi(geometry->property("x_pos")->value().c_str());
853                 y = atoi(geometry->property("y_pos")->value().c_str());
854                 xoff = atoi(geometry->property("x_off")->value().c_str());
855                 yoff = atoi(geometry->property("y_off")->value().c_str());
856         }
857                 
858         set_default_size(width, height);
859         // GTK2FIX
860         // set_uposition(x, y-yoff);
861
862         if ((prop = node.property ("narrow-strips"))) {
863                 if (prop->value() == "yes") {
864                         set_strip_width (Narrow);
865                 } else {
866                         set_strip_width (Wide);
867                 }
868         }
869
870         return 0;
871 }
872
873 XMLNode&
874 Mixer_UI::get_state (void)
875 {
876         XMLNode* node = new XMLNode ("Mixer");
877
878         if (is_realized()) {
879                 Glib::RefPtr<Gdk::Window> win = get_window();
880                 
881                 int x, y, xoff, yoff, width, height;
882                 win->get_root_origin(x, y);
883                 win->get_position(xoff, yoff);
884                 win->get_size(width, height);
885
886                 XMLNode* geometry = new XMLNode ("geometry");
887                 char buf[32];
888                 snprintf(buf, sizeof(buf), "%d", width);
889                 geometry->add_property(X_("x_size"), string(buf));
890                 snprintf(buf, sizeof(buf), "%d", height);
891                 geometry->add_property(X_("y_size"), string(buf));
892                 snprintf(buf, sizeof(buf), "%d", x);
893                 geometry->add_property(X_("x_pos"), string(buf));
894                 snprintf(buf, sizeof(buf), "%d", y);
895                 geometry->add_property(X_("y_pos"), string(buf));
896                 snprintf(buf, sizeof(buf), "%d", xoff);
897                 geometry->add_property(X_("x_off"), string(buf));
898                 snprintf(buf, sizeof(buf), "%d", yoff);
899                 geometry->add_property(X_("y_off"), string(buf));
900
901                 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&rhs_pane1)->gobj()));
902                 geometry->add_property(X_("mixer_rhs_pane1_pos"), string(buf));
903                 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&rhs_pane2)->gobj()));
904                 geometry->add_property(X_("mixer_rhs_pane2_pos"), string(buf));
905                 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&list_hpane)->gobj()));
906                 geometry->add_property(X_("mixer_list_hpane_pos"), string(buf));
907
908                 node->add_child_nocopy (*geometry);
909         }
910
911         node->add_property ("narrow-strips", _strip_width == Narrow ? "yes" : "no");
912
913         return *node;
914 }
915
916
917 void 
918 Mixer_UI::pane_allocation_handler (Allocation& alloc, Gtk::Paned* which)
919 {
920         int pos;
921         XMLProperty* prop = 0;
922         char buf[32];
923         XMLNode* node = ARDOUR_UI::instance()->mixer_settings();
924         XMLNode* geometry;
925         int width, height;
926         static int32_t done[3] = { 0, 0, 0 };
927
928         if ((geometry = find_named_node (*node, "geometry")) == 0) {
929                 width = default_width;
930                 height = default_height;
931         } else {
932                 width = atoi(geometry->property("x_size")->value());
933                 height = atoi(geometry->property("y_size")->value());
934         }
935
936         if (which == static_cast<Gtk::Paned*> (&rhs_pane1)) {
937
938                 if (done[0]) {
939                         return;
940                 }
941
942                 if (!geometry || (prop = geometry->property("mixer_rhs_pane1_pos")) == 0) {
943                         pos = height / 3;
944                         snprintf (buf, sizeof(buf), "%d", pos);
945                 } else {
946                         pos = atoi (prop->value());
947                 }
948
949                 if ((done[0] = GTK_WIDGET(rhs_pane1.gobj())->allocation.height > pos)) {
950                         rhs_pane1.set_position (pos);
951                 }
952
953         } else if (which == static_cast<Gtk::Paned*> (&rhs_pane2)) {
954
955                 if (done[1]) {
956                         return;
957                 }
958
959                 if (!geometry || (prop = geometry->property("mixer_rhs_pane2_pos")) == 0) {
960                         pos = height / 3;
961                         snprintf (buf, sizeof(buf), "%d", pos);
962                 } else {
963                         pos = atoi (prop->value());
964                 }
965
966                 if ((done[1] = GTK_WIDGET(rhs_pane2.gobj())->allocation.height > pos)) {
967                         rhs_pane2.set_position (pos);
968                 }
969
970         } else if (which == static_cast<Gtk::Paned*> (&list_hpane)) {
971
972                 if (done[2]) {
973                         return;
974                 }
975
976                 if (!geometry || (prop = geometry->property("mixer_list_hpane_pos")) == 0) {
977                         pos = 75;
978                         snprintf (buf, sizeof(buf), "%d", pos);
979                 } else {
980                         pos = atoi (prop->value());
981                 }
982
983                 if ((done[2] = GTK_WIDGET(list_hpane.gobj())->allocation.width > pos)) {
984                         list_hpane.set_position (pos);
985                 }
986         }
987 }
988