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