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