mega commit to remove gtk_object cruft, and much other stuff
[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->set_marked_for_display  (true);
520                         show_strip (strip);
521                 } else {
522                         strip->set_marked_for_display (false);
523                         hide_strip (strip);
524                 }
525         }
526         
527         track_display_reordered ();
528 }
529
530 void
531 Mixer_UI::select_strip_op (MixerStrip *strip, bool yn)
532 {
533         TreeModel::Children rows = track_display_model->children();
534         TreeModel::Children::iterator i;
535         Glib::RefPtr<TreeSelection> selection = track_display.get_selection();
536         
537         for (i = rows.begin(); i != rows.end(); ++i) {
538                 if ((*i)[track_display_columns.strip] == strip) {
539                         if (yn) {
540                                 selection->select (*i);
541                         } else {
542                                 selection->unselect (*i);
543                         }
544                         break;
545                 }
546         }
547 }
548
549 void
550 Mixer_UI::unselect_strip_in_display (MixerStrip *strip)
551 {
552         select_strip_op (strip, true);
553 }
554 void
555 Mixer_UI::select_strip_in_display (MixerStrip *strip)
556 {
557         select_strip_op (strip, false);
558 }
559
560 void
561 Mixer_UI::track_display_reordered_proxy (const TreeModel::Path& path, const TreeModel::iterator& i, int* n)
562 {
563         track_display_reordered ();
564 }
565
566 void
567 Mixer_UI::track_display_reordered ()
568 {
569         TreeModel::Children rows = track_display_model->children();
570         TreeModel::Children::iterator i;
571         long order;
572         
573         for (order = 0, i = rows.begin(); i != rows.end(); ++i, ++order) {
574                 MixerStrip* strip = (*i)[track_display_columns.strip];
575
576                 if (strip->marked_for_display()) {
577                         strip->route().set_order_key (N_("signal"), order);
578                         strip_packer.reorder_child (*strip, -1); /* put at end */
579                 }
580         }
581 }
582
583 void
584 Mixer_UI::track_column_click (gint col)
585 {
586         if (track_menu == 0) {
587                 build_track_menu ();
588         }
589
590         track_menu->popup (0, 0);
591 }
592
593 void
594 Mixer_UI::build_track_menu ()
595 {
596         using namespace Menu_Helpers;
597         using namespace Gtk;
598
599         track_menu = new Menu;
600         track_menu->set_name ("ArdourContextMenu");
601         MenuList& items = track_menu->items();
602         track_menu->set_name ("ArdourContextMenu");
603         
604         items.push_back (MenuElem (_("Show All"), mem_fun(*this, &Mixer_UI::select_all_strips)));
605         items.push_back (MenuElem (_("Hide All"), mem_fun(*this, &Mixer_UI::unselect_all_strips)));
606         items.push_back (MenuElem (_("Show All AudioTrack MixerStrips"), mem_fun(*this, &Mixer_UI::select_all_audiotrack_strips)));
607         items.push_back (MenuElem (_("Hide All AudioTrack MixerStrips"), mem_fun(*this, &Mixer_UI::unselect_all_audiotrack_strips)));
608         items.push_back (MenuElem (_("Show All AudioBus MixerStrips"), mem_fun(*this, &Mixer_UI::select_all_audiobus_strips)));
609         items.push_back (MenuElem (_("Hide All AudioBus MixerStrips"), mem_fun(*this, &Mixer_UI::unselect_all_audiobus_strips)));
610
611 }
612
613 void
614 Mixer_UI::strip_name_changed (void* src, MixerStrip* mx)
615 {
616         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::strip_name_changed), src, mx));
617         
618         TreeModel::Children rows = track_display_model->children();
619         TreeModel::Children::iterator i;
620         
621         for (i = rows.begin(); i != rows.end(); ++i) {
622                 if ((*i)[track_display_columns.strip] == mx) {
623                         (*i)[track_display_columns.text] = mx->route().name();
624                         return;
625                 }
626         } 
627
628         error << _("track display list item for renamed strip not found!") << endmsg;
629 }
630
631 void
632 Mixer_UI::new_mix_group ()
633 {
634         ArdourPrompter prompter;
635         string result;
636
637         prompter.set_prompt (_("Name for new mix group"));
638         prompter.show_all ();
639         
640         switch (prompter.run ()) {
641         case GTK_RESPONSE_ACCEPT:
642                 prompter.get_result (result);
643                 if (result.length()) {
644                         session->add_mix_group (result);
645                 }       
646                 break;
647         }
648 }
649
650 // GTK2FIX
651 //void
652 //Mixer_UI::group_display_button_clicked ()
653 //{
654 //      if (session) {
655 //              new_mix_group ();
656 //      }
657 //}
658
659 bool
660 Mixer_UI::group_display_button_press (GdkEventButton* ev)
661 {
662         RouteGroup* group;
663
664         TreeIter iter;
665         TreeModel::Path path;
666         TreeViewColumn* column;
667         int cellx;
668         int celly;
669
670         if (!group_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
671                 return false;
672         }
673         
674         switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
675         case 0:
676                 /* active column click */
677                 
678                 if ((iter = group_display_model->get_iter (path))) {
679                         /* path points to a valid node */
680                         if ((group = (*iter)[group_display_columns.group]) != 0) {
681                                 group->set_active (!group->is_active (), this);
682                         }
683                 }
684                 break;
685
686         case 1:
687                 if (Keyboard::is_edit_event (ev)) {
688                         // RouteGroup* group = (RouteGroup *) group_display.row(row).get_data ();
689                         // edit_mix_group (group);
690
691                 } else {
692                         /* allow regular select to occur */
693                         return false;
694                 }
695                 break;
696         }
697                 
698         return true;
699 }
700
701 void
702 Mixer_UI::group_display_selection_changed ()
703 {
704         TreeModel::iterator i;
705         TreeModel::Children rows = group_display_model->children();
706         Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
707
708         for (i = rows.begin(); i != rows.end(); ++i) {
709                 RouteGroup* group;
710
711                 group = (*i)[group_display_columns.group];
712
713                 if (selection->is_selected (i)) {
714                         group->set_hidden (true, this);
715                 } else {
716                         group->set_hidden (true, this);
717                 }
718         }
719
720         track_display_reordered ();
721 }
722
723 void
724 Mixer_UI::group_flags_changed (void* src, RouteGroup* group)
725 {
726         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::group_flags_changed), src, group));
727         
728         if (src != this) {
729                 // select row
730         }
731
732         TreeModel::Children rows = group_display_model->children();
733         TreeModel::Children::iterator gi;
734         TreeModel::Children::iterator ti;
735
736         for (gi = rows.begin(); gi != rows.end(); ++gi) {
737                 if ((*gi)[group_display_columns.group] == group) {
738                         break;
739                 }
740         }
741
742         if (gi == rows.end()) {
743                 return;
744         }
745                 
746         rows = track_display_model->children();
747
748         for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
749                 if ((*i)->route().mix_group() == group) {
750                         if (group->is_hidden ()) {
751                                 unselect_strip_in_display(*i);
752                                 group_display.get_selection()->unselect (*gi);
753                         } else {
754                                 select_strip_in_display(*i);
755                                 group_display.get_selection()->select (*gi);
756                         }
757                 }
758         }
759 }
760
761 void
762 Mixer_UI::add_mix_group (RouteGroup* group)
763
764 {
765         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::add_mix_group), group));
766
767         TreeModel::Row row = *(group_display_model->append());
768         row[group_display_columns.active] = group->is_active();
769         row[group_display_columns.text] = group->name();
770         row[group_display_columns.group] = group;
771
772         group->FlagsChanged.connect (bind (mem_fun(*this, &Mixer_UI::group_flags_changed), group));
773 }
774
775 void
776 Mixer_UI::redisplay_snapshots ()
777 {
778         if (session == 0) {
779                 return;
780         }
781
782         vector<string*>* states;
783         if ((states = session->possible_states()) == 0) {
784                 return;
785         }
786
787         snapshot_display_model->clear ();
788
789         for (vector<string*>::iterator i = states->begin(); i != states->end(); ++i) {
790                 string statename = *(*i);
791                 TreeModel::Row row = *(snapshot_display_model->append());
792                 row[snapshot_display_columns.visible_name] = statename;
793                 row[snapshot_display_columns.real_name] = statename;
794         }
795
796         delete states;
797 }
798
799 void
800 Mixer_UI::session_state_saved (string snap_name)
801 {
802         ENSURE_GUI_THREAD (bind (mem_fun(*this, &Mixer_UI::session_state_saved), snap_name));
803         redisplay_snapshots ();
804 }
805
806 gint
807 Mixer_UI::strip_scroller_button_release (GdkEventButton* ev)
808 {
809         using namespace Menu_Helpers;
810
811         if (Keyboard::is_context_menu_event (ev)) {
812                 ARDOUR_UI::instance()->add_route();
813                 return TRUE;
814         }
815
816         return FALSE;
817 }
818
819 void
820 Mixer_UI::set_strip_width (Width w)
821 {
822         _strip_width = w;
823
824         for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
825                 (*i)->set_width (w);
826         }
827 }
828
829
830 int
831 Mixer_UI::set_state (const XMLNode& node)
832 {
833         const XMLProperty* prop;
834         XMLNode* geometry;
835         int x, y, width, height, xoff, yoff;
836         
837         if ((geometry = find_named_node (node, "geometry")) == 0) {
838
839                 width = default_width;
840                 height = default_height;
841                 x = 1;
842                 y = 1;
843                 xoff = 0;
844                 yoff = 21;
845
846         } else {
847
848                 width = atoi(geometry->property("x_size")->value().c_str());
849                 height = atoi(geometry->property("y_size")->value().c_str());
850                 x = atoi(geometry->property("x_pos")->value().c_str());
851                 y = atoi(geometry->property("y_pos")->value().c_str());
852                 xoff = atoi(geometry->property("x_off")->value().c_str());
853                 yoff = atoi(geometry->property("y_off")->value().c_str());
854         }
855                 
856         set_default_size(width, height);
857         // GTK2FIX
858         // set_uposition(x, y-yoff);
859
860         if ((prop = node.property ("narrow-strips"))) {
861                 if (prop->value() == "yes") {
862                         set_strip_width (Narrow);
863                 } else {
864                         set_strip_width (Wide);
865                 }
866         }
867
868         return 0;
869 }
870
871 XMLNode&
872 Mixer_UI::get_state (void)
873 {
874         XMLNode* node = new XMLNode ("Mixer");
875
876         if (is_realized()) {
877                 Glib::RefPtr<Gdk::Window> win = get_window();
878                 
879                 int x, y, xoff, yoff, width, height;
880                 win->get_root_origin(x, y);
881                 win->get_position(xoff, yoff);
882                 win->get_size(width, height);
883
884                 XMLNode* geometry = new XMLNode ("geometry");
885                 char buf[32];
886                 snprintf(buf, sizeof(buf), "%d", width);
887                 geometry->add_property(X_("x_size"), string(buf));
888                 snprintf(buf, sizeof(buf), "%d", height);
889                 geometry->add_property(X_("y_size"), string(buf));
890                 snprintf(buf, sizeof(buf), "%d", x);
891                 geometry->add_property(X_("x_pos"), string(buf));
892                 snprintf(buf, sizeof(buf), "%d", y);
893                 geometry->add_property(X_("y_pos"), string(buf));
894                 snprintf(buf, sizeof(buf), "%d", xoff);
895                 geometry->add_property(X_("x_off"), string(buf));
896                 snprintf(buf, sizeof(buf), "%d", yoff);
897                 geometry->add_property(X_("y_off"), string(buf));
898
899                 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&rhs_pane1)->gobj()));
900                 geometry->add_property(X_("mixer_rhs_pane1_pos"), string(buf));
901                 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&rhs_pane2)->gobj()));
902                 geometry->add_property(X_("mixer_rhs_pane2_pos"), string(buf));
903                 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&list_hpane)->gobj()));
904                 geometry->add_property(X_("mixer_list_hpane_pos"), string(buf));
905
906                 node->add_child_nocopy (*geometry);
907         }
908
909         node->add_property ("narrow-strips", _strip_width == Narrow ? "yes" : "no");
910
911         return *node;
912 }
913
914
915 void 
916 Mixer_UI::pane_allocation_handler (Allocation& alloc, Gtk::Paned* which)
917 {
918         int pos;
919         XMLProperty* prop = 0;
920         char buf[32];
921         XMLNode* node = ARDOUR_UI::instance()->mixer_settings();
922         XMLNode* geometry;
923         int width, height;
924         static int32_t done[3] = { 0, 0, 0 };
925
926         if ((geometry = find_named_node (*node, "geometry")) == 0) {
927                 width = default_width;
928                 height = default_height;
929         } else {
930                 width = atoi(geometry->property("x_size")->value());
931                 height = atoi(geometry->property("y_size")->value());
932         }
933
934         if (which == static_cast<Gtk::Paned*> (&rhs_pane1)) {
935
936                 if (done[0]) {
937                         return;
938                 }
939
940                 if (!geometry || (prop = geometry->property("mixer_rhs_pane1_pos")) == 0) {
941                         pos = height / 3;
942                         snprintf (buf, sizeof(buf), "%d", pos);
943                 } else {
944                         pos = atoi (prop->value());
945                 }
946
947                 if ((done[0] = GTK_WIDGET(rhs_pane1.gobj())->allocation.height > pos)) {
948                         rhs_pane1.set_position (pos);
949                 }
950
951         } else if (which == static_cast<Gtk::Paned*> (&rhs_pane2)) {
952
953                 if (done[1]) {
954                         return;
955                 }
956
957                 if (!geometry || (prop = geometry->property("mixer_rhs_pane2_pos")) == 0) {
958                         pos = height / 3;
959                         snprintf (buf, sizeof(buf), "%d", pos);
960                 } else {
961                         pos = atoi (prop->value());
962                 }
963
964                 if ((done[1] = GTK_WIDGET(rhs_pane2.gobj())->allocation.height > pos)) {
965                         rhs_pane2.set_position (pos);
966                 }
967
968         } else if (which == static_cast<Gtk::Paned*> (&list_hpane)) {
969
970                 if (done[2]) {
971                         return;
972                 }
973
974                 if (!geometry || (prop = geometry->property("mixer_list_hpane_pos")) == 0) {
975                         pos = 75;
976                         snprintf (buf, sizeof(buf), "%d", pos);
977                 } else {
978                         pos = atoi (prop->value());
979                 }
980
981                 if ((done[2] = GTK_WIDGET(list_hpane.gobj())->allocation.width > pos)) {
982                         list_hpane.set_position (pos);
983                 }
984         }
985 }
986