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