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