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