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