6f9750a32b71d14fc2e78d6a17d0aa53965fa33e
[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                         strip->set_marked_for_display (false);
762
763                         if (strip->route()->is_master() || strip->route()->is_control()) {
764                                 /* do nothing, these cannot be hidden */
765                         } else {
766                                 if (strip->packed()) {
767                                         strip_packer.remove (*strip);
768                                         strip->set_packed (false);
769                                 }
770                         }
771                 }
772         }
773         
774         if (!strip_redisplay_does_not_reset_order_keys && !strip_redisplay_does_not_sync_order_keys) {
775                 session->sync_order_keys (get_order_key());
776         }
777
778         // Rebind all of the midi controls automatically
779         
780         if (auto_rebinding)
781                 auto_rebind_midi_controls ();
782
783         _group_tabs->set_dirty ();
784 }
785
786 #ifdef GTKOSX
787 void
788 Mixer_UI::queue_draw_all_strips ()
789 {
790         TreeModel::Children rows = track_model->children();
791         TreeModel::Children::iterator i;
792         long order;
793
794         for (order = 0, i = rows.begin(); i != rows.end(); ++i, ++order) {
795                 MixerStrip* strip = (*i)[track_columns.strip];
796
797                 if (strip == 0) {
798                         continue;
799                 }
800
801                 bool visible = (*i)[track_columns.visible];
802                 
803                 if (visible) {
804                         strip->queue_draw();
805                 }
806         }
807 }
808 #endif
809
810 void
811 Mixer_UI::set_auto_rebinding( bool val )
812 {
813         if( val == TRUE )
814         {
815                 auto_rebinding = TRUE;
816                 Session::AutoBindingOff();
817         }
818         else
819         {
820                 auto_rebinding = FALSE;
821                 Session::AutoBindingOn();
822         }
823 }
824
825 void 
826 Mixer_UI::toggle_auto_rebinding() 
827 {
828         if (auto_rebinding)
829         {
830                 set_auto_rebinding( FALSE );
831         }
832         
833         else
834         {
835                 set_auto_rebinding( TRUE );
836         }
837
838         auto_rebind_midi_controls();
839 }
840
841 void 
842 Mixer_UI::auto_rebind_midi_controls () 
843 {
844         TreeModel::Children rows = track_model->children();
845         TreeModel::Children::iterator i;
846         int pos;
847
848         // Create bindings for all visible strips and remove those that are not visible
849         pos = 1;  // 0 is reserved for the master strip
850         for (i = rows.begin(); i != rows.end(); ++i) {
851                 MixerStrip* strip = (*i)[track_columns.strip];
852     
853                 if ( (*i)[track_columns.visible] == true ) {  // add bindings for
854                         // make the actual binding
855                         //cout<<"Auto Binding:  Visible Strip Found: "<<strip->name()<<endl;
856
857                         int controlValue = pos;
858                         if( strip->route()->is_master() ) {
859                                 controlValue = 0;
860                         }
861                         else {
862                                 pos++;
863                         }
864
865                         PBD::Controllable::CreateBinding ( strip->solo_button->get_controllable().get(), controlValue, 0);
866                         PBD::Controllable::CreateBinding ( strip->mute_button->get_controllable().get(), controlValue, 1);
867
868                         if( strip->is_audio_track() ) {
869                                 PBD::Controllable::CreateBinding ( strip->rec_enable_button->get_controllable().get(), controlValue, 2);
870                         }
871
872                         PBD::Controllable::CreateBinding ( strip->gpm.get_controllable().get(), controlValue, 3);
873                         PBD::Controllable::CreateBinding ( strip->panners.get_controllable().get(), controlValue, 4);
874
875                 }
876                 else {  // Remove any existing binding
877                         PBD::Controllable::DeleteBinding ( strip->solo_button->get_controllable().get() );
878                         PBD::Controllable::DeleteBinding ( strip->mute_button->get_controllable().get() );
879
880                         if( strip->is_audio_track() ) {
881                                 PBD::Controllable::DeleteBinding ( strip->rec_enable_button->get_controllable().get() );
882                         }
883
884                         PBD::Controllable::DeleteBinding ( strip->gpm.get_controllable().get() );
885                         PBD::Controllable::DeleteBinding ( strip->panners.get_controllable().get() ); // This only takes the first panner if there are multiples...
886                 }
887
888         } // for
889   
890 }
891
892 struct SignalOrderRouteSorter {
893     bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
894             /* use of ">" forces the correct sort order */
895             return a->order_key (Mixer_UI::get_order_key()) < b->order_key (Mixer_UI::get_order_key());
896     }
897 };
898
899 void
900 Mixer_UI::initial_track_display ()
901 {
902         boost::shared_ptr<RouteList> routes = session->get_routes();
903         RouteList copy (*routes);
904         SignalOrderRouteSorter sorter;
905
906         copy.sort (sorter);
907         
908         no_track_list_redisplay = true;
909
910         track_model->clear ();
911
912         add_strip (copy);
913
914         no_track_list_redisplay = false;
915
916         redisplay_track_list ();
917 }
918
919 void
920 Mixer_UI::show_track_list_menu ()
921 {
922         if (track_menu == 0) {
923                 build_track_menu ();
924         }
925
926         track_menu->popup (1, gtk_get_current_event_time());
927 }
928
929 bool
930 Mixer_UI::track_display_button_press (GdkEventButton* ev)
931 {
932         if (Keyboard::is_context_menu_event (ev)) {
933                 show_track_list_menu ();
934                 return true;
935         }
936
937         TreeIter iter;
938         TreeModel::Path path;
939         TreeViewColumn* column;
940         int cellx;
941         int celly;
942         
943         if (!track_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
944                 return false;
945         }
946
947         switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
948         case 0:
949                 /* allow normal processing to occur */
950                 return false;
951
952         case 1: /* visibility */
953
954                 if ((iter = track_model->get_iter (path))) {
955                         MixerStrip* strip = (*iter)[track_columns.strip];
956                         if (strip) {
957
958                                 if (!strip->route()->is_master() && !strip->route()->is_control()) {
959                                         bool visible = (*iter)[track_columns.visible];
960                                         (*iter)[track_columns.visible] = !visible;
961                                 }
962 #ifdef GTKOSX
963                                 track_display.queue_draw();
964 #endif
965                         }
966                 }
967                 return true;
968
969         default:
970                 break;
971         }
972
973         return false;
974 }
975
976
977 void
978 Mixer_UI::build_track_menu ()
979 {
980         using namespace Menu_Helpers;
981         using namespace Gtk;
982
983         track_menu = new Menu;
984         track_menu->set_name ("ArdourContextMenu");
985         MenuList& items = track_menu->items();
986         
987         items.push_back (MenuElem (_("Show All"), mem_fun(*this, &Mixer_UI::show_all_routes)));
988         items.push_back (MenuElem (_("Hide All"), mem_fun(*this, &Mixer_UI::hide_all_routes)));
989         items.push_back (MenuElem (_("Show All Audio Tracks"), mem_fun(*this, &Mixer_UI::show_all_audiotracks)));
990         items.push_back (MenuElem (_("Hide All Audio Tracks"), mem_fun(*this, &Mixer_UI::hide_all_audiotracks)));
991         items.push_back (MenuElem (_("Show All Audio Busses"), mem_fun(*this, &Mixer_UI::show_all_audiobus)));
992         items.push_back (MenuElem (_("Hide All Audio Busses"), mem_fun(*this, &Mixer_UI::hide_all_audiobus)));
993
994 }
995
996 void
997 Mixer_UI::strip_name_changed (MixerStrip* mx)
998 {
999         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::strip_name_changed), mx));
1000         
1001         TreeModel::Children rows = track_model->children();
1002         TreeModel::Children::iterator i;
1003         
1004         for (i = rows.begin(); i != rows.end(); ++i) {
1005                 if ((*i)[track_columns.strip] == mx) {
1006                         (*i)[track_columns.text] = mx->route()->name();
1007                         return;
1008                 }
1009         } 
1010
1011         error << _("track display list item for renamed strip not found!") << endmsg;
1012 }
1013
1014
1015 void
1016 Mixer_UI::build_mix_group_context_menu ()
1017 {
1018         using namespace Gtk::Menu_Helpers;
1019
1020         mix_group_context_menu = new Menu;
1021         mix_group_context_menu->set_name ("ArdourContextMenu");
1022         MenuList& items = mix_group_context_menu->items();
1023
1024         items.push_back (MenuElem (_("Activate All"), mem_fun(*this, &Mixer_UI::activate_all_mix_groups)));
1025         items.push_back (MenuElem (_("Disable All"), mem_fun(*this, &Mixer_UI::disable_all_mix_groups)));
1026         items.push_back (SeparatorElem());
1027         items.push_back (MenuElem (_("Add group"), mem_fun(*this, &Mixer_UI::new_mix_group)));
1028         
1029 }
1030
1031 bool
1032 Mixer_UI::group_display_button_press (GdkEventButton* ev)
1033 {
1034         if (Keyboard::is_context_menu_event (ev)) {
1035                 if (mix_group_context_menu == 0) {
1036                         build_mix_group_context_menu ();
1037                 }
1038                 mix_group_context_menu->popup (1, ev->time);
1039                 return true;
1040         }
1041
1042
1043         RouteGroup* group;
1044         TreeIter iter;
1045         TreeModel::Path path;
1046         TreeViewColumn* column;
1047         int cellx;
1048         int celly;
1049
1050         if (!group_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
1051                 return false;
1052         }
1053
1054         switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
1055         case 0:
1056                 if (Keyboard::is_edit_event (ev)) {
1057                         if ((iter = group_model->get_iter (path))) {
1058                                 if ((group = (*iter)[group_columns.group]) != 0) {
1059                                         // edit_mix_group (group);
1060 #ifdef GTKOSX
1061                                         group_display.queue_draw();
1062 #endif
1063                                         return true;
1064                                 }
1065                         }
1066                         
1067                 } 
1068                 break;
1069
1070         case 1:
1071                 if ((iter = group_model->get_iter (path))) {
1072                         bool active = (*iter)[group_columns.active];
1073                         (*iter)[group_columns.active] = !active;
1074 #ifdef GTKOSX
1075                         group_display.queue_draw();
1076 #endif
1077                         return true;
1078                 }
1079                 break;
1080                 
1081         case 2:
1082                 if ((iter = group_model->get_iter (path))) {
1083                         bool visible = (*iter)[group_columns.visible];
1084                         (*iter)[group_columns.visible] = !visible;
1085 #ifdef GTKOSX
1086                         group_display.queue_draw();
1087 #endif
1088                         return true;
1089                 }
1090                 break;
1091
1092         default:
1093                 break;
1094         }
1095         
1096         return false;
1097  }
1098
1099 void
1100 Mixer_UI::activate_all_mix_groups ()
1101 {
1102         Gtk::TreeModel::Children children = group_model->children();
1103         for(Gtk::TreeModel::Children::iterator iter = children.begin(); iter != children.end(); ++iter) {
1104                 (*iter)[group_columns.active] = true;
1105         }
1106 }
1107
1108 void
1109 Mixer_UI::disable_all_mix_groups ()
1110 {
1111         Gtk::TreeModel::Children children = group_model->children();
1112         for(Gtk::TreeModel::Children::iterator iter = children.begin(); iter != children.end(); ++iter) {
1113                 (*iter)[group_columns.active] = false;
1114         }
1115 }
1116
1117 void
1118 Mixer_UI::mix_groups_changed ()
1119 {
1120         ENSURE_GUI_THREAD (mem_fun (*this, &Mixer_UI::mix_groups_changed));
1121
1122         /* just rebuild the while thing */
1123
1124         group_model->clear ();
1125
1126         {
1127                 TreeModel::Row row;
1128                 row = *(group_model->append());
1129                 row[group_columns.active] = false;
1130                 row[group_columns.visible] = true;
1131                 row[group_columns.text] = (_("-all-"));
1132                 row[group_columns.group] = 0;
1133         }
1134
1135         session->foreach_mix_group (mem_fun (*this, &Mixer_UI::add_mix_group));
1136 }
1137
1138 void
1139 Mixer_UI::new_mix_group ()
1140 {
1141         session->add_mix_group (new RouteGroup (*session, ""));
1142 }
1143
1144 void
1145 Mixer_UI::remove_selected_mix_group ()
1146 {
1147         Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
1148         TreeView::Selection::ListHandle_Path rows = selection->get_selected_rows ();
1149
1150         if (rows.empty()) {
1151                 return;
1152         }
1153
1154         TreeView::Selection::ListHandle_Path::iterator i = rows.begin();
1155         TreeIter iter;
1156         
1157         /* selection mode is single, so rows.begin() is it */
1158
1159         if ((iter = group_model->get_iter (*i))) {
1160
1161                 RouteGroup* rg = (*iter)[group_columns.group];
1162
1163                 if (rg) {
1164                         session->remove_mix_group (*rg);
1165                 }
1166         }
1167 }
1168
1169 void
1170 Mixer_UI::group_flags_changed (void* src, RouteGroup* group)
1171 {
1172         if (in_group_row_change) {
1173                 return;
1174         }
1175
1176         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::group_flags_changed), src, group));
1177
1178         /* force an update of any mixer strips that are using this group,
1179            otherwise mix group names don't change in mixer strips 
1180         */
1181
1182         for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1183                 if ((*i)->mix_group() == group) {
1184                         (*i)->mix_group_changed(0);
1185                 }
1186         }
1187         
1188         TreeModel::iterator i;
1189         TreeModel::Children rows = group_model->children();
1190         Glib::RefPtr<TreeSelection> selection = group_display.get_selection();
1191
1192         in_group_row_change = true;
1193         
1194         for (i = rows.begin(); i != rows.end(); ++i) {
1195                 if ((*i)[group_columns.group] == group) {
1196                         (*i)[group_columns.visible] = !group->is_hidden ();
1197                         (*i)[group_columns.active] = group->is_active ();
1198                         (*i)[group_columns.text] = group->name ();
1199                         break;
1200                 }
1201         }
1202
1203         in_group_row_change = false;
1204
1205         _group_tabs->set_dirty ();
1206 }
1207
1208 void
1209 Mixer_UI::mix_group_name_edit (const Glib::ustring& path, const Glib::ustring& new_text)
1210 {
1211         RouteGroup* group;
1212         TreeIter iter;
1213
1214         if ((iter = group_model->get_iter (path))) {
1215         
1216                 if ((group = (*iter)[group_columns.group]) == 0) {
1217                         return;
1218                 }
1219                 
1220                 if (new_text != group->name()) {
1221                         group->set_name (new_text);
1222                 }
1223         }
1224 }
1225
1226 void 
1227 Mixer_UI::mix_group_row_change (const Gtk::TreeModel::Path& path,const Gtk::TreeModel::iterator& iter)
1228 {
1229         RouteGroup* group;
1230
1231         if (in_group_row_change) {
1232                 return;
1233         }
1234
1235         if ((group = (*iter)[group_columns.group]) == 0) {
1236                 return;
1237         }
1238
1239         if ((*iter)[group_columns.visible]) {
1240                 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1241                         if ((*i)->mix_group() == group) {
1242                                 show_strip (*i);
1243                         }
1244                 }
1245         } else {
1246                 for (list<MixerStrip *>::iterator i = strips.begin(); i != strips.end(); ++i) {
1247                         if ((*i)->mix_group() == group) {
1248                                 hide_strip (*i);
1249                         }
1250                 }
1251         } 
1252
1253         bool active = (*iter)[group_columns.active];
1254         group->set_active (active, this);
1255
1256         Glib::ustring name = (*iter)[group_columns.text];
1257
1258         if (name != group->name()) {
1259                 group->set_name (name);
1260         }
1261
1262 }
1263
1264 void
1265 Mixer_UI::add_mix_group (RouteGroup* group)
1266 {
1267         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Mixer_UI::add_mix_group), group));
1268         bool focus = false;
1269
1270         in_group_row_change = true;
1271
1272         TreeModel::Row row = *(group_model->append());
1273         row[group_columns.active] = group->is_active();
1274         row[group_columns.visible] = true;
1275         row[group_columns.group] = group;
1276         if (!group->name().empty()) {
1277                 row[group_columns.text] = group->name();
1278         } else {
1279                 row[group_columns.text] = _("unnamed");
1280                 focus = true;
1281         }
1282
1283         group->FlagsChanged.connect (bind (mem_fun(*this, &Mixer_UI::group_flags_changed), group));
1284         
1285         if (focus) {
1286                 TreeViewColumn* col = group_display.get_column (0);
1287                 CellRendererText* name_cell = dynamic_cast<CellRendererText*>(group_display.get_column_cell_renderer (0));
1288                 group_display.set_cursor (group_model->get_path (row), *col, *name_cell, true);
1289         }
1290
1291         in_group_row_change = false;
1292 }
1293
1294 bool
1295 Mixer_UI::strip_scroller_button_release (GdkEventButton* ev)
1296 {
1297         using namespace Menu_Helpers;
1298
1299         if (Keyboard::is_context_menu_event (ev)) {
1300                 ARDOUR_UI::instance()->add_route (this);
1301                 return true;
1302         }
1303
1304         return false;
1305 }
1306
1307 void
1308 Mixer_UI::set_strip_width (Width w)
1309 {
1310         _strip_width = w;
1311
1312         for (list<MixerStrip*>::iterator i = strips.begin(); i != strips.end(); ++i) {
1313                 (*i)->set_width_enum (w, this);
1314         }
1315 }
1316
1317 void
1318 Mixer_UI::set_window_pos_and_size ()
1319 {
1320         resize (m_width, m_height);
1321         move (m_root_x, m_root_y);
1322 }
1323
1324         void
1325 Mixer_UI::get_window_pos_and_size ()
1326 {
1327         get_position(m_root_x, m_root_y);
1328         get_size(m_width, m_height);
1329 }
1330
1331 int
1332 Mixer_UI::set_state (const XMLNode& node)
1333 {
1334         const XMLProperty* prop;
1335         XMLNode* geometry;
1336         
1337         m_width = default_width;
1338         m_height = default_height;
1339         m_root_x = 1;
1340         m_root_y = 1;
1341         
1342         if ((geometry = find_named_node (node, "geometry")) != 0) {
1343
1344                 XMLProperty* prop;
1345
1346                 if ((prop = geometry->property("x_size")) == 0) {
1347                         prop = geometry->property ("x-size");
1348                 }
1349                 if (prop) {
1350                         m_width = atoi(prop->value());
1351                 }
1352                 if ((prop = geometry->property("y_size")) == 0) {
1353                         prop = geometry->property ("y-size");
1354                 }
1355                 if (prop) {
1356                         m_height = atoi(prop->value());
1357                 }
1358
1359                 if ((prop = geometry->property ("x_pos")) == 0) {
1360                         prop = geometry->property ("x-pos");
1361                 }
1362                 if (prop) {
1363                         m_root_x = atoi (prop->value());
1364                         
1365                 }
1366                 if ((prop = geometry->property ("y_pos")) == 0) {
1367                         prop = geometry->property ("y-pos");
1368                 }
1369                 if (prop) {
1370                         m_root_y = atoi (prop->value());
1371                 }
1372         }
1373
1374         set_window_pos_and_size ();
1375
1376         if ((prop = node.property ("narrow-strips"))) {
1377                 if (prop->value() == "yes") {
1378                         set_strip_width (Narrow);
1379                 } else {
1380                         set_strip_width (Wide);
1381                 }
1382         }
1383
1384         if ((prop = node.property ("show-mixer"))) {
1385                 if (prop->value() == "yes") {
1386                        _visible = true;
1387                 }
1388         }
1389
1390         return 0;
1391 }
1392
1393 XMLNode&
1394 Mixer_UI::get_state (void)
1395 {
1396         XMLNode* node = new XMLNode ("Mixer");
1397
1398         if (is_realized()) {
1399                 Glib::RefPtr<Gdk::Window> win = get_window();
1400         
1401                 get_window_pos_and_size ();
1402
1403                 XMLNode* geometry = new XMLNode ("geometry");
1404                 char buf[32];
1405                 snprintf(buf, sizeof(buf), "%d", m_width);
1406                 geometry->add_property(X_("x_size"), string(buf));
1407                 snprintf(buf, sizeof(buf), "%d", m_height);
1408                 geometry->add_property(X_("y_size"), string(buf));
1409                 snprintf(buf, sizeof(buf), "%d", m_root_x);
1410                 geometry->add_property(X_("x_pos"), string(buf));
1411                 snprintf(buf, sizeof(buf), "%d", m_root_y);
1412                 geometry->add_property(X_("y_pos"), string(buf));
1413                 
1414                 // written only for compatibility, they are not used.
1415                 snprintf(buf, sizeof(buf), "%d", 0);
1416                 geometry->add_property(X_("x_off"), string(buf));
1417                 snprintf(buf, sizeof(buf), "%d", 0);
1418                 geometry->add_property(X_("y_off"), string(buf));
1419
1420                 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&rhs_pane1)->gobj()));
1421                 geometry->add_property(X_("mixer_rhs_pane1_pos"), string(buf));
1422                 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&list_hpane)->gobj()));
1423                 geometry->add_property(X_("mixer_list_hpane_pos"), string(buf));
1424
1425                 node->add_child_nocopy (*geometry);
1426         }
1427
1428         node->add_property ("narrow-strips", _strip_width == Narrow ? "yes" : "no");
1429
1430         node->add_property ("show-mixer", _visible ? "yes" : "no");
1431
1432         return *node;
1433 }
1434
1435
1436 void 
1437 Mixer_UI::pane_allocation_handler (Allocation& alloc, Gtk::Paned* which)
1438 {
1439         int pos;
1440         XMLProperty* prop = 0;
1441         char buf[32];
1442         XMLNode* node = ARDOUR_UI::instance()->mixer_settings();
1443         XMLNode* geometry;
1444         int width, height;
1445         static int32_t done[3] = { 0, 0, 0 };
1446
1447         width = default_width;
1448         height = default_height;
1449
1450         if ((geometry = find_named_node (*node, "geometry")) != 0) {
1451
1452
1453                 if ((prop = geometry->property ("x_size")) == 0) {
1454                         prop = geometry->property ("x-size");
1455                 }
1456                 if (prop) {
1457                         width = atoi (prop->value());
1458                 }
1459                 if ((prop = geometry->property ("y_size")) == 0) {
1460                         prop = geometry->property ("y-size");
1461                 }
1462                 if (prop) {
1463                         height = atoi (prop->value());
1464                 }
1465         }
1466
1467         if (which == static_cast<Gtk::Paned*> (&rhs_pane1)) {
1468
1469                 if (done[0]) {
1470                         return;
1471                 }
1472
1473                 if (!geometry || (prop = geometry->property("mixer-rhs-pane1-pos")) == 0) {
1474                         pos = height / 3;
1475                         snprintf (buf, sizeof(buf), "%d", pos);
1476                 } else {
1477                         pos = atoi (prop->value());
1478                 }
1479
1480                 if ((done[0] = GTK_WIDGET(rhs_pane1.gobj())->allocation.height > pos)) {
1481                         rhs_pane1.set_position (pos);
1482                 }
1483
1484         } else if (which == static_cast<Gtk::Paned*> (&list_hpane)) {
1485
1486                 if (done[2]) {
1487                         return;
1488                 }
1489
1490                 if (!geometry || (prop = geometry->property("mixer-list-hpane-pos")) == 0) {
1491                         pos = 75;
1492                         snprintf (buf, sizeof(buf), "%d", pos);
1493                 } else {
1494                         pos = atoi (prop->value());
1495                 }
1496
1497                 if ((done[2] = GTK_WIDGET(list_hpane.gobj())->allocation.width > pos)) {
1498                         list_hpane.set_position (pos);
1499                 }
1500         }
1501 }
1502
1503 bool
1504 Mixer_UI::on_key_press_event (GdkEventKey* ev)
1505 {
1506         return key_press_focus_accelerator_handler (*this, ev);
1507 }
1508
1509 void
1510 Mixer_UI::parameter_changed (string const & p)
1511 {
1512         if (p == "show-group-tabs") {
1513                 bool const s = session->config.get_show_group_tabs ();
1514                 if (s) {
1515                         _group_tabs->show ();
1516                 } else {
1517                         _group_tabs->hide ();
1518                 }
1519         }
1520 }
1521