remove <gtkmm/gtkmm.h> from all files, plus a small fix related to map/realize handling
[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
171         signal_configure_event().connect (mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::configure_handler));
172
173         _selection.RoutesChanged.connect (mem_fun(*this, &Mixer_UI::follow_strip_selection));
174 }
175
176 Mixer_UI::~Mixer_UI ()
177 {
178 }
179
180 void
181 Mixer_UI::ensure_float (Window& win)
182 {
183         win.set_transient_for (*this);
184 }
185
186 void
187 Mixer_UI::show_window ()
188 {
189         show_all ();
190
191         /* now reset each strips width so the right widgets are shown */
192         MixerStrip* ms;
193
194         TreeModel::Children rows = track_display_model->children();
195         TreeModel::Children::iterator ri;
196
197         for (ri = rows.begin(); ri != rows.end(); ++ri) {
198                 ms = (*ri)[track_display_columns.strip];
199                 ms->set_width (ms->get_width());
200         }
201 }
202
203 void
204 Mixer_UI::add_strip (Route* route)
205 {
206         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::add_strip), route));
207         
208         MixerStrip* strip;
209         
210         if (route->hidden()) {
211                 return;
212         }
213
214         strip = new MixerStrip (*this, *session, *route);
215         strips.push_back (strip);
216
217         strip->set_width (_strip_width);
218         show_strip (strip);
219
220         TreeModel::Row row = *(track_display_model->append());
221         row[track_display_columns.text] = route->name();
222         row[track_display_columns.route] = route;
223         row[track_display_columns.strip] = strip;
224
225         if (strip->marked_for_display() || strip->packed()) {
226                 track_display.get_selection()->select (row);
227         }
228         
229         route->name_changed.connect (bind (mem_fun(*this, &Mixer_UI::strip_name_changed), strip));
230         strip->GoingAway.connect (bind (mem_fun(*this, &Mixer_UI::remove_strip), strip));
231
232         strip->signal_button_release_event().connect (bind (mem_fun(*this, &Mixer_UI::strip_button_release_event), strip));
233
234 //      if (width() < gdk_screen_width()) {
235 //              set_size_request (width() + (_strip_width == Wide ? 75 : 50), height());
236 //      }
237 }
238
239 void
240 Mixer_UI::remove_strip (MixerStrip* strip)
241 {
242         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::remove_strip), strip));
243         
244         TreeModel::Children rows = track_display_model->children();
245         TreeModel::Children::iterator ri;
246         list<MixerStrip *>::iterator i;
247
248         if ((i = find (strips.begin(), strips.end(), strip)) != strips.end()) {
249                 strips.erase (i);
250         }
251
252         for (ri = rows.begin(); ri != rows.end(); ++ri) {
253                 if ((*ri)[track_display_columns.strip] == strip) {
254                         track_display_model->erase (ri);
255                         break;
256                 }
257         }
258 }
259
260 void
261 Mixer_UI::follow_strip_selection ()
262 {
263         for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
264                 (*i)->set_selected (_selection.selected (&(*i)->route()));
265         }
266 }
267
268 gint
269 Mixer_UI::strip_button_release_event (GdkEventButton *ev, MixerStrip *strip)
270 {
271         if (ev->button == 1) {
272
273                 /* this allows the user to click on the strip to terminate comment
274                    editing. XXX it needs improving so that we don't select the strip
275                    at the same time.
276                 */
277                 
278                 if (_selection.selected (&strip->route())) {
279                         _selection.remove (&strip->route());
280                 } else {
281                         if (Keyboard::modifier_state_equals (ev->state, Keyboard::Shift)) {
282                                 _selection.add (&strip->route());
283                         } else {
284                                 _selection.set (&strip->route());
285                         }
286                 }
287         }
288
289         return TRUE;
290 }
291
292 void
293 Mixer_UI::connect_to_session (Session* sess)
294 {
295         session = sess;
296
297         string wintitle = _("ardour: mixer: ");
298         wintitle += session->name();
299         set_title (wintitle);
300
301         // GTK2FIX
302         // track_display_list.freeze ();
303
304         track_display_model->clear ();
305
306         session->foreach_route (this, &Mixer_UI::add_strip);
307         
308         // track_display_list.thaw ();
309
310         session->going_away.connect (mem_fun(*this, &Mixer_UI::disconnect_from_session));
311         session->RouteAdded.connect (mem_fun(*this, &Mixer_UI::add_strip));
312         session->mix_group_added.connect (mem_fun(*this, &Mixer_UI::add_mix_group));
313
314         session->foreach_mix_group(this, &Mixer_UI::add_mix_group);
315         
316         session->StateSaved.connect (mem_fun(*this, &Mixer_UI::session_state_saved));
317         redisplay_snapshots ();
318         
319         _plugin_selector->set_session (session);
320
321         start_updating ();
322 }
323
324 void
325 Mixer_UI::disconnect_from_session ()
326 {
327         ENSURE_GUI_THREAD(mem_fun(*this, &Mixer_UI::disconnect_from_session));
328         
329         group_display_model->clear ();
330         set_title (_("ardour: mixer"));
331         stop_updating ();
332         hide_all_strips (false);
333 }
334
335 void
336 Mixer_UI::hide_all_strips (bool with_select)
337 {
338         TreeModel::Children rows = track_display_model->children();
339         TreeModel::Children::iterator i;
340
341         // GTK2FIX
342         // track_display_list.freeze ();
343         
344         for (i = rows.begin(); i != rows.end(); ++i) {
345                 
346                 TreeModel::Row row = (*i);
347                 MixerStrip* ms = row[track_display_columns.strip];
348                 
349                 if (with_select) {
350                         track_display.get_selection()->unselect (i);
351                 } else {
352                         hide_strip (ms);
353                 }
354         }
355
356         // track_display_list.thaw ();
357 }
358
359 void
360 Mixer_UI::unselect_all_strips ()
361 {
362         hide_all_strips (false);
363 }
364
365 void
366 Mixer_UI::select_all_strips ()
367 {
368         TreeModel::Children rows = track_display_model->children();
369         TreeModel::Children::iterator i;
370
371         for (i = rows.begin(); i != rows.end(); ++i) {
372                 track_display.get_selection()->select (i);
373         }
374 }
375
376 void
377 Mixer_UI::strip_select_op (bool audiotrack, bool select)
378 {
379         MixerStrip* ms;
380         TreeModel::Children rows = track_display_model->children();
381         TreeModel::Children::iterator i;
382
383         // GTK2FIX
384         // track_display_list.freeze ();
385         
386         for (i = rows.begin(); i != rows.end(); ++i) {
387                 ms = (*i)[track_display_columns.strip];
388
389                 if (ms->is_audio_track() == audiotrack) {
390                         if (select) {
391                                 track_display.get_selection()->select (i);
392                         } else {
393                                 track_display.get_selection()->unselect (i);
394                         }
395                 }
396         }
397         
398         // track_display_list.thaw ();  
399 }
400
401 void
402 Mixer_UI::select_all_audiotrack_strips ()
403 {
404         strip_select_op (true, true);
405 }
406 void
407 Mixer_UI::unselect_all_audiotrack_strips ()
408 {
409         strip_select_op (true, false);
410 }
411
412 void
413 Mixer_UI::select_all_audiobus_strips ()
414 {
415         strip_select_op (false, true);
416 }
417
418 void
419 Mixer_UI::unselect_all_audiobus_strips ()
420 {
421         strip_select_op (false, false);
422 }
423
424 void
425 Mixer_UI::show_strip (MixerStrip* ms)
426 {
427         if (!ms->packed()) {
428                 
429                 if (ms->route().master() || ms->route().control()) {
430                         out_packer.pack_start (*ms, false, false);
431                 } else {
432                         strip_packer.pack_start (*ms, false, false);
433                 }
434                 ms->set_packed (true);
435                 ms->show ();
436
437         }
438 }
439
440 void
441 Mixer_UI::hide_strip (MixerStrip* ms)
442 {
443         if (ms->packed()) {
444                 if (ms->route().master() || ms->route().control()) {
445                         out_packer.remove (*ms);
446                 } else {
447                         strip_packer.remove (*ms);
448                 }
449                 ms->set_packed (false);
450         }
451 }
452
453 gint
454 Mixer_UI::start_updating ()
455 {
456         screen_update_connection = ARDOUR_UI::instance()->RapidScreenUpdate.connect (mem_fun(*this, &Mixer_UI::update_strips));
457         fast_screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (mem_fun(*this, &Mixer_UI::fast_update_strips));
458         return 0;
459 }
460
461 gint
462 Mixer_UI::stop_updating ()
463 {
464         screen_update_connection.disconnect();
465         fast_screen_update_connection.disconnect();
466         return 0;
467 }
468
469 void
470 Mixer_UI::update_strips ()
471 {
472         if (is_mapped () && session) {
473                 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
474                         (*i)->update ();
475                 }
476         }
477 }
478
479 void
480 Mixer_UI::fast_update_strips ()
481 {
482         if (is_mapped () && session) {
483                 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
484                         (*i)->fast_update ();
485                 }
486         }
487 }
488
489 void
490 Mixer_UI::snapshot_display_selection_changed ()
491 {
492         TreeModel::iterator i = snapshot_display.get_selection()->get_selected();
493
494         Glib::ustring snap_name = (*i)[snapshot_display_columns.real_name];
495         
496         if (session->snap_name() == snap_name) {
497                 return;
498         }
499         
500         ARDOUR_UI::instance()->load_session(session->path(), string (snap_name));
501 }
502
503 bool
504 Mixer_UI::snapshot_display_button_press (GdkEventButton* ev)
505 {
506         return false;
507 }
508
509 void
510 Mixer_UI::track_display_selection_changed ()
511 {
512         MixerStrip* strip;
513         TreeModel::Children rows = track_display_model->children();
514         TreeModel::Children::iterator i;
515         Glib::RefPtr<TreeSelection> selection = track_display.get_selection();
516
517         for (i = rows.begin(); i != rows.end(); ++i) {
518                 if (selection->is_selected (i)) {
519                         strip = (*i)[track_display_columns.strip];
520                         strip->set_marked_for_display  (true);
521                         show_strip (strip);
522                 } else {
523                         strip = (*i)[track_display_columns.strip];
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