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