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