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