42fa7d105429926d7459fdfd0e936248384bd601
[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 */
19
20 #include <algorithm>
21 #include <sigc++/bind.h>
22
23 #include <gtkmm/accelmap.h>
24
25 #include "pbd/convert.h"
26 #include <glibmm/thread.h>
27
28 #include <gtkmm2ext/gtk_ui.h>
29 #include <gtkmm2ext/utils.h>
30 #include <gtkmm2ext/stop_signal.h>
31 #include <gtkmm2ext/window_title.h>
32
33 #include "ardour/audio_diskstream.h"
34 #include "ardour/audio_track.h"
35 #include "ardour/plugin_manager.h"
36 #include "ardour/route_group.h"
37 #include "ardour/session.h"
38 #include "ardour/session_route.h"
39
40 #include "keyboard.h"
41 #include "mixer_ui.h"
42 #include "mixer_strip.h"
43 #include "plugin_selector.h"
44 #include "ardour_ui.h"
45 #include "prompter.h"
46 #include "utils.h"
47 #include "actions.h"
48 #include "gui_thread.h"
49 #include "mixer_group_tabs.h"
50
51 #include "i18n.h"
52
53 using namespace ARDOUR;
54 using namespace PBD;
55 using namespace Gtk;
56 using namespace Glib;
57 using namespace Gtkmm2ext;
58 using namespace sigc;
59 using namespace std;
60
61 using PBD::atoi;
62
63 Mixer_UI::Mixer_UI ()
64         : Window (Gtk::WINDOW_TOPLEVEL)
65 {
66         session = 0;
67         _strip_width = Config->get_default_narrow_ms() ? Narrow : Wide;
68         track_menu = 0;
69         mix_group_context_menu = 0;
70         no_track_list_redisplay = false;
71         in_group_row_change = false;
72         _visible = false;
73         strip_redisplay_does_not_reset_order_keys = false;
74         strip_redisplay_does_not_sync_order_keys = false;
75         ignore_sync = false;
76
77         Route::SyncOrderKeys.connect (mem_fun (*this, &Mixer_UI::sync_order_keys));
78
79         scroller_base.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
80         scroller_base.set_name ("MixerWindow");
81         scroller_base.signal_button_release_event().connect (mem_fun(*this, &Mixer_UI::strip_scroller_button_release));
82         // add as last item of strip packer
83         strip_packer.pack_end (scroller_base, true, true);
84
85         _group_tabs = new MixerGroupTabs (this);
86         VBox* b = manage (new VBox);
87         b->pack_start (*_group_tabs, PACK_SHRINK);
88         b->pack_start (strip_packer);
89         b->show_all ();
90
91         scroller.add (*b);
92         scroller.set_policy (Gtk::POLICY_ALWAYS, Gtk::POLICY_AUTOMATIC);
93
94         track_model = ListStore::create (track_columns);
95         track_display.set_model (track_model);
96         track_display.append_column (_("Strips"), track_columns.text);
97         track_display.append_column (_("Show"), track_columns.visible);
98         track_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0));
99         track_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
100         track_display.get_column (0)->set_expand(true);
101         track_display.get_column (1)->set_expand(false);
102         track_display.set_name (X_("MixerTrackDisplayList"));
103         track_display.get_selection()->set_mode (Gtk::SELECTION_NONE);
104         track_display.set_reorderable (true);
105         track_display.set_headers_visible (true);
106
107         track_model->signal_row_deleted().connect (mem_fun (*this, &Mixer_UI::track_list_delete));
108         track_model->signal_row_changed().connect (mem_fun (*this, &Mixer_UI::track_list_change));
109         track_model->signal_rows_reordered().connect (mem_fun (*this, &Mixer_UI::track_list_reorder));
110
111         CellRendererToggle* track_list_visible_cell = dynamic_cast<CellRendererToggle*>(track_display.get_column_cell_renderer (1));
112         track_list_visible_cell->property_activatable() = true;
113         track_list_visible_cell->property_radio() = false;
114
115         track_display.signal_button_press_event().connect (mem_fun (*this, &Mixer_UI::track_display_button_press), false);
116
117         track_display_scroller.add (track_display);
118         track_display_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
119
120         group_model = ListStore::create (group_columns);
121         group_display.set_model (group_model);
122         group_display.append_column (_("Group"), group_columns.text);
123         group_display.append_column (_("Active"), group_columns.active);
124         group_display.append_column (_("Show"), group_columns.visible);
125         group_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0));
126         group_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
127         group_display.get_column (2)->set_data (X_("colnum"), GUINT_TO_POINTER(2));
128         group_display.get_column (0)->set_expand(true);
129         group_display.get_column (1)->set_expand(false);
130         group_display.get_column (2)->set_expand(false);
131         group_display.set_name ("MixerGroupList");
132         group_display.get_selection()->set_mode (Gtk::SELECTION_SINGLE);
133         group_display.set_reorderable (true);
134         group_display.set_headers_visible (true);
135         group_display.set_rules_hint (true);
136
137         /* name is directly editable */
138
139         CellRendererText* name_cell = dynamic_cast<CellRendererText*>(group_display.get_column_cell_renderer (0));
140         name_cell->property_editable() = true;
141         name_cell->signal_edited().connect (mem_fun (*this, &Mixer_UI::mix_group_name_edit));
142
143         /* use checkbox for the active column */
144
145         CellRendererToggle* active_cell = dynamic_cast<CellRendererToggle*>(group_display.get_column_cell_renderer (1));
146         active_cell->property_activatable() = true;
147         active_cell->property_radio() = false;
148
149         /* use checkbox for the visible column */
150
151         active_cell = dynamic_cast<CellRendererToggle*>(group_display.get_column_cell_renderer (2));
152         active_cell->property_activatable() = true;
153         active_cell->property_radio() = false;
154
155         group_model->signal_row_changed().connect (mem_fun (*this, &Mixer_UI::mix_group_row_change));
156
157         group_display.signal_button_press_event().connect (mem_fun (*this, &Mixer_UI::group_display_button_press), false);
158
159         group_display_scroller.add (group_display);
160         group_display_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
161
162         HBox* mix_group_display_button_box = manage (new HBox());
163
164         Button* mix_group_add_button = manage (new Button ());
165         Button* mix_group_remove_button = manage (new Button ());
166
167         Widget* w;
168
169         w = manage (new Image (Stock::ADD, ICON_SIZE_BUTTON));
170         w->show();
171         mix_group_add_button->add (*w);
172
173         w = manage (new Image (Stock::REMOVE, ICON_SIZE_BUTTON));
174         w->show();
175         mix_group_remove_button->add (*w);
176
177         mix_group_display_button_box->set_homogeneous (true);
178
179         mix_group_add_button->signal_clicked().connect (mem_fun (*this, &Mixer_UI::new_mix_group));
180         mix_group_remove_button->signal_clicked().connect (mem_fun (*this, &Mixer_UI::remove_selected_mix_group));
181
182         mix_group_display_button_box->add (*mix_group_remove_button);
183         mix_group_display_button_box->add (*mix_group_add_button);
184
185         group_display_vbox.pack_start (group_display_scroller, true, true);
186         group_display_vbox.pack_start (*mix_group_display_button_box, false, false);
187
188         track_display_frame.set_name("BaseFrame");
189         track_display_frame.set_shadow_type (Gtk::SHADOW_IN);
190         track_display_frame.add(track_display_scroller);
191
192         group_display_frame.set_name ("BaseFrame");
193         group_display_frame.set_shadow_type (Gtk::SHADOW_IN);
194         group_display_frame.add (group_display_vbox);
195
196         rhs_pane1.pack1 (track_display_frame);
197         rhs_pane1.pack2 (group_display_frame);
198
199         list_vpacker.pack_start (rhs_pane1, true, true);
200
201         global_hpacker.pack_start (scroller, true, true);
202 #ifdef GTKOSX
203         /* current gtk-quartz has dirty updates on borders like this one */
204         global_hpacker.pack_start (out_packer, false, false, 0);
205 #else
206         global_hpacker.pack_start (out_packer, false, false, 12);
207 #endif
208         list_hpane.add1(list_vpacker);
209         list_hpane.add2(global_hpacker);
210
211         rhs_pane1.signal_size_allocate().connect (bind (mem_fun(*this, &Mixer_UI::pane_allocation_handler), 
212                                                         static_cast<Gtk::Paned*> (&rhs_pane1)));
213         list_hpane.signal_size_allocate().connect (bind (mem_fun(*this, &Mixer_UI::pane_allocation_handler), 
214                                                          static_cast<Gtk::Paned*> (&list_hpane)));
215         
216         global_vpacker.pack_start (list_hpane, true, true);
217
218         add (global_vpacker);
219         set_name ("MixerWindow");
220         
221         WindowTitle title(Glib::get_application_name());
222         title += _("Mixer");
223         set_title (title.get_string());
224
225         set_wmclass (X_("ardour_mixer"), "Ardour");
226
227         add_accel_group (ActionManager::ui_manager->get_accel_group());
228
229         signal_delete_event().connect (mem_fun (*this, &Mixer_UI::hide_window));
230         add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
231
232         _plugin_selector = new PluginSelector (PluginManager::the_manager());
233
234         signal_configure_event().connect (mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::configure_handler));
235
236         _selection.RoutesChanged.connect (mem_fun(*this, &Mixer_UI::follow_strip_selection));
237
238         mix_group_display_button_box->show();
239         mix_group_add_button->show();
240         mix_group_remove_button->show();
241
242         global_hpacker.show();
243         global_vpacker.show();
244         scroller.show();
245         scroller_base.show();
246         scroller_hpacker.show();
247         mixer_scroller_vpacker.show();
248         list_vpacker.show();
249         group_display_button_label.show();
250         group_display_button.show();
251         track_display_scroller.show();
252         group_display_scroller.show();
253         group_display_vbox.show();
254         track_display_frame.show();
255         group_display_frame.show();
256         rhs_pane1.show();
257         strip_packer.show();
258         out_packer.show();
259         list_hpane.show();
260         track_display.show();
261         group_display.show();
262
263         auto_rebinding = FALSE;
264 }
265
266 Mixer_UI::~Mixer_UI ()
267 {
268 }
269
270 void
271 Mixer_UI::ensure_float (Window& win)
272 {
273         win.set_transient_for (*this);
274 }
275
276 void
277 Mixer_UI::show_window ()
278 {
279         present ();
280         if (!_visible) {
281                 set_window_pos_and_size ();
282
283                 /* now reset each strips width so the right widgets are shown */
284                 MixerStrip* ms;
285                 
286                 TreeModel::Children rows = track_model->children();
287                 TreeModel::Children::iterator ri;
288                 
289                 for (ri = rows.begin(); ri != rows.end(); ++ri) {
290                         ms = (*ri)[track_columns.strip];
291                         ms->set_width_enum (ms->get_width_enum (), ms->width_owner());
292                 }
293         }
294         _visible = true;
295 }
296
297 bool
298 Mixer_UI::hide_window (GdkEventAny *ev)
299 {
300         get_window_pos_and_size ();
301
302         _visible = false;
303         return just_hide_it(ev, static_cast<Gtk::Window *>(this));
304 }
305
306
307 void
308 Mixer_UI::add_strip (RouteList& routes)
309 {
310         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::add_strip), routes));
311         
312         MixerStrip* strip;
313
314         no_track_list_redisplay = true;
315         strip_redisplay_does_not_sync_order_keys = true;
316
317         for (RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
318                 boost::shared_ptr<Route> route = (*x);
319
320                 if (route->is_hidden()) {
321                         return;
322                 }
323                 
324                 strip = new MixerStrip (*this, *session, route);
325                 strips.push_back (strip);
326                 
327                 Config->get_default_narrow_ms() ? _strip_width = Narrow : _strip_width = Wide;
328
329                 Config->get_default_narrow_ms() ? _strip_width = Narrow : _strip_width = Wide;
330
331                 if (strip->width_owner() != strip) {
332                         strip->set_width_enum (_strip_width, this);
333                 }
334
335                 show_strip (strip);
336                 
337                 TreeModel::Row row = *(track_model->append());
338                 row[track_columns.text] = route->name();
339                 row[track_columns.visible] = strip->marked_for_display();
340                 row[track_columns.route] = route;
341                 row[track_columns.strip] = strip;
342
343                 if (route->order_key (N_("signal")) == -1) {
344                         route->set_order_key (N_("signal"), track_model->children().size()-1);
345                 }
346                 
347                 route->NameChanged.connect (bind (mem_fun(*this, &Mixer_UI::strip_name_changed), strip));
348
349                 strip->GoingAway.connect (bind (mem_fun(*this, &Mixer_UI::remove_strip), strip));
350 #ifdef GTKOSX
351                 strip->WidthChanged.connect (mem_fun(*this, &Mixer_UI::queue_draw_all_strips));
352 #endif  
353                 strip->signal_button_release_event().connect (bind (mem_fun(*this, &Mixer_UI::strip_button_release_event), strip));
354         }
355
356         no_track_list_redisplay = false;
357
358         redisplay_track_list ();
359         
360         strip_redisplay_does_not_sync_order_keys = false;
361 }
362
363 void
364 Mixer_UI::remove_strip (MixerStrip* strip)
365 {
366         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::remove_strip), strip));
367
368         TreeModel::Children rows = track_model->children();
369         TreeModel::Children::iterator ri;
370         list<MixerStrip *>::iterator i;
371
372         if ((i = find (strips.begin(), strips.end(), strip)) != strips.end()) {
373                 strips.erase (i);
374         }
375
376         strip_redisplay_does_not_sync_order_keys = true;
377
378         for (ri = rows.begin(); ri != rows.end(); ++ri) {
379                 if ((*ri)[track_columns.strip] == strip) {
380                         track_model->erase (ri);
381                         break;
382                 }
383         }
384
385         strip_redisplay_does_not_sync_order_keys = false;
386 }
387
388 const char*
389 Mixer_UI::get_order_key() 
390 {
391         return X_("signal");
392 #if 0
393         if (Config->get_sync_all_route_ordering()) {
394                 return X_("editor");
395         } else {
396                 return X_("signal");
397         }
398 #endif
399 }
400
401 void
402 Mixer_UI::sync_order_keys (const char *src)
403 {
404         vector<int> neworder;
405         TreeModel::Children rows = track_model->children();
406         TreeModel::Children::iterator ri;
407
408         if ((strcmp (src, get_order_key()) == 0) || !session || (session->state_of_the_state() & Session::Loading) || rows.empty()) {
409                 return;
410         }
411
412         for (ri = rows.begin(); ri != rows.end(); ++ri) {
413                 neworder.push_back (0);
414         }
415
416         bool changed = false;
417         int order;
418
419         for (order = 0, ri = rows.begin(); ri != rows.end(); ++ri, ++order) {
420                 boost::shared_ptr<Route> route = (*ri)[track_columns.route];
421                 int old_key = order;
422                 int new_key = route->order_key (get_order_key());
423
424                 neworder[new_key] = old_key;
425
426                 if (new_key != old_key) {
427                         changed = true;
428                 }
429         }
430
431         if (changed) {
432                 strip_redisplay_does_not_reset_order_keys = true;
433                 track_model->reorder (neworder);
434                 strip_redisplay_does_not_reset_order_keys = false;
435         }
436 }
437
438 void
439 Mixer_UI::follow_strip_selection ()
440 {
441         for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
442                 (*i)->set_selected (_selection.selected ((*i)->route()));
443         }
444 }
445
446 bool
447 Mixer_UI::strip_button_release_event (GdkEventButton *ev, MixerStrip *strip)
448 {
449         if (ev->button == 1) {
450
451                 /* this allows the user to click on the strip to terminate comment
452                    editing. XXX it needs improving so that we don't select the strip
453                    at the same time.
454                 */
455                 
456                 if (_selection.selected (strip->route())) {
457                         _selection.remove (strip->route());
458                 } else {
459                         if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
460                                 _selection.add (strip->route());
461                         } else {
462                                 _selection.set (strip->route());
463                         }
464                 }
465         }
466
467         return true;
468 }
469
470 void
471 Mixer_UI::connect_to_session (Session* sess)
472 {
473         session = sess;
474
475         XMLNode* node = ARDOUR_UI::instance()->mixer_settings();
476         set_state (*node);
477
478         WindowTitle title(session->name());
479         title += _("Mixer");
480         title += Glib::get_application_name();
481
482         set_title (title.get_string());
483
484         initial_track_display ();
485
486         session->GoingAway.connect (mem_fun(*this, &Mixer_UI::disconnect_from_session));
487         session->RouteAdded.connect (mem_fun(*this, &Mixer_UI::add_strip));
488         session->mix_group_added.connect (mem_fun(*this, &Mixer_UI::add_mix_group));
489         session->mix_group_removed.connect (mem_fun(*this, &Mixer_UI::mix_groups_changed));
490         session->config.ParameterChanged.connect (mem_fun (*this, &Mixer_UI::parameter_changed));
491
492         mix_groups_changed ();
493         
494         _plugin_selector->set_session (session);
495
496         if (_visible) {
497                show_window();
498         }
499
500         _group_tabs->set_session (sess);
501
502         start_updating ();
503 }
504
505 void
506 Mixer_UI::disconnect_from_session ()
507 {
508         ENSURE_GUI_THREAD(mem_fun(*this, &Mixer_UI::disconnect_from_session));
509         
510         group_model->clear ();
511         _selection.clear ();
512
513         WindowTitle title(Glib::get_application_name());
514         title += _("Mixer");
515         set_title (title.get_string());
516         
517         stop_updating ();
518 }
519
520 void
521 Mixer_UI::show_strip (MixerStrip* ms)
522 {
523         TreeModel::Children rows = track_model->children();
524         TreeModel::Children::iterator i;
525         
526         for (i = rows.begin(); i != rows.end(); ++i) {
527         
528                 MixerStrip* strip = (*i)[track_columns.strip];
529                 if (strip == ms) {
530                         (*i)[track_columns.visible] = true;
531                         break;
532                 }
533         }
534 }
535
536 void
537 Mixer_UI::hide_strip (MixerStrip* ms)
538 {
539         TreeModel::Children rows = track_model->children();
540         TreeModel::Children::iterator i;
541         
542         for (i = rows.begin(); i != rows.end(); ++i) {
543                 
544                 MixerStrip* strip = (*i)[track_columns.strip];
545                 if (strip == ms) {
546                         (*i)[track_columns.visible] = false;
547                         break;
548                 }
549         }
550 }
551
552 gint
553 Mixer_UI::start_updating ()
554 {
555     fast_screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (mem_fun(*this, &Mixer_UI::fast_update_strips));
556     return 0;
557 }
558
559 gint
560 Mixer_UI::stop_updating ()
561 {
562     fast_screen_update_connection.disconnect();
563     return 0;
564 }
565
566 void
567 Mixer_UI::fast_update_strips ()
568 {
569         if (is_mapped () && session) {
570                 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
571                         (*i)->fast_update ();
572                 }
573         }
574 }
575
576 void
577 Mixer_UI::set_all_strips_visibility (bool yn)
578 {
579         TreeModel::Children rows = track_model->children();
580         TreeModel::Children::iterator i;
581
582         no_track_list_redisplay = true;
583
584         for (i = rows.begin(); i != rows.end(); ++i) {
585
586                 TreeModel::Row row = (*i);
587                 MixerStrip* strip = row[track_columns.strip];
588                 
589                 if (strip == 0) {
590                         continue;
591                 }
592                 
593                 if (strip->route()->is_master() || strip->route()->is_control()) {
594                         continue;
595                 }
596
597                 (*i)[track_columns.visible] = yn;
598         }
599
600         no_track_list_redisplay = false;
601         redisplay_track_list ();
602 }
603
604
605 void
606 Mixer_UI::set_all_audio_visibility (int tracks, bool yn) 
607 {
608         TreeModel::Children rows = track_model->children();
609         TreeModel::Children::iterator i;
610
611         no_track_list_redisplay = true;
612
613         for (i = rows.begin(); i != rows.end(); ++i) {
614                 TreeModel::Row row = (*i);
615                 MixerStrip* strip = row[track_columns.strip];
616
617                 if (strip == 0) {
618                         continue;
619                 }
620
621                 if (strip->route()->is_master() || strip->route()->is_control()) {
622                         continue;
623                 }
624
625                 boost::shared_ptr<AudioTrack> at = strip->audio_track();
626
627                 switch (tracks) {
628                 case 0:
629                         (*i)[track_columns.visible] = yn;
630                         break;
631                         
632                 case 1:
633                         if (at) { /* track */
634                                 (*i)[track_columns.visible] = yn;
635                         }
636                         break;
637                         
638                 case 2:
639                         if (!at) { /* bus */
640                                 (*i)[track_columns.visible] = yn;
641                         }
642                         break;
643                 }
644         }
645
646         no_track_list_redisplay = false;
647         redisplay_track_list ();
648 }
649
650 void
651 Mixer_UI::hide_all_routes ()
652 {
653         set_all_strips_visibility (false);
654 }
655
656 void
657 Mixer_UI::show_all_routes ()
658 {
659         set_all_strips_visibility (true);
660 }
661
662 void
663 Mixer_UI::show_all_audiobus ()
664 {
665         set_all_audio_visibility (2, true);
666 }
667 void
668 Mixer_UI::hide_all_audiobus ()
669 {
670         set_all_audio_visibility (2, false);
671 }
672
673 void
674 Mixer_UI::show_all_audiotracks()
675 {
676         set_all_audio_visibility (1, true);
677 }
678 void
679 Mixer_UI::hide_all_audiotracks ()
680 {
681         set_all_audio_visibility (1, false);
682 }
683
684 void
685 Mixer_UI::track_list_reorder (const TreeModel::Path& path, const TreeModel::iterator& iter, int* new_order)
686 {
687         strip_redisplay_does_not_sync_order_keys = true;
688         session->set_remote_control_ids();
689         redisplay_track_list ();
690         strip_redisplay_does_not_sync_order_keys = false;
691 }
692
693 void
694 Mixer_UI::track_list_change (const Gtk::TreeModel::Path& path,const Gtk::TreeModel::iterator& iter)
695 {
696         // never reset order keys because of a property change
697         strip_redisplay_does_not_reset_order_keys = true; 
698         session->set_remote_control_ids();
699         redisplay_track_list ();
700         strip_redisplay_does_not_reset_order_keys = false;
701 }
702
703 void
704 Mixer_UI::track_list_delete (const Gtk::TreeModel::Path& path)
705 {
706         /* this could require an order sync */
707         session->set_remote_control_ids();
708         redisplay_track_list ();
709 }
710
711 void
712 Mixer_UI::redisplay_track_list ()
713 {
714         TreeModel::Children rows = track_model->children();
715         TreeModel::Children::iterator i;
716         long order;
717
718         if (no_track_list_redisplay) {
719                 return;
720         }
721
722         for (order = 0, i = rows.begin(); i != rows.end(); ++i, ++order) {
723                 MixerStrip* strip = (*i)[track_columns.strip];
724
725                 if (strip == 0) {
726                         /* we're in the middle of changing a row, don't worry */
727                         continue;
728                 }
729
730                 bool visible = (*i)[track_columns.visible];
731
732                 if (visible) {
733                         strip->set_marked_for_display (true);
734                         strip->route()->set_order_key (N_("signal"), order);
735
736                         if (!strip_redisplay_does_not_reset_order_keys) {
737                                 strip->route()->set_order_key (get_order_key(), order);
738                         } 
739
740                         if (strip->packed()) {
741
742                                 if (strip->route()->is_master() || strip->route()->is_control()) {
743                                         out_packer.reorder_child (*strip, -1);
744                                 } else {
745                                         strip_packer.reorder_child (*strip, -1); /* put at end */
746                                 }
747
748                         } else {
749
750                                 if (strip->route()->is_master() || strip->route()->is_control()) {
751                                         out_packer.pack_start (*strip, false, false);
752                                 } else {
753                                         strip_packer.pack_start (*strip, false, false);
754                                 }
755                                 strip->set_packed (true);
756                                 //strip->show();
757                         }
758
759                 } else {
760
761                         if (strip->route()->is_master() || strip->route()->is_control()) {
762                                 /* do nothing, these cannot be hidden */
763                         } else {
764                                 if (strip->packed()) {
765                                         strip_packer.remove (*strip);
766                                         strip->set_packed (false);
767                                 }
768                         }
769                 }
770         }
771         
772         if (!strip_redisplay_does_not_reset_order_keys && !strip_redisplay_does_not_sync_order_keys) {
773                 session->sync_order_keys (get_order_key());
774         }
775
776         // Rebind all of the midi controls automatically
777         
778         if (auto_rebinding)
779                 auto_rebind_midi_controls ();
780
781         _group_tabs->set_dirty ();
782 }
783
784 #ifdef GTKOSX
785 void
786 Mixer_UI::queue_draw_all_strips ()
787 {
788         TreeModel::Children rows = track_model->children();
789         TreeModel::Children::iterator i;
790         long order;
791
792         for (order = 0, i = rows.begin(); i != rows.end(); ++i, ++order) {
793                 MixerStrip* strip = (*i)[track_columns.strip];
794
795                 if (strip == 0) {
796                         continue;
797                 }
798
799                 bool visible = (*i)[track_columns.visible];
800                 
801                 if (visible) {
802                         strip->queue_draw();
803                 }
804         }
805 }
806 #endif
807
808 void
809 Mixer_UI::set_auto_rebinding( bool val )
810 {
811         if( val == TRUE )
812         {
813                 auto_rebinding = TRUE;
814                 Session::AutoBindingOff();
815         }
816         else
817         {
818                 auto_rebinding = FALSE;
819                 Session::AutoBindingOn();
820         }
821 }
822
823 void 
824 Mixer_UI::toggle_auto_rebinding() 
825 {
826         if (auto_rebinding)
827         {
828                 set_auto_rebinding( FALSE );
829         }
830         
831         else
832         {
833                 set_auto_rebinding( TRUE );
834         }
835
836         auto_rebind_midi_controls();
837 }
838
839 void 
840 Mixer_UI::auto_rebind_midi_controls () 
841 {
842         TreeModel::Children rows = track_model->children();
843         TreeModel::Children::iterator i;
844         int pos;
845
846         // Create bindings for all visible strips and remove those that are not visible
847         pos = 1;  // 0 is reserved for the master strip
848         for (i = rows.begin(); i != rows.end(); ++i) {
849                 MixerStrip* strip = (*i)[track_columns.strip];
850     
851                 if ( (*i)[track_columns.visible] == true ) {  // add bindings for
852                         // make the actual binding
853                         //cout<<"Auto Binding:  Visible Strip Found: "<<strip->name()<<endl;
854
855                         int controlValue = pos;
856                         if( strip->route()->is_master() ) {
857                                 controlValue = 0;
858                         }
859                         else {
860                                 pos++;
861                         }
862
863                         PBD::Controllable::CreateBinding ( strip->solo_button->get_controllable().get(), controlValue, 0);
864                         PBD::Controllable::CreateBinding ( strip->mute_button->get_controllable().get(), controlValue, 1);
865
866                         if( strip->is_audio_track() ) {
867                                 PBD::Controllable::CreateBinding ( strip->rec_enable_button->get_controllable().get(), controlValue, 2);
868                         }
869
870                         PBD::Controllable::CreateBinding ( strip->gpm.get_controllable().get(), controlValue, 3);
871                         PBD::Controllable::CreateBinding ( strip->panners.get_controllable().get(), controlValue, 4);
872
873                 }
874                 else {  // Remove any existing binding
875                         PBD::Controllable::DeleteBinding ( strip->solo_button->get_controllable().get() );
876                         PBD::Controllable::DeleteBinding ( strip->mute_button->get_controllable().get() );
877
878                         if( strip->is_audio_track() ) {
879                                 PBD::Controllable::DeleteBinding ( strip->rec_enable_button->get_controllable().get() );
880                         }
881
882                         PBD::Controllable::DeleteBinding ( strip->gpm.get_controllable().get() );
883                         PBD::Controllable::DeleteBinding ( strip->panners.get_controllable().get() ); // This only takes the first panner if there are multiples...
884                 }
885
886         } // for
887   
888 }
889
890 struct SignalOrderRouteSorter {
891     bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
892             /* use of ">" forces the correct sort order */
893             return a->order_key (Mixer_UI::get_order_key()) < b->order_key (Mixer_UI::get_order_key());
894     }
895 };
896
897 void
898 Mixer_UI::initial_track_display ()
899 {
900         boost::shared_ptr<RouteList> routes = session->get_routes();
901         RouteList copy (*routes);
902         SignalOrderRouteSorter sorter;
903
904         copy.sort (sorter);
905         
906         no_track_list_redisplay = true;
907
908         track_model->clear ();
909
910         add_strip (copy);
911
912         no_track_list_redisplay = false;
913
914         redisplay_track_list ();
915 }
916
917 void
918 Mixer_UI::show_track_list_menu ()
919 {
920         if (track_menu == 0) {
921                 build_track_menu ();
922         }
923
924         track_menu->popup (1, gtk_get_current_event_time());
925 }
926
927 bool
928 Mixer_UI::track_display_button_press (GdkEventButton* ev)
929 {
930         if (Keyboard::is_context_menu_event (ev)) {
931                 show_track_list_menu ();
932                 return true;
933         }
934
935         TreeIter iter;
936         TreeModel::Path path;
937         TreeViewColumn* column;
938         int cellx;
939         int celly;
940         
941         if (!track_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
942                 return false;
943         }
944
945         switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
946         case 0:
947                 /* allow normal processing to occur */
948                 return false;
949
950         case 1: /* visibility */
951
952                 if ((iter = track_model->get_iter (path))) {
953                         MixerStrip* strip = (*iter)[track_columns.strip];
954                         if (strip) {
955
956                                 if (!strip->route()->is_master() && !strip->route()->is_control()) {
957                                         bool visible = (*iter)[track_columns.visible];
958                                         (*iter)[track_columns.visible] = !visible;
959                                 }
960 #ifdef GTKOSX
961                                 track_display.queue_draw();
962 #endif
963                         }
964                 }
965                 return true;
966
967         default:
968                 break;
969         }
970
971         return false;
972 }
973
974
975 void
976 Mixer_UI::build_track_menu ()
977 {
978         using namespace Menu_Helpers;
979         using namespace Gtk;
980
981         track_menu = new Menu;
982         track_menu->set_name ("ArdourContextMenu");
983         MenuList& items = track_menu->items();
984         
985         items.push_back (MenuElem (_("Show All"), mem_fun(*this, &Mixer_UI::show_all_routes)));
986         items.push_back (MenuElem (_("Hide All"), mem_fun(*this, &Mixer_UI::hide_all_routes)));
987         items.push_back (MenuElem (_("Show All Audio Tracks"), mem_fun(*this, &Mixer_UI::show_all_audiotracks)));
988         items.push_back (MenuElem (_("Hide All Audio Tracks"), mem_fun(*this, &Mixer_UI::hide_all_audiotracks)));
989         items.push_back (MenuElem (_("Show All Audio Busses"), mem_fun(*this, &Mixer_UI::show_all_audiobus)));
990         items.push_back (MenuElem (_("Hide All Audio Busses"), mem_fun(*this, &Mixer_UI::hide_all_audiobus)));
991
992 }
993
994 void
995 Mixer_UI::strip_name_changed (MixerStrip* mx)
996 {
997         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::strip_name_changed), mx));
998         
999         TreeModel::Children rows = track_model->children();
1000         TreeModel::Children::iterator i;
1001         
1002         for (i = rows.begin(); i != rows.end(); ++i) {
1003                 if ((*i)[track_columns.strip] == mx) {
1004                         (*i)[track_columns.text] = mx->route()->name();
1005                         return;
1006                 }
1007         } 
1008
1009         error << _("track display list item for renamed strip not found!") << endmsg;
1010 }
1011
1012
1013 void
1014 Mixer_UI::build_mix_group_context_menu ()
1015 {
1016         using namespace Gtk::Menu_Helpers;
1017
1018         mix_group_context_menu = new Menu;
1019         mix_group_context_menu->set_name ("ArdourContextMenu");
1020         MenuList& items = mix_group_context_menu->items();
1021
1022         items.push_back (MenuElem (_("Activate All"), mem_fun(*this, &Mixer_UI::activate_all_mix_groups)));
1023         items.push_back (MenuElem (_("Disable All"), mem_fun(*this, &Mixer_UI::disable_all_mix_groups)));
1024         items.push_back (SeparatorElem());
1025         items.push_back (MenuElem (_("Add group"), mem_fun(*this, &Mixer_UI::new_mix_group)));
1026         
1027 }
1028
1029 bool
1030 Mixer_UI::group_display_button_press (GdkEventButton* ev)
1031 {
1032         if (Keyboard::is_context_menu_event (ev)) {
1033                 if (mix_group_context_menu == 0) {
1034                         build_mix_group_context_menu ();
1035                 }
1036                 mix_group_context_menu->popup (1, ev->time);
1037                 return true;
1038         }
1039
1040
1041         RouteGroup* group;
1042         TreeIter iter;
1043         TreeModel::Path path;
1044         TreeViewColumn* column;
1045         int cellx;
1046         int celly;
1047
1048         if (!group_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
1049                 return false;
1050         }
1051
1052         switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
1053         case 0:
1054                 if (Keyboard::is_edit_event (ev)) {
1055                         if ((iter = group_model->get_iter (path))) {
1056                                 if ((group = (*iter)[group_columns.group]) != 0) {
1057                                         // edit_mix_group (group);
1058 #ifdef GTKOSX
1059                                         group_display.queue_draw();
1060 #endif
1061                                         return true;
1062                                 }
1063                         }
1064                         
1065                 } 
1066                 break;
1067
1068         case 1:
1069                 if ((iter = group_model->get_iter (path))) {
1070                         bool active = (*iter)[group_columns.active];
1071                         (*iter)[group_columns.active] = !active;
1072 #ifdef GTKOSX
1073                         group_display.queue_draw();
1074 #endif
1075                         return true;
1076                 }
1077                 break;
1078                 
1079         case 2:
1080                 if ((iter = group_model->get_iter (path))) {
1081                         bool visible = (*iter)[group_columns.visible];
1082                         (*iter)[group_columns.visible] = !visible;
1083 #ifdef GTKOSX
1084                         group_display.queue_draw();
1085 #endif
1086                         return true;
1087                 }
1088                 break;
1089
1090         default:
1091                 break;
1092         }
1093         
1094         return false;
1095  }
1096
1097 void
1098 Mixer_UI::activate_all_mix_groups ()
1099 {
1100         Gtk::TreeModel::Children children = group_model->children();
1101         for(Gtk::TreeModel::Children::iterator iter = children.begin(); iter != children.end(); ++iter) {
1102                 (*iter)[group_columns.active] = true;
1103         }
1104 }
1105
1106 void
1107 Mixer_UI::disable_all_mix_groups ()
1108 {
1109         Gtk::TreeModel::Children children = group_model->children();
1110         for(Gtk::TreeModel::Children::iterator iter = children.begin(); iter != children.end(); ++iter) {
1111                 (*iter)[group_columns.active] = false;
1112         }
1113 }
1114
1115 void
1116 Mixer_UI::mix_groups_changed ()
1117 {
1118         ENSURE_GUI_THREAD (mem_fun (*this, &Mixer_UI::mix_groups_changed));
1119
1120         /* just rebuild the while thing */
1121
1122         group_model->clear ();
1123
1124         {
1125                 TreeModel::Row row;
1126                 row = *(group_model->append());
1127                 row[group_columns.active] = false;
1128                 row[group_columns.visible] = true;
1129                 row[group_columns.text] = (_("-all-"));
1130                 row[group_columns.group] = 0;
1131         }
1132
1133         session->foreach_mix_group (mem_fun (*this, &Mixer_UI::add_mix_group));
1134 }
1135
1136 void
1137 Mixer_UI::new_mix_group ()
1138 {
1139         session->add_mix_group (new RouteGroup (*session, ""));
1140 }
1141
1142 void
1143 Mixer_UI::remove_selected_mix_group ()
1144 {
1145         Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
1146         TreeView::Selection::ListHandle_Path rows = selection->get_selected_rows ();
1147
1148         if (rows.empty()) {
1149                 return;
1150         }
1151
1152         TreeView::Selection::ListHandle_Path::iterator i = rows.begin();
1153         TreeIter iter;
1154         
1155         /* selection mode is single, so rows.begin() is it */
1156
1157         if ((iter = group_model->get_iter (*i))) {
1158
1159                 RouteGroup* rg = (*iter)[group_columns.group];
1160
1161                 if (rg) {
1162                         session->remove_mix_group (*rg);
1163                 }
1164         }
1165 }
1166
1167 void
1168 Mixer_UI::group_flags_changed (void* src, RouteGroup* group)
1169 {
1170         if (in_group_row_change) {
1171                 return;
1172         }
1173
1174         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::group_flags_changed), src, group));
1175
1176         /* force an update of any mixer strips that are using this group,
1177            otherwise mix group names don't change in mixer strips 
1178         */
1179
1180         for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1181                 if ((*i)->mix_group() == group) {
1182                         (*i)->mix_group_changed(0);
1183                 }
1184         }
1185         
1186         TreeModel::iterator i;
1187         TreeModel::Children rows = group_model->children();
1188         Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
1189
1190         in_group_row_change = true;
1191         
1192         for (i = rows.begin(); i != rows.end(); ++i) {
1193                 if ((*i)[group_columns.group] == group) {
1194                         (*i)[group_columns.visible] = !group->is_hidden ();
1195                         (*i)[group_columns.active] = group->is_active ();
1196                         (*i)[group_columns.text] = group->name ();
1197                         break;
1198                 }
1199         }
1200
1201         in_group_row_change = false;
1202
1203         _group_tabs->set_dirty ();
1204 }
1205
1206 void
1207 Mixer_UI::mix_group_name_edit (const Glib::ustring& path, const Glib::ustring& new_text)
1208 {
1209         RouteGroup* group;
1210         TreeIter iter;
1211
1212         if ((iter = group_model->get_iter (path))) {
1213         
1214                 if ((group = (*iter)[group_columns.group]) == 0) {
1215                         return;
1216                 }
1217                 
1218                 if (new_text != group->name()) {
1219                         group->set_name (new_text);
1220                 }
1221         }
1222 }
1223
1224 void 
1225 Mixer_UI::mix_group_row_change (const Gtk::TreeModel::Path& path,const Gtk::TreeModel::iterator& iter)
1226 {
1227         RouteGroup* group;
1228
1229         if (in_group_row_change) {
1230                 return;
1231         }
1232
1233         if ((group = (*iter)[group_columns.group]) == 0) {
1234                 return;
1235         }
1236
1237         if ((*iter)[group_columns.visible]) {
1238                 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1239                         if ((*i)->mix_group() == group) {
1240                                 show_strip (*i);
1241                         }
1242                 }
1243         } else {
1244                 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1245                         if ((*i)->mix_group() == group) {
1246                                 hide_strip (*i);
1247                         }
1248                 }
1249         } 
1250
1251         bool active = (*iter)[group_columns.active];
1252         group->set_active (active, this);
1253
1254         Glib::ustring name = (*iter)[group_columns.text];
1255
1256         if (name != group->name()) {
1257                 group->set_name (name);
1258         }
1259
1260 }
1261
1262 void
1263 Mixer_UI::add_mix_group (RouteGroup* group)
1264
1265 {
1266         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::add_mix_group), group));
1267         bool focus = false;
1268
1269         in_group_row_change = true;
1270
1271         TreeModel::Row row = *(group_model->append());
1272         row[group_columns.active] = group->is_active();
1273         row[group_columns.visible] = true;
1274         row[group_columns.group] = group;
1275         if (!group->name().empty()) {
1276                 row[group_columns.text] = group->name();
1277         } else {
1278                 row[group_columns.text] = _("unnamed");
1279                 focus = true;
1280         }
1281
1282         group->FlagsChanged.connect (bind (mem_fun(*this, &Mixer_UI::group_flags_changed), group));
1283         
1284         if (focus) {
1285                 TreeViewColumn* col = group_display.get_column (0);
1286                 CellRendererText* name_cell = dynamic_cast<CellRendererText*>(group_display.get_column_cell_renderer (0));
1287                 group_display.set_cursor (group_model->get_path (row), *col, *name_cell, true);
1288         }
1289
1290         in_group_row_change = false;
1291 }
1292
1293 bool
1294 Mixer_UI::strip_scroller_button_release (GdkEventButton* ev)
1295 {
1296         using namespace Menu_Helpers;
1297
1298         if (Keyboard::is_context_menu_event (ev)) {
1299                 ARDOUR_UI::instance()->add_route (this);
1300                 return true;
1301         }
1302
1303         return false;
1304 }
1305
1306 void
1307 Mixer_UI::set_strip_width (Width w)
1308 {
1309         _strip_width = w;
1310
1311         for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
1312                 (*i)->set_width_enum (w, this);
1313         }
1314 }
1315
1316 void
1317 Mixer_UI::set_window_pos_and_size ()
1318 {
1319         resize (m_width, m_height);
1320         move (m_root_x, m_root_y);
1321 }
1322
1323         void
1324 Mixer_UI::get_window_pos_and_size ()
1325 {
1326         get_position(m_root_x, m_root_y);
1327         get_size(m_width, m_height);
1328 }
1329
1330 int
1331 Mixer_UI::set_state (const XMLNode& node)
1332 {
1333         const XMLProperty* prop;
1334         XMLNode* geometry;
1335         
1336         m_width = default_width;
1337         m_height = default_height;
1338         m_root_x = 1;
1339         m_root_y = 1;
1340         
1341         if ((geometry = find_named_node (node, "geometry")) != 0) {
1342
1343                 XMLProperty* prop;
1344
1345                 if ((prop = geometry->property("x_size")) == 0) {
1346                         prop = geometry->property ("x-size");
1347                 }
1348                 if (prop) {
1349                         m_width = atoi(prop->value());
1350                 }
1351                 if ((prop = geometry->property("y_size")) == 0) {
1352                         prop = geometry->property ("y-size");
1353                 }
1354                 if (prop) {
1355                         m_height = atoi(prop->value());
1356                 }
1357
1358                 if ((prop = geometry->property ("x_pos")) == 0) {
1359                         prop = geometry->property ("x-pos");
1360                 }
1361                 if (prop) {
1362                         m_root_x = atoi (prop->value());
1363                         
1364                 }
1365                 if ((prop = geometry->property ("y_pos")) == 0) {
1366                         prop = geometry->property ("y-pos");
1367                 }
1368                 if (prop) {
1369                         m_root_y = atoi (prop->value());
1370                 }
1371         }
1372
1373         set_window_pos_and_size ();
1374
1375         if ((prop = node.property ("narrow-strips"))) {
1376                 if (prop->value() == "yes") {
1377                         set_strip_width (Narrow);
1378                 } else {
1379                         set_strip_width (Wide);
1380                 }
1381         }
1382
1383         if ((prop = node.property ("show-mixer"))) {
1384                 if (prop->value() == "yes") {
1385                        _visible = true;
1386                 }
1387         }
1388
1389         return 0;
1390 }
1391
1392 XMLNode&
1393 Mixer_UI::get_state (void)
1394 {
1395         XMLNode* node = new XMLNode ("Mixer");
1396
1397         if (is_realized()) {
1398                 Glib::RefPtr<Gdk::Window> win = get_window();
1399         
1400                 get_window_pos_and_size ();
1401
1402                 XMLNode* geometry = new XMLNode ("geometry");
1403                 char buf[32];
1404                 snprintf(buf, sizeof(buf), "%d", m_width);
1405                 geometry->add_property(X_("x_size"), string(buf));
1406                 snprintf(buf, sizeof(buf), "%d", m_height);
1407                 geometry->add_property(X_("y_size"), string(buf));
1408                 snprintf(buf, sizeof(buf), "%d", m_root_x);
1409                 geometry->add_property(X_("x_pos"), string(buf));
1410                 snprintf(buf, sizeof(buf), "%d", m_root_y);
1411                 geometry->add_property(X_("y_pos"), string(buf));
1412                 
1413                 // written only for compatibility, they are not used.
1414                 snprintf(buf, sizeof(buf), "%d", 0);
1415                 geometry->add_property(X_("x_off"), string(buf));
1416                 snprintf(buf, sizeof(buf), "%d", 0);
1417                 geometry->add_property(X_("y_off"), string(buf));
1418
1419                 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&rhs_pane1)->gobj()));
1420                 geometry->add_property(X_("mixer_rhs_pane1_pos"), string(buf));
1421                 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&list_hpane)->gobj()));
1422                 geometry->add_property(X_("mixer_list_hpane_pos"), string(buf));
1423
1424                 node->add_child_nocopy (*geometry);
1425         }
1426
1427         node->add_property ("narrow-strips", _strip_width == Narrow ? "yes" : "no");
1428
1429         node->add_property ("show-mixer", _visible ? "yes" : "no");
1430
1431         return *node;
1432 }
1433
1434
1435 void 
1436 Mixer_UI::pane_allocation_handler (Allocation& alloc, Gtk::Paned* which)
1437 {
1438         int pos;
1439         XMLProperty* prop = 0;
1440         char buf[32];
1441         XMLNode* node = ARDOUR_UI::instance()->mixer_settings();
1442         XMLNode* geometry;
1443         int width, height;
1444         static int32_t done[3] = { 0, 0, 0 };
1445
1446         width = default_width;
1447         height = default_height;
1448
1449         if ((geometry = find_named_node (*node, "geometry")) != 0) {
1450
1451
1452                 if ((prop = geometry->property ("x_size")) == 0) {
1453                         prop = geometry->property ("x-size");
1454                 }
1455                 if (prop) {
1456                         width = atoi (prop->value());
1457                 }
1458                 if ((prop = geometry->property ("y_size")) == 0) {
1459                         prop = geometry->property ("y-size");
1460                 }
1461                 if (prop) {
1462                         height = atoi (prop->value());
1463                 }
1464         }
1465
1466         if (which == static_cast<Gtk::Paned*> (&rhs_pane1)) {
1467
1468                 if (done[0]) {
1469                         return;
1470                 }
1471
1472                 if (!geometry || (prop = geometry->property("mixer-rhs-pane1-pos")) == 0) {
1473                         pos = height / 3;
1474                         snprintf (buf, sizeof(buf), "%d", pos);
1475                 } else {
1476                         pos = atoi (prop->value());
1477                 }
1478
1479                 if ((done[0] = GTK_WIDGET(rhs_pane1.gobj())->allocation.height > pos)) {
1480                         rhs_pane1.set_position (pos);
1481                 }
1482
1483         } else if (which == static_cast<Gtk::Paned*> (&list_hpane)) {
1484
1485                 if (done[2]) {
1486                         return;
1487                 }
1488
1489                 if (!geometry || (prop = geometry->property("mixer-list-hpane-pos")) == 0) {
1490                         pos = 75;
1491                         snprintf (buf, sizeof(buf), "%d", pos);
1492                 } else {
1493                         pos = atoi (prop->value());
1494                 }
1495
1496                 if ((done[2] = GTK_WIDGET(list_hpane.gobj())->allocation.width > pos)) {
1497                         list_hpane.set_position (pos);
1498                 }
1499         }
1500 }
1501
1502 bool
1503 Mixer_UI::on_key_press_event (GdkEventKey* ev)
1504 {
1505         return key_press_focus_accelerator_handler (*this, ev);
1506 }
1507
1508 void
1509 Mixer_UI::parameter_changed (string const & p)
1510 {
1511         if (p == "show-group-tabs") {
1512                 bool const s = session->config.get_show_group_tabs ();
1513                 if (s) {
1514                         _group_tabs->show ();
1515                 } else {
1516                         _group_tabs->hide ();
1517                 }
1518         }
1519 }
1520